|
@@ -12,6 +12,7 @@ from PyQt4.QtGui import *
|
|
|
from PyQt4.QtCore import *
|
|
|
|
|
|
from canvas import Canvas
|
|
|
+from zoomwidget import ZoomWidget
|
|
|
|
|
|
__appname__ = 'labelme'
|
|
|
|
|
@@ -74,23 +75,43 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
self.dock = QDockWidget(u'Label', parent=self)
|
|
|
self.dock.setObjectName(u'Label')
|
|
|
self.dock.setWidget(self.label)
|
|
|
+ self.zoom_widget = ZoomWidget()
|
|
|
#self.dock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetFloatable)
|
|
|
|
|
|
- self.imageWidget = Canvas()
|
|
|
- self.imageWidget.setAlignment(Qt.AlignCenter)
|
|
|
- self.imageWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
|
+ self.canvas = Canvas()
|
|
|
+ self.canvas.setAlignment(Qt.AlignCenter)
|
|
|
+ self.canvas.setContextMenuPolicy(Qt.ActionsContextMenu)
|
|
|
|
|
|
+ scroll = QScrollArea()
|
|
|
+ scroll.setWidget(self.canvas)
|
|
|
+ scroll.setWidgetResizable(True)
|
|
|
+
|
|
|
+ self.setCentralWidget(scroll)
|
|
|
self.addDockWidget(Qt.BottomDockWidgetArea, self.dock)
|
|
|
- self.setCentralWidget(self.imageWidget)
|
|
|
|
|
|
# Actions
|
|
|
quit = action(self, '&Quit', self.close, 'Ctrl+Q', u'Exit application')
|
|
|
open = action(self, '&Open', self.openFile, 'Ctrl+O', u'Open file')
|
|
|
+ color = action(self, '&Color', self.chooseColor, 'Ctrl+C', u'Choose line color')
|
|
|
labl = self.dock.toggleViewAction()
|
|
|
labl.setShortcut('Ctrl+L')
|
|
|
|
|
|
- add_actions(self.menu('&File'), (open, None, labl, None, quit))
|
|
|
- add_actions(self.toolbar('Tools'), (open, None, labl, None, quit))
|
|
|
+ zoom = QWidgetAction(self)
|
|
|
+ zoom.setDefaultWidget(self.zoom_widget)
|
|
|
+ fit_window = action(self, '&Fit Window', self.setFitWindow,
|
|
|
+ 'Ctrl+F', u'Fit image to window', checkable=True)
|
|
|
+
|
|
|
+ self.menus = struct(
|
|
|
+ file=self.menu('&File'),
|
|
|
+ edit=self.menu('&Image'),
|
|
|
+ view=self.menu('&View'))
|
|
|
+ add_actions(self.menus.file, (open, quit))
|
|
|
+ add_actions(self.menus.edit, (color, fit_window))
|
|
|
+ add_actions(self.menus.view, (labl,))
|
|
|
+
|
|
|
+ self.tools = self.toolbar('Tools')
|
|
|
+ add_actions(self.tools, (open, color, None, zoom, fit_window, None, quit))
|
|
|
+
|
|
|
|
|
|
self.statusBar().showMessage('%s started.' % __appname__)
|
|
|
self.statusBar().show()
|
|
@@ -99,6 +120,9 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
self.image = QImage()
|
|
|
self.filename = filename
|
|
|
self.recent_files = []
|
|
|
+ self.color = None
|
|
|
+ self.zoom_level = 100
|
|
|
+ self.fit_window = False
|
|
|
|
|
|
# TODO: Could be completely declarative.
|
|
|
# Restore application settings.
|
|
@@ -120,12 +144,23 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
# or simply:
|
|
|
#self.restoreGeometry(settings['window/geometry']
|
|
|
self.restoreState(settings['window/state'])
|
|
|
+ self.color = QColor(settings.get('line/color', QColor(0, 255, 0, 128)))
|
|
|
|
|
|
# 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))
|
|
|
|
|
|
+ # Callbacks:
|
|
|
+ self.zoom_widget.editingFinished.connect(self.showImage)
|
|
|
+
|
|
|
+
|
|
|
+ ## Callback functions:
|
|
|
+ def setFitWindow(self, value=True):
|
|
|
+ self.zoom_widget.setEnabled(not value)
|
|
|
+ self.fit_window = value
|
|
|
+ self.showImage()
|
|
|
+
|
|
|
def queueEvent(self, function):
|
|
|
QTimer.singleShot(0, function)
|
|
|
|
|
@@ -146,22 +181,27 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
self.showImage()
|
|
|
self.statusBar().showMessage(message)
|
|
|
|
|
|
- def showImage(self):
|
|
|
- if self.image.isNull():
|
|
|
- return
|
|
|
- self.imageWidget.setPixmap(self.scaled(QPixmap.fromImage(self.image)))
|
|
|
- self.imageWidget.show()
|
|
|
-
|
|
|
def resizeEvent(self, event):
|
|
|
- if self.imageWidget and self.imageWidget.pixmap():
|
|
|
- self.imageWidget.setPixmap(self.scaled(self.imageWidget.pixmap()))
|
|
|
+ if self.fit_window and self.canvas and not self.image.isNull():
|
|
|
+ self.showImage()
|
|
|
super(MainWindow, self).resizeEvent(event)
|
|
|
|
|
|
- def scaled(self, pixmap):
|
|
|
- width = self.centralWidget().width()
|
|
|
- height = self.centralWidget().height()
|
|
|
- return pixmap.scaled(width, height,
|
|
|
- Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
|
|
+ def showImage(self):
|
|
|
+ if self.image.isNull():
|
|
|
+ return
|
|
|
+ size = self.imageSize()
|
|
|
+ self.canvas.setPixmap(QPixmap.fromImage(self.image.scaled(
|
|
|
+ size, Qt.KeepAspectRatio, Qt.SmoothTransformation)))
|
|
|
+ self.canvas.show()
|
|
|
+
|
|
|
+ def imageSize(self):
|
|
|
+ """Calculate the size of the image based on current settings."""
|
|
|
+ if self.fit_window:
|
|
|
+ width, height = self.centralWidget().width()-2, self.centralWidget().height()-2
|
|
|
+ else: # Follow zoom:
|
|
|
+ s = self.zoom_widget.value() / 100.0
|
|
|
+ width, height = s * self.image.width(), s * self.image.height()
|
|
|
+ return QSize(width, height)
|
|
|
|
|
|
def closeEvent(self, event):
|
|
|
# TODO: Make sure changes are saved.
|
|
@@ -170,6 +210,7 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
s['window/size'] = self.size()
|
|
|
s['window/position'] = self.pos()
|
|
|
s['window/state'] = self.saveState()
|
|
|
+ s['line/color'] = self.color
|
|
|
#s['window/geometry'] = self.saveGeometry()
|
|
|
|
|
|
def updateFileMenu(self):
|
|
@@ -192,6 +233,10 @@ class MainWindow(QMainWindow, WindowMixin):
|
|
|
# TODO: Prompt user to save labels etc.
|
|
|
return True
|
|
|
|
|
|
+ def chooseColor(self):
|
|
|
+ self.color = QColorDialog.getColor(self.color, self,
|
|
|
+ u'Choose line color', QColorDialog.ShowAlphaChannel)
|
|
|
+
|
|
|
|
|
|
class Settings(object):
|
|
|
"""Convenience dict-like wrapper around QSettings."""
|
|
@@ -219,6 +264,10 @@ class Settings(object):
|
|
|
return value
|
|
|
|
|
|
|
|
|
+class struct(object):
|
|
|
+ def __init__(self, **kwargs):
|
|
|
+ self.__dict__.update(kwargs)
|
|
|
+
|
|
|
|
|
|
def main(argv):
|
|
|
"""Standard boilerplate Qt application code."""
|