|
@@ -1,8 +1,13 @@
|
|
|
#!/usr/bin/env python
|
|
|
# -*- coding: utf8 -*-
|
|
|
|
|
|
+import os.path
|
|
|
+import re
|
|
|
import sys
|
|
|
|
|
|
+from functools import partial
|
|
|
+from collections import defaultdict
|
|
|
+
|
|
|
from PyQt4.QtGui import *
|
|
|
from PyQt4.QtCore import *
|
|
|
|
|
@@ -57,7 +62,7 @@ class WindowMixin(object):
|
|
|
|
|
|
|
|
|
class MainWindow(QMainWindow, WindowMixin):
|
|
|
- def __init__(self):
|
|
|
+ def __init__(self, filename=None):
|
|
|
super(MainWindow, self).__init__()
|
|
|
self.setWindowTitle(__appname__)
|
|
|
|
|
@@ -66,10 +71,15 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
# Main widgets.
|
|
|
self.label = QLineEdit(u'Hello world, مرحبا ، العالم, Γεια σου κόσμε!')
|
|
|
self.dock = QDockWidget(u'Label', parent=self)
|
|
|
+ self.dock.setObjectName(u'Label')
|
|
|
self.dock.setWidget(self.label)
|
|
|
|
|
|
+ self.imageWidget = QLabel()
|
|
|
+ self.imageWidget.setAlignment(Qt.AlignCenter)
|
|
|
+ self.imageWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
|
+
|
|
|
self.addDockWidget(Qt.BottomDockWidgetArea, self.dock)
|
|
|
- self.setCentralWidget(QWidget())
|
|
|
+ self.setCentralWidget(self.imageWidget)
|
|
|
|
|
|
# Actions
|
|
|
quit = action(self, '&Quit', self.close, 'Ctrl+Q', u'Exit application')
|
|
@@ -82,12 +92,109 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
self.statusBar().showMessage('%s started.' % __appname__)
|
|
|
self.statusBar().show()
|
|
|
|
|
|
+ # Application state.
|
|
|
+ self.image = QImage()
|
|
|
+ self.filename = filename
|
|
|
+ self.recent_files = []
|
|
|
+
|
|
|
+ # TODO: Could be completely declarative.
|
|
|
+ # Restore application settings.
|
|
|
+ types = {
|
|
|
+ 'filename': QString,
|
|
|
+ 'recent-files': QStringList,
|
|
|
+ 'window/size': QSize,
|
|
|
+ 'window/position': QPoint,
|
|
|
+ 'window/geometry': QByteArray,
|
|
|
+ # Docks and toolbars:
|
|
|
+ 'window/state': QByteArray,
|
|
|
+ }
|
|
|
+ self.settings = settings = Settings(types)
|
|
|
+ self.recent_files = settings['recent-files']
|
|
|
+ size = settings.get('window/size', QSize(600, 500))
|
|
|
+ position = settings.get('window/position', QPoint(0, 0))
|
|
|
+ self.resize(size)
|
|
|
+ self.move(position)
|
|
|
+ # or simply:
|
|
|
+ #self.restoreGeometry(settings['window/geometry']
|
|
|
+ self.restoreState(settings['window/state'])
|
|
|
+
|
|
|
+ # The file menu has default dynamically generated entries.
|
|
|
+ self.updateFileMenu()
|
|
|
+ # Since loading the file may take some time, make sure it runs in the background.
|
|
|
+ self.queueEvent(partial(self.loadFile, self.filename))
|
|
|
+
|
|
|
+ def queueEvent(self, function):
|
|
|
+ QTimer.singleShot(0, function)
|
|
|
+
|
|
|
+ def loadFile(self, filename=None):
|
|
|
+ """Load the specified file, or the last opened file if None."""
|
|
|
+ if filename is None:
|
|
|
+ filename = self.settings['filename']
|
|
|
+ # FIXME: Load the actual file here.
|
|
|
+ if QFile.exists(filename):
|
|
|
+ # Load image
|
|
|
+ image = QImage(filename)
|
|
|
+ if image.isNull():
|
|
|
+ message = "Failed to read %s" % filename
|
|
|
+ else:
|
|
|
+ message = "Loaded %s" % os.path.basename(unicode(filename))
|
|
|
+ self.image = image
|
|
|
+ self.filename = filename
|
|
|
+ self.showImage()
|
|
|
+ self.statusBar().showMessage(message)
|
|
|
+
|
|
|
+ def showImage(self):
|
|
|
+ if self.image.isNull():
|
|
|
+ return
|
|
|
+ self.imageWidget.setPixmap(QPixmap.fromImage(self.image))
|
|
|
+ self.imageWidget.show()
|
|
|
+
|
|
|
+ def closeEvent(self, event):
|
|
|
+ # TODO: Make sure changes are saved.
|
|
|
+ s = self.settings
|
|
|
+ s['filename'] = self.filename if self.filename else QString()
|
|
|
+ s['window/size'] = self.size()
|
|
|
+ s['window/position'] = self.pos()
|
|
|
+ s['window/state'] = self.saveState()
|
|
|
+ #s['window/geometry'] = self.saveGeometry()
|
|
|
+
|
|
|
+ def updateFileMenu(self):
|
|
|
+ """Populate menu with recent files."""
|
|
|
+
|
|
|
+
|
|
|
+class Settings(object):
|
|
|
+ """Convenience dict-like wrapper around QSettings."""
|
|
|
+ def __init__(self, types=None):
|
|
|
+ self.data = QSettings()
|
|
|
+ self.types = defaultdict(lambda: QVariant, types if types else {})
|
|
|
+
|
|
|
+ def __setitem__(self, key, value):
|
|
|
+ t = self.types[key]
|
|
|
+ self.data.setValue(key,
|
|
|
+ t(value) if not isinstance(value, t) else value)
|
|
|
+
|
|
|
+ def __getitem__(self, key):
|
|
|
+ return self._cast(key, self.data.value(key))
|
|
|
+
|
|
|
+ def get(self, key, default=None):
|
|
|
+ return self._cast(key, self.data.value(key, default))
|
|
|
+
|
|
|
+ def _cast(self, key, value):
|
|
|
+ # XXX: Very nasty way of converting types to QVariant methods :P
|
|
|
+ t = self.types[key]
|
|
|
+ if t != QVariant:
|
|
|
+ method = getattr(QVariant, re.sub('^Q', 'to', t.__name__, count=1))
|
|
|
+ return method(value)
|
|
|
+ return value
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
def main(argv):
|
|
|
"""Standard boilerplate Qt application code."""
|
|
|
app = QApplication(argv)
|
|
|
app.setApplicationName(__appname__)
|
|
|
- win = MainWindow()
|
|
|
+ win = MainWindow(argv[1] if len(argv) == 2 else None)
|
|
|
win.show()
|
|
|
return app.exec_()
|
|
|
|