summaryrefslogtreecommitdiff
path: root/src/leap/gui/progress.py
diff options
context:
space:
mode:
authorkali <kali@leap.se>2012-11-06 01:26:05 +0900
committerkali <kali@leap.se>2012-11-06 01:26:05 +0900
commitc387a52f841e8933ed7282d198ed1ece863979fc (patch)
treec3465991425dc6de6ae9296b931e8a805491fa05 /src/leap/gui/progress.py
parent0a8a34879a701a2d045f628403c6a0f8be21dc82 (diff)
new validation pages in a reusable MVC style
using progress indicators inside QTableWidget
Diffstat (limited to 'src/leap/gui/progress.py')
-rw-r--r--src/leap/gui/progress.py261
1 files changed, 261 insertions, 0 deletions
diff --git a/src/leap/gui/progress.py b/src/leap/gui/progress.py
new file mode 100644
index 00000000..d04e0f1f
--- /dev/null
+++ b/src/leap/gui/progress.py
@@ -0,0 +1,261 @@
+"""
+classes used in progress pages
+from first run wizard
+"""
+try:
+ from collections import OrderedDict
+except ImportError:
+ # We must be in 2.6
+ from leap.util.dicts import OrderedDict
+import time
+
+from PyQt4 import QtCore
+from PyQt4 import QtGui
+
+from leap.baseapp.mainwindow import FunThread
+
+from leap.gui import mainwindow_rc
+
+
+class ImgWidget(QtGui.QWidget):
+
+ # XXX move to widgets
+
+ def __init__(self, parent=None, img=None):
+ super(ImgWidget, self).__init__(parent)
+ self.pic = QtGui.QPixmap(img)
+
+ def paintEvent(self, event):
+ painter = QtGui.QPainter(self)
+ painter.drawPixmap(0, 0, self.pic)
+
+
+class ProgressStep(object):
+ """
+ Data model for sequential steps
+ to be used in a progress page in
+ connection wizard
+ """
+ NAME = 0
+ DONE = 1
+
+ def __init__(self, stepname, done, index=None):
+ """
+ @param step: the name of the step
+ @type step: str
+ @param done: whether is completed or not
+ @type done: bool
+ """
+ self.index = int(index) if index else 0
+ self.name = unicode(stepname)
+ self.done = bool(done)
+
+ @classmethod
+ def columns(self):
+ return ('name', 'done')
+
+
+class ProgressStepContainer(object):
+ """
+ a container for ProgressSteps objects
+ access data in the internal dict
+ """
+
+ def __init__(self):
+ self.dirty = False
+ self.steps = {}
+
+ def step(self, identity):
+ return self.step.get(identity)
+
+ def addStep(self, step):
+ self.steps[step.index] = step
+
+ def removeStep(self, step):
+ del self.steps[step.index]
+ del step
+ self.dirty = True
+
+ def removeAllSteps(self):
+ for item in iter(self):
+ self.removeStep(item)
+
+ @property
+ def columns(self):
+ return ProgressStep.columns()
+
+ def __len__(self):
+ return len(self.steps)
+
+ def __iter__(self):
+ for step in self.steps.values():
+ yield step
+
+
+class StepsTableWidget(QtGui.QTableWidget):
+ """
+ initializes a TableWidget
+ suitable for our display purposes, like removing
+ header info and grid display
+ """
+
+ def __init__(self, parent=None):
+ super(StepsTableWidget, self).__init__(parent)
+
+ # remove headers and all edit/select behavior
+ self.horizontalHeader().hide()
+ self.verticalHeader().hide()
+ self.setEditTriggers(
+ QtGui.QAbstractItemView.NoEditTriggers)
+ self.setSelectionMode(
+ QtGui.QAbstractItemView.NoSelection)
+ width = self.width()
+ # WTF? Here init width is 100...
+ # but on populating is 456... :(
+
+ # XXX do we need this initial?
+ print 'init table. width=%s' % width
+ self.horizontalHeader().resizeSection(0, width * 0.7)
+
+ # this disables the table grid.
+ # we should add alignment to the ImgWidget (it's top-left now)
+ self.setShowGrid(False)
+
+ # XXX change image for done to rc
+
+ # Note about the "done" status painting:
+ #
+ # XXX currently we are setting the CellWidget
+ # for the whole table on a per-row basis
+ # (on add_status_line method on ValidationPage).
+ # However, a more generic solution might be
+ # to implement a custom Delegate that overwrites
+ # the paint method (so it paints a checked tickmark if
+ # done is True and some other thing if checking or false).
+ # What we have now is quick and works because
+ # I'm supposing that on first fail we will
+ # go back to previous wizard page to signal the failure.
+ # A more generic solution could be used for
+ # some failing tests if they are not critical.
+
+
+class ValidationPage(QtGui.QWizardPage):
+ """
+ class to be used as an intermediate
+ between two pages in a wizard.
+ shows feedback to the user and goes back if errors,
+ goes forward if ok.
+ initializePage triggers a one shot timer
+ that calls do_checks.
+ Derived classes should implement
+ _do_checks and
+ _do_validation
+ """
+
+ # signals
+
+ stepChanged = QtCore.pyqtSignal([str])
+
+ def __init__(self, parent=None):
+ super(ValidationPage, self).__init__(parent)
+
+ self.steps = ProgressStepContainer()
+ self.progress = QtGui.QProgressBar()
+
+ # steps table widget
+ self.stepsTableWidget = StepsTableWidget(self)
+
+ layout = QtGui.QVBoxLayout()
+ layout.addWidget(self.progress)
+ layout.addWidget(self.stepsTableWidget)
+
+ self.setLayout(layout)
+ self.layout = layout
+
+ self.timer = QtCore.QTimer()
+
+ # connect the new step status
+ # signal to status handler
+ self.stepChanged.connect(
+ self.onStepStatusChanged)
+
+ self.errors = OrderedDict()
+
+ def populateStepsTable(self):
+ # from examples,
+ # but I guess it's not needed to re-populate
+ # the whole table.
+ table = self.stepsTableWidget
+ table.setRowCount(len(self.steps))
+ columns = self.steps.columns
+ table.setColumnCount(len(columns))
+
+ for row, step in enumerate(self.steps):
+ item = QtGui.QTableWidgetItem(step.name)
+ item.setData(QtCore.Qt.UserRole,
+ long(id(step)))
+ table.setItem(row, columns.index('name'), item)
+ table.setItem(row, columns.index('done'),
+ QtGui.QTableWidgetItem(step.done))
+ self.resizeTable()
+ self.update()
+
+ def clearTable(self):
+ # ??? -- not sure what's the difference
+ #self.stepsTableWidget.clear()
+ self.stepsTableWidget.clearContents()
+
+ def resizeTable(self):
+ # resize first column to ~80%
+ table = self.stepsTableWidget
+ FIRST_COLUMN_PERCENT = 0.75
+ width = table.width()
+ print 'populate table. width=%s' % width
+ table.horizontalHeader().resizeSection(0, width * FIRST_COLUMN_PERCENT)
+
+ def onStepStatusChanged(self, status):
+ if status != "end_sentinel":
+ self.add_status_line(status)
+
+ def add_status_line(self, message):
+ print 'adding status line...'
+ index = len(self.steps)
+ step = ProgressStep(message, False, index=index)
+ self.steps.addStep(step)
+ self.populateStepsTable()
+ table = self.stepsTableWidget
+
+ # setting cell widget.
+ # see note on StepsTableWidget about plans to
+ # change this for a better solution.
+
+ table.setCellWidget(
+ index - 1,
+ ProgressStep.DONE,
+ # XXX pass image in rc
+ ImgWidget(img=":/images/checked.png"))
+
+ def go_back(self):
+ self.wizard().back()
+
+ def go_next(self):
+ self.wizard().next()
+
+ def initializePage(self):
+ self.steps.removeAllSteps()
+ self.clearTable()
+ self.resizeTable()
+ self.timer.singleShot(0, self.do_checks)
+
+ def do_checks(self):
+ """
+ launches a thread to do the checks
+ """
+ signal = self.stepChanged
+ self.checks = FunThread(
+ self._do_checks(signal=signal))
+ self.checks.finished.connect(self._do_validation)
+ self.checks.begin()
+ print 'check thread started!'
+ print 'waiting for it to terminate...'
+ self.checks.wait()