linkchecker/linkcheck/gui/__init__.py

624 lines
24 KiB
Python
Raw Normal View History

# -*- coding: iso-8859-1 -*-
2014-01-08 21:33:04 +00:00
# Copyright (C) 2008-2014 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.
#
2009-07-24 21:58:20 +00:00
# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import os
import sys
2011-10-20 07:12:28 +00:00
import re
import webbrowser
from PyQt4 import QtCore, QtGui
from .linkchecker_ui_main import Ui_MainWindow
from .properties import set_properties, clear_properties
from .statistics import set_statistics, clear_statistics
from .debug import LinkCheckerDebug
from .logger import SignalLogger, GuiLogHandler, StatusLogger
from .help import HelpWindow
from .options import LinkCheckerOptions
from .checker import CheckerThread
2009-03-07 13:40:13 +00:00
from .contextmenu import ContextMenu
2010-10-03 10:12:57 +00:00
from .editor import EditorWindow
from .updater import UpdateDialog
2010-11-26 19:29:33 +00:00
from .urlmodel import UrlItemModel
2010-11-26 20:23:27 +00:00
from .urlsave import urlsave
2010-11-17 19:28:43 +00:00
from .settings import Settings
2011-05-11 20:15:04 +00:00
from .recentdocs import RecentDocumentModel
from .projects import openproject, saveproject, loadproject, ProjectExt
from .. import configuration, checker, director, get_link_pat, \
2014-03-01 11:01:47 +00:00
strformat, fileutil, LinkCheckerError, i18n
from ..containers import enum
2010-10-03 10:12:57 +00:00
from .. import url as urlutil
from ..checker import httpheaders
2009-07-29 19:53:52 +00:00
DocBaseUrl = "qthelp://bfk.app.linkchecker/doc/"
RegistryBase = "Bastian"
Status = enum('idle', 'checking')
2012-11-06 20:54:09 +00:00
MaxMessageLength = 60
def get_app_style ():
"""Return appropriate QStyle object for the current platform to
be used in QApplication.setStyle().
Currently prefers Macintosh on OS X, else Plastique.
2011-05-07 19:14:08 +00:00
Style names are case insensitive.
See also
http://doc.trolltech.com/latest/gallery-macintosh.html
and
http://doc.trolltech.com/latest/gallery-plastique.html
"""
if sys.platform == 'darwin':
style = "Macintosh"
else:
style = "Plastique"
return QtGui.QStyleFactory.create(style)
2011-05-09 18:49:07 +00:00
def get_icon (name):
"""Return QIcon with given pixmap resource name."""
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(name), QtGui.QIcon.Normal, QtGui.QIcon.Off)
return icon
def warninglines2regex(lines):
"""Convert a list of strings to a regular expression matching any of
the given strings."""
return u"|".join([re.escape(line) for line in lines])
class LinkCheckerMain (QtGui.QMainWindow, Ui_MainWindow):
2011-02-17 18:59:02 +00:00
"""The main window displaying checked URLs."""
2010-11-18 22:26:34 +00:00
log_url_signal = QtCore.pyqtSignal(object)
2014-03-14 20:06:10 +00:00
log_status_signal = QtCore.pyqtSignal(int, int, int, float, int)
log_stats_signal = QtCore.pyqtSignal(object)
error_signal = QtCore.pyqtSignal(str)
2010-11-18 22:26:34 +00:00
2012-01-26 19:23:15 +00:00
def __init__(self, parent=None, url=None, project=None):
"""Initialize UI."""
super(LinkCheckerMain, self).__init__(parent)
self.setupUi(self)
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowContextHelpButtonHint)
self.setWindowTitle(configuration.App)
2010-11-17 19:28:43 +00:00
# app settings
self.settings = Settings(RegistryBase, configuration.AppName)
# init subdialogs
self.options = LinkCheckerOptions(parent=self)
2010-10-14 19:13:28 +00:00
self.debug = LinkCheckerDebug(parent=self)
2011-05-07 19:14:08 +00:00
self.checker = CheckerThread(parent=self)
2009-03-07 13:40:13 +00:00
self.contextmenu = ContextMenu(parent=self)
2010-10-03 10:12:57 +00:00
self.editor = EditorWindow(parent=self)
2010-11-17 19:28:43 +00:00
# Note: do not use QT assistant here because of the .exe packaging
self.assistant = HelpWindow(self, self.get_qhcpath())
self.config_error = None
2011-05-09 18:49:07 +00:00
self.icon_start = get_icon(":/icons/start.png")
self.icon_stop = get_icon(":/icons/stop.png")
self.movie = QtGui.QMovie(":/icons/busy.gif")
self.movie.setCacheMode(QtGui.QMovie.CacheAll)
self.label_busy.setText(u"")
self.label_busy.setMovie(self.movie)
# init the rest
2011-05-11 20:15:04 +00:00
self.init_url(url)
self.init_treeview()
self.connect_widgets()
2012-01-09 19:52:24 +00:00
self.init_shortcuts()
self.init_config()
self.read_config()
self.init_menu()
self.init_drop()
2012-01-26 19:23:15 +00:00
self.init_app(project)
2010-11-17 19:28:43 +00:00
2011-05-11 20:15:04 +00:00
def init_url (self, url):
"""Initialize URL input."""
documents = self.settings.read_recent_documents()
self.recent = RecentDocumentModel(parent=self, documents=documents)
self.urlinput.setModel(self.recent)
if url:
self.urlinput.setText(url)
2011-07-25 19:31:33 +00:00
elif documents:
self.urlinput.setText(documents[0])
2011-05-11 20:15:04 +00:00
def init_menu (self):
"""Add menu entries for bookmark file checking."""
self.urlinput.addMenuEntries(self.menuEdit)
2014-03-01 11:01:47 +00:00
self.menuLang = self.menuEdit.addMenu(_('Languages'))
self.menuLang.setTitle(_("&Language"))
# ensure only one action is checked
langActionGroup = QtGui.QActionGroup(self)
langActionGroup.triggered.connect(self.switch_language)
for i, lang in enumerate(sorted(i18n.supported_languages)):
action = self.menuLang.addAction("&%d %s" % (i, lang))
action.setCheckable(True)
action.setData(lang)
if lang == i18n.default_language:
action.setChecked(True)
langActionGroup.addAction(action)
def init_drop(self):
2012-01-29 16:21:24 +00:00
"""Set and activate drag-and-drop functions."""
self.__class__.dragEnterEvent = self.handleDragEvent
self.__class__.dragMoveEvent = self.handleDragEvent
self.__class__.dropEvent = self.handleDropEvent
self.setAcceptDrops(True)
2012-01-26 19:23:15 +00:00
def init_app (self, project):
2011-02-17 18:59:02 +00:00
"""Set window size and position, GUI options and reset status."""
2010-11-17 20:25:13 +00:00
data = self.settings.read_geometry()
if data["size"] is not None:
self.resize(data["size"])
if data["pos"] is not None:
self.move(data["pos"])
self.options.set_options(self.settings.read_options())
self.status = Status.idle
self.actionSave.setEnabled(False)
2012-01-26 19:23:15 +00:00
if project:
loadproject(self, project)
2012-01-26 19:23:15 +00:00
else:
msg = self.config_error or _("Ready.")
self.set_statusmsg(msg)
2013-12-12 19:44:09 +00:00
data = self.settings.read_misc()
self.saveresultas = data['saveresultas']
def get_qhcpath (self):
2010-11-17 19:28:43 +00:00
"""Helper function to search for the QHC help file in different
locations."""
2012-06-18 21:05:44 +00:00
devel_dir = os.path.join(configuration.configdata.install_data, "doc", "html")
return configuration.get_share_file('lccollection.qhc', devel_dir=devel_dir)
def connect_widgets (self):
2009-03-07 12:46:19 +00:00
"""Connect widget signals. Some signals use the AutoConnect feature.
Autoconnected methods have the form on_<objectname>_<signal>.
"""
2010-11-18 22:26:34 +00:00
def set_idle ():
2011-02-17 18:59:02 +00:00
"""Set application status to idle."""
2010-11-18 22:26:34 +00:00
self.status = Status.idle
2011-05-07 19:14:08 +00:00
self.set_statusmsg(_("Check finished."))
2011-05-09 18:49:07 +00:00
self.controlButton.clicked.disconnect(self.checker.cancel)
2010-11-18 22:26:34 +00:00
self.checker.finished.connect(set_idle)
self.checker.terminated.connect(set_idle)
2010-11-26 19:29:33 +00:00
self.log_url_signal.connect(self.model.log_url)
self.log_stats_signal.connect(self.log_stats)
self.error_signal.connect(self.internal_error)
self.options.editor.saved.connect(self.read_config)
2011-05-07 19:14:08 +00:00
self.log_status_signal.connect(self.log_status)
2012-08-14 21:00:50 +00:00
self.prop_url.linkHovered.connect(self.hover_link)
self.prop_parenturl.linkHovered.connect(self.hover_link)
2012-01-09 19:52:24 +00:00
def init_shortcuts (self):
"""Configure application shortcuts."""
def selectUrl():
2012-01-29 16:21:24 +00:00
"""Highlight URL input textbox."""
2012-01-09 19:52:24 +00:00
self.urlinput.setFocus()
self.urlinput.selectAll()
shortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+L"), self)
shortcut.activated.connect(selectUrl)
def init_treeview (self):
2011-02-17 18:59:02 +00:00
"""Set treeview model and layout."""
self.model = UrlItemModel()
self.treeView.setModel(self.model)
data = self.settings.read_treeviewcols()
self.treeView.setColumnWidth(0, data["col1"])
self.treeView.setColumnWidth(1, data["col2"])
self.treeView.setColumnWidth(2, data["col3"])
selectionModel = self.treeView.selectionModel()
selectionModel.selectionChanged.connect(self.set_properties)
def get_treeviewcols (self):
2011-02-17 18:59:02 +00:00
"""Return URL treeview column widths."""
return dict(
col1=self.treeView.columnWidth(0),
col2=self.treeView.columnWidth(1),
col3=self.treeView.columnWidth(2),
)
def init_config (self):
"""Create a configuration object."""
self.config = configuration.Configuration()
# dictionary holding overwritten values
self.config_backup = {}
# set standard GUI configuration values
2013-12-11 17:41:55 +00:00
self.config.logger_add(SignalLogger)
self.config["logger"] = self.config.logger_new(SignalLogger.LoggerName,
signal=self.log_url_signal, stats=self.log_stats_signal)
self.config["status"] = True
self.config["status_wait_seconds"] = 2
self.handler = GuiLogHandler(self.debug.log_msg_signal)
2011-05-07 19:14:08 +00:00
status = StatusLogger(self.log_status_signal)
self.config.init_logging(status, handler=self.handler)
def read_config (self, filename=None):
"""Read user and system configuration file."""
try:
self.config.read()
except LinkCheckerError as msg:
self.config_error = unicode(msg)
def set_config (self):
"""Set configuration."""
data = self.options.get_options()
self.config["recursionlevel"] = data["recursionlevel"]
self.config["verbose"] = data["verbose"]
if data["debug"]:
self.config.set_debug(["all"])
# make sure at least one thread is used
self.config["threads"] = 1
else:
self.config.reset_loglevel()
if data["warninglines"]:
lines = data["warninglines"].splitlines()
pattern = warninglines2regex(lines)
try:
ro = re.compile(pattern)
self.backup_config("warningregex", ro)
except re.error as err:
msg = _("Invalid regular expression %r: %s" % (pattern, err))
self.set_statusmsg(msg)
# set ignore patterns
ignorepats = data["ignorelines"].strip()
if ignorepats:
self.backup_config("externlinks")
lines = ignorepats.splitlines()
for line in lines:
try:
pat = get_link_pat(line, strict=1)
self.config["externlinks"].append(pat)
except re.error as err:
msg = _("Invalid regular expression %r: %s" % (pat, err))
self.set_statusmsg(msg)
def backup_config (self, key, value=None):
"""Backup config key if not already done and set given value."""
if key not in self.config_backup:
confvalue = self.config[key]
if isinstance(confvalue, list):
# make copy of lists to avoid unwanted inserted items
2012-12-13 16:06:06 +00:00
confvalue = confvalue[:]
self.config_backup[key] = confvalue
if value is not None:
self.config[key] = value
def restore_config (self):
"""Restore config from backup."""
for key in self.config_backup:
2012-12-13 16:06:06 +00:00
confvalue = self.config_backup[key]
if isinstance(confvalue, list):
# make copy of lists to avoid unwanted inserted items
confvalue = confvalue[:]
self.config[key] = confvalue
def get_status (self):
2011-02-17 18:59:02 +00:00
"""Return current application status."""
return self._status
def set_status (self, status):
2011-02-17 18:59:02 +00:00
"""Set application status."""
self._status = status
if status == Status.idle:
self.aggregate = None
2011-05-07 19:14:08 +00:00
self.controlButton.setText(_("Start"))
2011-05-09 18:49:07 +00:00
self.controlButton.setIcon(self.icon_start)
self.controlButton.setEnabled(True)
self.actionSave.setEnabled(True)
self.actionDebug.setEnabled(self.options.get_options()["debug"])
self.treeView.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.treeView.setSortingEnabled(True)
self.treeView.scrollToTop()
2011-05-09 18:49:07 +00:00
self.movie.stop()
# Reset progress information.
self.label_active.setText(u"0")
self.label_queued.setText(u"0")
self.label_checked.setText(u"0")
2011-05-09 18:49:07 +00:00
self.label_busy.hide()
self.menubar.setEnabled(True)
2011-05-10 04:56:41 +00:00
self.urlinput.setEnabled(True)
self.restore_config()
elif status == Status.checking:
self.treeView.setSortingEnabled(False)
2010-10-14 19:13:28 +00:00
self.debug.reset()
2014-03-01 11:01:47 +00:00
self.set_statusmsg(_(u"Checking site..."))
2011-05-09 18:49:07 +00:00
# disable commands
self.menubar.setEnabled(False)
2011-05-10 04:56:41 +00:00
self.urlinput.setEnabled(False)
2011-05-09 18:49:07 +00:00
# reset widgets
self.controlButton.setText(_("Stop"))
self.controlButton.setIcon(self.icon_stop)
self.controlButton.clicked.connect(self.checker.cancel)
self.movie.start()
self.label_busy.show()
status = property(get_status, set_status)
2010-11-18 22:26:34 +00:00
@QtCore.pyqtSlot()
2009-03-07 12:46:19 +00:00
def on_actionHelp_triggered (self):
"""Show help page."""
url = QtCore.QUrl("%sindex.html" % DocBaseUrl)
self.assistant.showDocumentation(url)
2010-11-18 22:26:34 +00:00
@QtCore.pyqtSlot()
2009-03-09 22:44:49 +00:00
def on_actionOptions_triggered (self):
"""Show option dialog."""
self.options.exec_()
2010-11-18 22:26:34 +00:00
@QtCore.pyqtSlot()
2009-03-09 22:44:49 +00:00
def on_actionQuit_triggered (self):
"""Quit application."""
self.close()
def closeEvent (self, e=None):
2010-11-17 19:28:43 +00:00
"""Save settings and remove registered logging handler"""
2010-11-17 20:25:13 +00:00
self.settings.save_geometry(dict(size=self.size(), pos=self.pos()))
self.settings.save_treeviewcols(self.get_treeviewcols())
2010-11-17 20:25:13 +00:00
self.settings.save_options(self.options.get_options())
2011-05-11 20:15:04 +00:00
self.settings.save_recent_documents(self.recent.get_documents())
2013-12-12 19:44:09 +00:00
self.settings.save_misc(dict(saveresultas=self.saveresultas))
2010-11-17 19:28:43 +00:00
self.settings.sync()
self.config.remove_loghandler(self.handler)
if e is not None:
e.accept()
2010-11-18 22:26:34 +00:00
@QtCore.pyqtSlot()
2009-03-07 12:46:19 +00:00
def on_actionAbout_triggered (self):
"""Display about dialog."""
2011-04-14 10:20:56 +00:00
modules = u"<br>\n".join(configuration.get_modules_info())
d = {
"app": configuration.App,
"appname": configuration.AppName,
"copyright": configuration.HtmlCopyright,
2011-02-10 11:40:38 +00:00
"donateurl": configuration.DonateUrl,
2011-04-14 10:20:56 +00:00
"pyver": u"%d.%d.%d" % sys.version_info[:3],
"modules": modules,
2012-11-08 10:59:20 +00:00
"portable": _("yes") if configuration.Portable else _("no"),
2011-05-05 16:30:58 +00:00
"releasedate": configuration.ReleaseDate,
}
QtGui.QMessageBox.about(self, _(u"About %(appname)s") % d,
_(u"""<qt><center>
<h1>%(app)s</h1>
2011-05-05 16:30:58 +00:00
<p>Released on %(releasedate)s
<p>Python: %(pyver)s<br>
2012-11-08 10:59:20 +00:00
%(modules)s<br>
Portable version: %(portable)s
2010-12-03 21:49:32 +00:00
<p>%(copyright)s
<br>%(appname)s is licensed under the
<a href="http://www.gnu.org/licenses/gpl.html">GPL</a>
2010-12-03 21:49:32 +00:00
Version 2 or later.
2011-02-10 11:40:38 +00:00
<p>If you like %(appname)s, consider one of several ways to
<a href="%(donateurl)s">donate</a>. Thanks!
</center></qt>""") % d)
2010-11-18 22:26:34 +00:00
@QtCore.pyqtSlot()
2010-10-14 19:13:28 +00:00
def on_actionDebug_triggered (self):
"""Display debug dialog."""
self.debug.show()
2012-01-26 19:23:15 +00:00
@QtCore.pyqtSlot()
def on_actionOpen_project_triggered (self):
"""Open project."""
openproject(self)
@QtCore.pyqtSlot()
def on_actionSave_project_triggered (self):
"""Save project."""
saveproject(self, self.get_url())
2010-11-26 20:23:27 +00:00
@QtCore.pyqtSlot()
def on_actionSave_triggered (self):
2012-01-26 19:23:15 +00:00
"""Save URL results."""
2013-12-12 19:44:09 +00:00
saveresultas = urlsave(self, self.config, self.model.urls)
if saveresultas:
self.saveresultas = saveresultas
2010-11-26 20:23:27 +00:00
2011-01-04 18:45:43 +00:00
@QtCore.pyqtSlot()
def on_actionCheckUpdates_triggered (self):
"""Display update check result."""
dialog = UpdateDialog(self)
dialog.reset()
dialog.show()
2011-01-04 18:45:43 +00:00
def start (self):
"""Start a new check."""
if self.status == Status.idle:
self.check()
2011-05-07 19:14:08 +00:00
on_urlinput_returnPressed = start
def cancel (self):
"""Note that checking is canceled."""
self.controlButton.setEnabled(False)
duration = strformat.strduration_long(self.config["aborttimeout"])
2012-11-06 20:54:09 +00:00
self.set_statusmsg(_(u"Closing active URLs with timeout %s...") % duration)
2011-05-07 19:14:08 +00:00
2011-05-09 18:49:07 +00:00
@QtCore.pyqtSlot()
2011-05-07 19:14:08 +00:00
def on_controlButton_clicked (self):
"""Start or Cancel has been clicked."""
if self.status == Status.idle:
self.start()
elif self.status == Status.checking:
self.cancel()
else:
raise ValueError("Invalid application status %r" % self.status)
def get_url (self):
"""Return URL to check from the urlinput widget."""
url = strformat.stripurl(unicode(self.urlinput.text()))
2012-09-23 14:19:42 +00:00
url = checker.guess_url(url)
if url and u":" not in url:
# Look for local file, else assume it's an HTTP URL.
if not os.path.exists(url):
url = u"http://%s" % url
return url
def check (self):
"""Check given URL."""
self.model.clear()
clear_properties(self)
clear_statistics(self)
self.set_config()
aggregate = director.get_aggregate(self.config)
url = self.get_url()
if not url:
2011-05-07 19:14:08 +00:00
self.set_statusmsg(_("Error, empty URL"))
return
2011-05-07 19:14:08 +00:00
self.set_statusmsg(_("Checking '%s'.") % strformat.limit(url, 40))
url_data = checker.get_url_from(url, 0, aggregate, extern=(0, 0))
2011-05-11 20:15:04 +00:00
self.recent.add_document(url)
aggregate.urlqueue.put(url_data)
self.aggregate = aggregate
# check in background
2011-05-07 19:14:08 +00:00
self.checker.check(self.aggregate)
self.status = Status.checking
def set_properties (self, selected, deselected):
"""Set URL properties for selected item."""
indexes = selected.indexes()
if len(indexes):
index = indexes[0]
urlitem = self.model.getUrlItem(index)
if urlitem is not None:
set_properties(self, urlitem.url_data)
selected_rows = len(self.treeView.selectionModel().selectedRows())
if selected_rows:
self.set_statusmsg(_n("%d URL selected.", "%d URLs selected",
selected_rows) % selected_rows)
else:
self.set_statusmsg(_("Ready."))
2009-03-07 13:40:13 +00:00
def on_treeView_customContextMenuRequested (self, point):
2009-03-07 13:40:13 +00:00
"""Show item context menu."""
2010-11-06 07:40:46 +00:00
urlitem = self.model.getUrlItem(self.treeView.currentIndex())
if urlitem is not None:
self.contextmenu.enableFromItem(urlitem)
2009-03-07 13:40:13 +00:00
self.contextmenu.popup(QtGui.QCursor.pos())
2010-11-18 22:26:34 +00:00
@QtCore.pyqtSlot()
2009-03-07 13:40:13 +00:00
def on_actionViewOnline_triggered (self):
"""View item URL online."""
2010-11-06 07:40:46 +00:00
urlitem = self.model.getUrlItem(self.treeView.currentIndex())
if urlitem is not None:
webbrowser.open(urlitem.url_data.url)
2010-11-18 22:26:34 +00:00
@QtCore.pyqtSlot()
2010-10-03 10:12:57 +00:00
def on_actionViewParentOnline_triggered (self):
"""View item parent URL online."""
2010-11-06 07:40:46 +00:00
urlitem = self.model.getUrlItem(self.treeView.currentIndex())
if urlitem is not None:
webbrowser.open(urlitem.url_data.parent_url)
2010-09-30 05:32:39 +00:00
2010-11-18 22:26:34 +00:00
@QtCore.pyqtSlot()
2010-10-03 10:12:57 +00:00
def on_actionViewParentSource_triggered (self):
"""View item parent URL source in local text editor (read-only)."""
2010-11-06 07:40:46 +00:00
urlitem = self.model.getUrlItem(self.treeView.currentIndex())
if urlitem is not None:
self.view_source(urlitem.url_data.parent_url,
urlitem.url_data.line, urlitem.url_data.column)
2010-10-03 10:12:57 +00:00
def view_source (self, url, line, col):
2011-02-17 18:59:02 +00:00
"""View URL source in editor window."""
2010-10-03 10:12:57 +00:00
self.editor.setWindowTitle(u"View %s" % url)
self.editor.setUrl(url)
data, info = urlutil.get_content(url, proxy=self.config["proxy"])
if data is None:
msg = u"An error occurred retreiving URL `%s': %s." % (url, info)
self.editor.setText(msg)
2010-10-03 10:12:57 +00:00
else:
content_type = httpheaders.get_content_type(info)
if not content_type:
# read function for content type guessing
read = lambda: data
content_type = fileutil.guess_mimetype(url, read=read)
2010-10-03 10:12:57 +00:00
self.editor.setContentType(content_type)
self.editor.setText(data, line=line, col=col)
self.editor.show()
2010-09-30 05:32:39 +00:00
2010-11-18 22:26:34 +00:00
@QtCore.pyqtSlot()
def on_actionCopyToClipboard_triggered (self):
2010-09-30 05:32:39 +00:00
"""Copy item URL to clipboard."""
2010-11-06 15:44:49 +00:00
urlitem = self.model.getUrlItem(self.treeView.currentIndex())
2010-11-06 07:40:46 +00:00
if urlitem:
clipboard = QtGui.QApplication.clipboard()
2010-11-06 07:40:46 +00:00
clipboard.setText(urlitem.url_data.url)
event = QtCore.QEvent(QtCore.QEvent.Clipboard)
QtGui.QApplication.sendEvent(clipboard, event)
2011-05-07 19:14:08 +00:00
def set_statusmsg (self, msg):
"""Show given status message."""
2012-01-26 19:23:15 +00:00
self.statusBar.showMessage(msg)
2012-11-06 20:54:09 +00:00
if len(msg) > MaxMessageLength:
2011-05-07 19:14:08 +00:00
self.label_status.setToolTip(msg)
2012-11-06 20:54:09 +00:00
msg = msg[:MaxMessageLength-3]+u"..."
else:
self.label_status.setToolTip(u"")
2011-05-07 19:14:08 +00:00
self.label_status.setText(msg)
2012-08-14 21:00:50 +00:00
def hover_link (self, link):
"""Show given link in status bar."""
self.statusBar.showMessage(link)
2014-03-14 20:06:10 +00:00
def log_status (self, checked, in_progress, queued, duration,
downloaded_bytes):
2014-03-14 19:25:37 +00:00
"""Update number of checked, active and queued links."""
2011-05-07 19:14:08 +00:00
self.label_checked.setText(u"%d" % checked)
self.label_active.setText(u"%d" % in_progress)
self.label_queued.setText(u"%d" % queued)
2014-03-14 20:06:10 +00:00
# XXX display downloaded bytes
def log_stats (self, statistics):
2011-02-17 18:59:02 +00:00
"""Set statistic information for selected URL."""
set_statistics(self, statistics)
def internal_error (self, msg):
"""Display internal error message. Triggered by sys.excepthook()."""
QtGui.QMessageBox.warning(self, _(u"LinkChecker internal error"), msg)
def handleDragEvent(self, event):
"""Handle drag enter of move event."""
mime = event.mimeData()
if not mime.hasUrls():
return event.ignore()
url = mime.urls()[0]
if url.scheme() != 'file':
return event.ignore()
event.accept()
def handleDropEvent(self, event):
"""Handle drop event. Detects and loads project files, else sets the URL."""
mime = event.mimeData()
url = mime.urls()[0]
2013-01-22 18:06:10 +00:00
if url.path().toLower().endsWith(ProjectExt):
filename = unicode(url.toLocalFile())
loadproject(self, filename)
else:
2013-01-22 18:06:10 +00:00
self.urlinput.setText(url.toString())
2014-03-01 11:01:47 +00:00
def retranslateUi(self, Window):
"""Translate menu titles."""
super(LinkCheckerMain, self).retranslateUi(Window)
# self.menu_lang is created after calling retranslateUi
# the first time, so check for its excistance
if hasattr(self, "menu_lang"):
self.menuLang.setTitle(_("&Language"))
def switch_language(self, action):
"""Change UI language."""
lang = str(action.data().toString())
i18n.install_language(lang)
self.retranslateUi(self)
self.options.retranslateUi(self.options)
self.debug.retranslateUi(self.debug)
self.editor.retranslateUi(self.editor)