class TextTreeNode(QGraphicsTextItem, GraphNode): def setBackgroundBrush(self, brush): if self._background_brush != brush: self._background_brush = QBrush(brush) color = brush.color() r, g, b, _ = color.getRgb() lum = 0.2126 * r + 0.7152 * g + 0.0722 * b if lum > 100: self.setDefaultTextColor(Qt.black) else: self.setDefaultTextColor(Qt.white) self.update() def backgroundBrush(self): brush = getattr(self, "_background_brush") if brush is None: brush = getattr(self.scene(), "defaultItemBrush", Qt.NoBrush) return QBrush(brush) backgroundBrush = pyqtProperty( "QBrush", fget=backgroundBrush, fset=setBackgroundBrush, doc="Background brush") def __init__(self, parent, *args, **kwargs): QGraphicsTextItem.__init__(self, *args) GraphNode.__init__(self, **kwargs) self._background_brush = None self._rect = None self.parent = parent font = self.font() font.setPointSize(10) self.setFont(font) self.droplet = GraphicsDroplet(-5, 0, 10, 10, self) self.droplet.setPos(self.rect().center().x(), self.rect().height()) self.document().contentsChanged.connect(self.update_contents) self.isOpen = True self.setFlag(QGraphicsItem.ItemIsSelectable, True) def setHtml(self, html): return super().setHtml("<body>" + html + "</body>") def update_contents(self): self.setTextWidth(-1) self.setTextWidth(self.document().idealWidth()) self.droplet.setPos(self.rect().center().x(), self.rect().height()) self.droplet.setVisible(bool(self.branches)) def set_rect(self, rect): self.prepareGeometryChange() rect = QRectF() if rect is None else rect self._rect = rect self.update_contents() self.update() def shape(self): path = QPainterPath() path.addRect(self.boundingRect()) return path def rect(self): if getattr(self, "_rect", QRectF()).isValid(): return self._rect else: return QRectF(QPointF(0, 0), self.document().size()) | \ getattr(self, "_rect", QRectF(0, 0, 1, 1)) def boundingRect(self): return self._rect if getattr(self, "_rect", QRectF()).isValid() \ else super().boundingRect() @property def branches(self): return [edge.node2 for edge in self.graph_edges() if edge.node1 is self] def paint(self, painter, option, widget=0): painter.save() painter.setBrush(self.backgroundBrush) painter.setPen(QPen(Qt.gray)) rect = self.rect() painter.drawRoundedRect(rect, 4, 4) painter.restore() painter.setClipRect(rect) return QGraphicsTextItem.paint(self, painter, option, widget)
class ProgressBarMixin: __progressBarValue = 0 __progressState = 0 startTime = -1 # used in progressbar captionTitle = "" def setCaption(self, caption): self.captionTitle = caption self.setWindowTitle(caption) @pyqtSlot() def progressBarInit(self): """ Initialize the widget's progress (i.e show and set progress to 0%). """ self.startTime = time.time() self.setWindowTitle(self.captionTitle + " (0% complete)") if self.__progressState != 1: self.__progressState = 1 self.processingStateChanged.emit(1) self.progressBarSet(0) @pyqtSlot(float) def progressBarSet(self, value): """ Set the current progress bar to `value`. Parameters ---------- value : float Progress value. """ old = self.__progressBarValue self.__progressBarValue = value if value > 0: if self.__progressState != 1: warnings.warn( "progressBarSet() called without a " "preceding progressBarInit()", stacklevel=2) self.__progressState = 1 self.processingStateChanged.emit(1) usedTime = max(1., time.time() - self.startTime) totalTime = 100.0 * usedTime / value remainingTime = max(0, int(totalTime - usedTime)) hrs = remainingTime // 3600 mins = (remainingTime % 3600) // 60 secs = remainingTime % 60 if hrs > 0: text = "{}:{:02}:{:02}".format(hrs, mins, secs) else: text = "{}:{}:{:02}".format(hrs, mins, secs) self.setWindowTitle("{} ({:d}%, ETA: {})".format( self.captionTitle, int(value), text)) else: self.setWindowTitle(self.captionTitle + " (0% complete)") if old != value: self.progressBarValueChanged.emit(value) def progressBarValue(self): """ Return the state (value) of the progress bar """ return self.__progressBarValue progressBarValueChanged = pyqtSignal(float) progressBarValue = pyqtProperty(float, fset=progressBarSet, fget=progressBarValue, notify=progressBarValueChanged) processingStateChanged = pyqtSignal(int) processingState = pyqtProperty(int, fget=lambda self: self.__progressState, notify=processingStateChanged) @pyqtSlot(float) def progressBarAdvance(self, value): """ Advance the progress bar by `value`. Parameters ---------- value : float Progress value increment. """ self.progressBarSet(self.__progressBarValue + value) @pyqtSlot() def progressBarFinished(self): """ Stop the widget's progress (i.e hide the progress bar). Parameters ---------- value : float Progress value increment. """ self.setWindowTitle(self.captionTitle) if self.__progressState != 0: self.__progressState = 0 self.processingStateChanged.emit(0)
class ProgressBarMixin: # Set these here so we avoid having to call `__init__` fromm classes # that use this mix-in __progressBarValue = -1 __progressState = 0 startTime = time.time() # used in progressbar def progressBarInit(self, processEvents=None): """ Initialize the widget's progress (i.e show and set progress to 0%). Parameters ---------- processEvents : Optional[QEventLoop.ProcessEventsFlags] If present then `QApplication.processEvents(processEvents)` will be called. Passing any value here is highly discouraged. It is up to the client to handle the consequences of such action. .. versionchanged:: 3.4.2 Deprecated and changed default `processEvents` value. """ self.startTime = time.time() self.setWindowTitle(self.captionTitle + " (0% complete)") if self.__progressState != 1: self.__progressState = 1 self.processingStateChanged.emit(1) self.progressBarSet(0, processEvents) def progressBarSet(self, value, processEvents=None): """ Set the current progress bar to `value`. Parameters ---------- value : float Progress value. processEvents : Optional[QEventLoop.ProcessEventsFlags] If present then `QApplication.processEvents(processEvents)` will be called. Passing any value here is highly discouraged. It is up to the client to handle the consequences of such action. .. versionchanged:: 3.4.2 Deprecated and changed default `processEvents` value. """ old = self.__progressBarValue self.__progressBarValue = value if value > 0: if self.__progressState != 1: warnings.warn( "progressBarSet() called without a " "preceding progressBarInit()", stacklevel=2, ) self.__progressState = 1 self.processingStateChanged.emit(1) usedTime = max(1, time.time() - self.startTime) totalTime = 100.0 * usedTime / value remainingTime = max(0, int(totalTime - usedTime)) hrs = remainingTime // 3600 mins = (remainingTime % 3600) // 60 secs = remainingTime % 60 if hrs > 0: text = "{}:{:02}:{:02}".format(hrs, mins, secs) else: text = "{}:{}:{:02}".format(hrs, mins, secs) self.setWindowTitle("{} ({:d}%, ETA: {})".format( self.captionTitle, int(value), text)) else: self.setWindowTitle(self.captionTitle + " (0% complete)") if old != value: self.progressBarValueChanged.emit(value) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents) def progressBarValue(self): """Return the state of the progress bar """ return self.__progressBarValue progressBarValue = pyqtProperty(float, fset=progressBarSet, fget=progressBarValue) processingState = pyqtProperty(int, fget=lambda self: self.__progressState) def progressBarAdvance(self, value, processEvents=None): """ Advance the progress bar by `value`. Parameters ---------- value : float Progress value increment. processEvents : Optional[QEventLoop.ProcessEventsFlags] If present then `QApplication.processEvents(processEvents)` will be called. Passing any value here is highly discouraged. It is up to the client to handle the consequences of such action. .. versionchanged:: 3.4.2 Deprecated and changed default `processEvents` value. """ self.progressBarSet(self.progressBarValue + value, processEvents) def progressBarFinished(self, processEvents=None): """ Stop the widget's progress (i.e hide the progress bar). Parameters ---------- value : float Progress value increment. processEvents : Optional[QEventLoop.ProcessEventsFlags] If present then `QApplication.processEvents(processEvents)` will be called. Passing any value here is highly discouraged. It is up to the client to handle the consequences of such action. .. versionchanged:: 3.4.2 Deprecated and changed default `processEvents` value. """ self.setWindowTitle(self.captionTitle) if self.__progressState != 0: self.__progressState = 0 self.processingStateChanged.emit(0) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents) @contextlib.contextmanager def progressBar(self, iterations=0): """ Context manager for progress bar. Using it ensures that the progress bar is removed at the end without needing the `finally` blocks. Usage: with self.progressBar(20) as progress: ... progress.advance() or with self.progressBar() as progress: ... progress.advance(0.15) or with self.progressBar(): ... self.progressBarSet(50) :param iterations: the number of iterations (optional) :type iterations: int """ progress_bar = gui.ProgressBar(self, iterations) try: yield progress_bar finally: progress_bar.finish()
class ProgressBarMixin: # Set these here so we avoid having to call `__init__` fromm classes # that use this mix-in __progressBarValue = -1 __progressState = 0 startTime = time.time() # used in progressbar def progressBarInit(self, processEvents=QEventLoop.AllEvents): """ Initialize the widget's progress (i.e show and set progress to 0%). .. note:: This method will by default call `QApplication.processEvents` with `processEvents`. To suppress this behavior pass ``processEvents=None``. :param processEvents: Process events flag :type processEvents: `QEventLoop.ProcessEventsFlags` or `None` """ self.startTime = time.time() self.setWindowTitle(self.captionTitle + " (0% complete)") if self.__progressState != 1: self.__progressState = 1 self.processingStateChanged.emit(1) self.progressBarSet(0, processEvents) def progressBarSet(self, value, processEvents=QEventLoop.AllEvents): """ Set the current progress bar to `value`. .. note:: This method will by default call `QApplication.processEvents` with `processEvents`. To suppress this behavior pass ``processEvents=None``. :param float value: Progress value :param processEvents: Process events flag :type processEvents: `QEventLoop.ProcessEventsFlags` or `None` """ old = self.__progressBarValue self.__progressBarValue = value if value > 0: if self.__progressState != 1: warnings.warn( "progressBarSet() called without a " "preceding progressBarInit()", stacklevel=2) self.__progressState = 1 self.processingStateChanged.emit(1) usedTime = max(1, time.time() - self.startTime) totalTime = 100.0 * usedTime / value remainingTime = max(0, int(totalTime - usedTime)) hrs = remainingTime // 3600 mins = (remainingTime % 3600) // 60 secs = remainingTime % 60 if hrs > 0: text = "{}:{:02}:{:02}".format(hrs, mins, secs) else: text = "{}:{}:{:02}".format(hrs, mins, secs) self.setWindowTitle("{} ({:d}%, ETA: {})".format( self.captionTitle, int(value), text)) else: self.setWindowTitle(self.captionTitle + " (0% complete)") if old != value: self.progressBarValueChanged.emit(value) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents) def progressBarValue(self): """Return the state of the progress bar """ return self.__progressBarValue progressBarValue = pyqtProperty(float, fset=progressBarSet, fget=progressBarValue) processingState = pyqtProperty(int, fget=lambda self: self.__progressState) def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents): """ Advance the progress bar. .. note:: This method will by default call `QApplication.processEvents` with `processEvents`. To suppress this behavior pass ``processEvents=None``. Args: value (int): progress value processEvents (`QEventLoop.ProcessEventsFlags` or `None`): process events flag """ self.progressBarSet(self.progressBarValue + value, processEvents) def progressBarFinished(self, processEvents=QEventLoop.AllEvents): """ Stop the widget's progress (i.e hide the progress bar). .. note:: This method will by default call `QApplication.processEvents` with `processEvents`. To suppress this behavior pass ``processEvents=None``. :param processEvents: Process events flag :type processEvents: `QEventLoop.ProcessEventsFlags` or `None` """ self.setWindowTitle(self.captionTitle) if self.__progressState != 0: self.__progressState = 0 self.processingStateChanged.emit(0) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents) @contextlib.contextmanager def progressBar(self, iterations=0): """ Context manager for progress bar. Using it ensures that the progress bar is removed at the end without needing the `finally` blocks. Usage: with self.progressBar(20) as progress: ... progress.advance() or with self.progressBar() as progress: ... progress.advance(0.15) or with self.progressBar(): ... self.progressBarSet(50) :param iterations: the number of iterations (optional) :type iterations: int """ progress_bar = gui.ProgressBar(self, iterations) try: yield progress_bar finally: progress_bar.finish()