Added simple GUI client.

git-svn-id: https://linkchecker.svn.sourceforge.net/svnroot/linkchecker/trunk/linkchecker@3813 e7d03fd6-7b0d-0410-9947-9c21f3af8025
This commit is contained in:
calvin 2008-06-11 13:33:28 +00:00
parent 9a4df92e8b
commit 2a3e855c48
5 changed files with 623 additions and 3 deletions

View file

@ -1,5 +1,4 @@
- [INTERFACE] make a nice GUI for linkchecker
+ QT GUI
http://www.diotavelli.net/PyQtWiki/Threading,_Signals_and_Slots
- [INTERFACE] GUI for linkchecker
+ add debian .menu file
+ test on Windows
+ more configuration options

29
gui/linkchecker-gui Executable file
View file

@ -0,0 +1,29 @@
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-
# Copyright (C) 2008 Bastian Kleineidam
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
"""
Check HTML pages for broken links. This is the GUI client.
"""
import sys
from PyQt4.QtGui import QApplication
from linkchecker_main import LinkCheckerMain
if __name__ == "__main__":
app = QApplication(sys.argv)
window = LinkCheckerMain()
window.show()
sys.exit(app.exec_())

175
gui/linkchecker_main.py Normal file
View file

@ -0,0 +1,175 @@
# -*- coding: iso-8859-1 -*-
# Copyright (C) 2008 Bastian Kleineidam
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
import os
from PyQt4 import QtCore, QtGui
from linkchecker_ui import Ui_MainWindow
import linkcheck
from linkcheck import configuration, checker, director, add_intern_pattern, \
strformat
class LinkCheckerMain (QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(LinkCheckerMain, self).__init__(parent)
self.setupUi(self)
self.setWindowTitle(configuration.App)
self.textEdit.setFontFamily("mono")
self.checker = Checker()
self.connect(self.checker, QtCore.SIGNAL("finished()"), self.updateUi)
self.connect(self.checker, QtCore.SIGNAL("terminated()"), self.updateUi)
self.connect(self.checker, QtCore.SIGNAL("addMessage(QString)"), self.addMessage)
self.connect(self.checker, QtCore.SIGNAL("setStatus(QString)"), self.setStatus)
self.connect(self.pushButton, QtCore.SIGNAL("clicked()"), self.check_or_cancel)
self.connect(self.actionQuit, QtCore.SIGNAL("triggered()"), QtGui.qApp, QtCore.SLOT("quit()"))
self.connect(self.actionAbout, QtCore.SIGNAL("triggered()"), self.about)
self.updateUi()
def about (self):
d = {
"app": configuration.App,
"appname": configuration.AppName,
"copyright": configuration.HtmlCopyright,
}
QtGui.QMessageBox.about(self, _(u"About %(appname)s") % d,
_(u"""<qt><p>%(appname)s checks HTML documents and websites
for broken links.</p>
<p>%(copyright)s</p>
<p>%(app)s is licensed under the
<a href="http://www.gnu.org/licenses/gpl.html">GPL</a>
Version 2 or later.</p>
</qt>""") % d)
def check_or_cancel (self):
if self.aggregate is None:
self.check()
else:
self.cancel()
def check (self):
self.pushButton.setEnabled(False)
self.textEdit.setText("")
config = self.get_config()
aggregate = director.get_aggregate(config)
url = unicode(self.lineEdit.text()).strip()
if not url:
self.textEdit.setText(_("Error, empty URL"))
self.updateUi()
return
if url.startswith(u"www."):
url = u"http://%s" % url
elif url.startswith(u"ftp."):
url = u"ftp://%s" % url
self.setStatus(_("Checking '%s'.") % strformat.limit(url, 40))
url_data = checker.get_url_from(url, 0, aggregate)
try:
add_intern_pattern(url_data, config)
except UnicodeError:
self.textEdit.setText(_("Error, invalid URL '%s'.") %
strformat.limit(url, 40))
self.updateUi()
return
aggregate.urlqueue.put(url_data)
self.pushButton.setText(_("Cancel"))
self.aggregate = aggregate
self.pushButton.setEnabled(True)
# check in background
self.checker.check(self.aggregate)
def cancel (self):
self.setStatus(_("Aborting."))
director.abort(self.aggregate)
def get_config (self):
config = configuration.Configuration()
config["recursionlevel"] = self.spinBox.value()
config.logger_add("gui", GuiLogger)
config["logger"] = config.logger_new('gui', widget=self.checker)
config["verbose"] = self.checkBox.isChecked()
config.init_logging(StatusLogger(self.checker))
config["status"] = True
return config
def addMessage (self, msg):
text = self.textEdit.toPlainText()
self.textEdit.setText(text+msg)
self.textEdit.moveCursor(QtGui.QTextCursor.End)
def setStatus (self, msg):
self.statusBar.showMessage(msg)
def updateUi (self):
self.pushButton.setText(_("Check"))
self.aggregate = None
self.pushButton.setEnabled(True)
self.setStatus(_("Ready."))
class Checker (QtCore.QThread):
def __init__ (self, parent=None):
super(Checker, self).__init__(parent)
self.exiting = False
self.aggregate = None
def __del__(self):
self.exiting = True
self.wait()
def check (self, aggregate):
self.aggregate = aggregate
# setup the thread and call run()
self.start()
def run (self):
# start checking
director.check_urls(self.aggregate)
from linkcheck.logger.text import TextLogger
class GuiLogger (TextLogger):
def __init__ (self, **args):
super(GuiLogger, self).__init__(**args)
self.widget = args["widget"]
def write (self, s, **args):
self.widget.emit(QtCore.SIGNAL("addMessage(QString)"), s)
def start_fileoutput (self):
pass
def close_fileoutput (self):
pass
class StatusLogger (object):
def __init__ (self, widget):
self.widget = widget
self.buf = []
def write (self, msg):
self.buf.append(msg)
def writeln (self, msg):
self.buf.extend([msg, unicode(os.linesep)])
def flush (self):
self.widget.emit(QtCore.SIGNAL("setStatus(QString)"), u"".join(self.buf))
self.buf = []

163
gui/linkchecker_ui.py Normal file
View file

@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui/mainwindow.ui'
#
# Created: Wed Jun 11 15:18:13 2008
# by: PyQt4 UI code generator 4.3.3
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(QtCore.QSize(QtCore.QRect(0,0,400,500).size()).expandedTo(MainWindow.minimumSizeHint()))
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
MainWindow.setSizePolicy(sizePolicy)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setGeometry(QtCore.QRect(0,26,400,455))
self.centralwidget.setObjectName("centralwidget")
self.layoutWidget = QtGui.QWidget(self.centralwidget)
self.layoutWidget.setGeometry(QtCore.QRect(20,20,351,411))
self.layoutWidget.setObjectName("layoutWidget")
self.vboxlayout = QtGui.QVBoxLayout(self.layoutWidget)
self.vboxlayout.setObjectName("vboxlayout")
self.gridlayout = QtGui.QGridLayout()
self.gridlayout.setObjectName("gridlayout")
self.label = QtGui.QLabel(self.layoutWidget)
self.label.setObjectName("label")
self.gridlayout.addWidget(self.label,0,0,1,1)
self.hboxlayout = QtGui.QHBoxLayout()
self.hboxlayout.setObjectName("hboxlayout")
self.lineEdit = QtGui.QLineEdit(self.layoutWidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(1)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth())
self.lineEdit.setSizePolicy(sizePolicy)
self.lineEdit.setObjectName("lineEdit")
self.hboxlayout.addWidget(self.lineEdit)
spacerItem = QtGui.QSpacerItem(40,20,QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Minimum)
self.hboxlayout.addItem(spacerItem)
self.gridlayout.addLayout(self.hboxlayout,0,1,1,1)
self.label_2 = QtGui.QLabel(self.layoutWidget)
self.label_2.setObjectName("label_2")
self.gridlayout.addWidget(self.label_2,1,0,1,1)
self.hboxlayout1 = QtGui.QHBoxLayout()
self.hboxlayout1.setObjectName("hboxlayout1")
self.spinBox = QtGui.QSpinBox(self.layoutWidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.spinBox.sizePolicy().hasHeightForWidth())
self.spinBox.setSizePolicy(sizePolicy)
self.spinBox.setMinimumSize(QtCore.QSize(0,0))
self.spinBox.setObjectName("spinBox")
self.hboxlayout1.addWidget(self.spinBox)
spacerItem1 = QtGui.QSpacerItem(40,20,QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Minimum)
self.hboxlayout1.addItem(spacerItem1)
self.gridlayout.addLayout(self.hboxlayout1,1,1,1,1)
self.label_3 = QtGui.QLabel(self.layoutWidget)
self.label_3.setObjectName("label_3")
self.gridlayout.addWidget(self.label_3,2,0,1,1)
self.hboxlayout2 = QtGui.QHBoxLayout()
self.hboxlayout2.setObjectName("hboxlayout2")
self.checkBox = QtGui.QCheckBox(self.layoutWidget)
self.checkBox.setObjectName("checkBox")
self.hboxlayout2.addWidget(self.checkBox)
spacerItem2 = QtGui.QSpacerItem(40,20,QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Minimum)
self.hboxlayout2.addItem(spacerItem2)
self.gridlayout.addLayout(self.hboxlayout2,2,1,1,1)
self.vboxlayout.addLayout(self.gridlayout)
self.hboxlayout3 = QtGui.QHBoxLayout()
self.hboxlayout3.setObjectName("hboxlayout3")
spacerItem3 = QtGui.QSpacerItem(40,20,QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Minimum)
self.hboxlayout3.addItem(spacerItem3)
self.pushButton = QtGui.QPushButton(self.layoutWidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(1)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth())
self.pushButton.setSizePolicy(sizePolicy)
self.pushButton.setObjectName("pushButton")
self.hboxlayout3.addWidget(self.pushButton)
self.vboxlayout.addLayout(self.hboxlayout3)
self.textEdit = QtGui.QTextEdit(self.layoutWidget)
self.textEdit.setUndoRedoEnabled(False)
self.textEdit.setLineWrapMode(QtGui.QTextEdit.NoWrap)
self.textEdit.setReadOnly(True)
self.textEdit.setObjectName("textEdit")
self.vboxlayout.addWidget(self.textEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0,0,400,26))
self.menubar.setObjectName("menubar")
self.menuLinkChecka = QtGui.QMenu(self.menubar)
self.menuLinkChecka.setObjectName("menuLinkChecka")
self.menuHelp = QtGui.QMenu(self.menubar)
self.menuHelp.setObjectName("menuHelp")
MainWindow.setMenuBar(self.menubar)
self.statusBar = QtGui.QStatusBar(MainWindow)
self.statusBar.setGeometry(QtCore.QRect(0,481,400,19))
self.statusBar.setObjectName("statusBar")
MainWindow.setStatusBar(self.statusBar)
self.actionQuit = QtGui.QAction(MainWindow)
self.actionQuit.setObjectName("actionQuit")
self.actionAbout = QtGui.QAction(MainWindow)
self.actionAbout.setObjectName("actionAbout")
self.menuLinkChecka.addAction(self.actionQuit)
self.menuHelp.addAction(self.actionAbout)
self.menubar.addAction(self.menuLinkChecka.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())
self.label.setBuddy(self.lineEdit)
self.label_2.setBuddy(self.spinBox)
self.label_3.setBuddy(self.checkBox)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "LinkChecker", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("MainWindow", "URL", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setText(QtGui.QApplication.translate("MainWindow", "Recursion", None, QtGui.QApplication.UnicodeUTF8))
self.label_3.setText(QtGui.QApplication.translate("MainWindow", "Show all", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton.setText(QtGui.QApplication.translate("MainWindow", "Check", None, QtGui.QApplication.UnicodeUTF8))
self.menuLinkChecka.setTitle(QtGui.QApplication.translate("MainWindow", "File", None, QtGui.QApplication.UnicodeUTF8))
self.menuHelp.setTitle(QtGui.QApplication.translate("MainWindow", "Help", None, QtGui.QApplication.UnicodeUTF8))
self.actionQuit.setText(QtGui.QApplication.translate("MainWindow", "Quit", None, QtGui.QApplication.UnicodeUTF8))
self.actionAbout.setText(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8))

254
gui/ui/mainwindow.ui Normal file
View file

@ -0,0 +1,254 @@
<ui version="4.0" >
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>500</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle" >
<string>LinkChecker</string>
</property>
<property name="statusTip" >
<string/>
</property>
<widget class="QWidget" name="centralwidget" >
<property name="geometry" >
<rect>
<x>0</x>
<y>26</y>
<width>400</width>
<height>455</height>
</rect>
</property>
<widget class="QWidget" name="layoutWidget" >
<property name="geometry" >
<rect>
<x>20</x>
<y>20</y>
<width>351</width>
<height>411</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>URL</string>
</property>
<property name="buddy" >
<cstring>lineEdit</cstring>
</property>
</widget>
</item>
<item row="0" column="1" >
<layout class="QHBoxLayout" name="horizontalLayout_3" >
<item>
<widget class="QLineEdit" name="lineEdit" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>Recursion</string>
</property>
<property name="buddy" >
<cstring>spinBox</cstring>
</property>
</widget>
</item>
<item row="1" column="1" >
<layout class="QHBoxLayout" name="horizontalLayout_2" >
<item>
<widget class="QSpinBox" name="spinBox" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Minimum" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>Show all</string>
</property>
<property name="buddy" >
<cstring>checkBox</cstring>
</property>
</widget>
</item>
<item row="2" column="1" >
<layout class="QHBoxLayout" name="horizontalLayout_4" >
<item>
<widget class="QCheckBox" name="checkBox" >
<property name="text" >
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<spacer name="horizontalSpacer_4" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Minimum" >
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>Check</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="textEdit" >
<property name="undoRedoEnabled" >
<bool>false</bool>
</property>
<property name="lineWrapMode" >
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="readOnly" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QMenuBar" name="menubar" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuLinkChecka" >
<property name="title" >
<string>File</string>
</property>
<addaction name="actionQuit" />
</widget>
<widget class="QMenu" name="menuHelp" >
<property name="title" >
<string>Help</string>
</property>
<addaction name="actionAbout" />
</widget>
<addaction name="menuLinkChecka" />
<addaction name="menuHelp" />
</widget>
<widget class="QStatusBar" name="statusBar" >
<property name="geometry" >
<rect>
<x>0</x>
<y>481</y>
<width>400</width>
<height>19</height>
</rect>
</property>
</widget>
<action name="actionQuit" >
<property name="text" >
<string>Quit</string>
</property>
</action>
<action name="actionAbout" >
<property name="text" >
<string>About</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>