def __init__(self, parent): super(PythonHighlighter, self).__init__(parent) self.rules = [] brush = QBrush(QtCore.Qt.darkGreen, QtCore.Qt.SolidPattern) builtin = QTextCharFormat() builtin.setForeground(brush) builtin.setFontWeight(QFont.Bold) builtins = dir(__builtin__) for word in builtins: pattern = QtCore.QRegExp("\\b{w}\\b".format(w=word)) rule = HighlightRule(pattern, builtin) self.rules.append(rule) brush = QBrush(QtCore.Qt.darkBlue, QtCore.Qt.SolidPattern) keyword = QTextCharFormat() keyword.setForeground(brush) keyword.setFontWeight(QFont.Bold) keywords = pythonkeyword.kwlist for word in keywords: pattern = QtCore.QRegExp("\\b{w}\\b".format(w=word)) rule = HighlightRule(pattern, keyword) self.rules.append(rule) brush = QBrush(QColor.fromRgb(255, 140, 0), QtCore.Qt.SolidPattern) pattern = QtCore.QRegExp("#[^\n]*") comment = QTextCharFormat() comment.setForeground(brush) comment.setFontWeight(QFont.Light) rule = HighlightRule(pattern, comment) self.rules.append(rule) self.setDocument(parent.document())
def resizeEvent(self, event): QPlainTextEdit.resizeEvent(self, event) contents = self.contentsRect() self.line_number_widget.setGeometry( QtCore.QRect(contents.left(), contents.top(), self.line_number_widget.digits_width(), contents.height())) # use the viewport width to determine the right edge. This allows for # the propper placement w/ and w/o the scrollbar right_pos = self.viewport().width() + self.line_number_widget.width() + 1\ - self.status_widget.sizeHint().width() self.status_widget.setGeometry( QtCore.QRect(right_pos, contents.top(), self.status_widget.sizeHint().width(), contents.height()))
def resizeEvent(self, event): super(QtTermEntryWidget, self).resizeEvent(event) cr = self.contentsRect() nr = QtCore.QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()) self._lineNumber.setGeometry(nr)
def set_position(self, center, radius, pixels_displacement): self.setRect( QtCore.QRectF( center[0] - radius + pixels_displacement[0], center[1] - radius + pixels_displacement[1], 2. * radius, 2. * radius))
def highlightBlock(self, text): for rule in self.rules: expression = QtCore.QRegExp(rule.pattern) index = expression.indexIn(text) while index >= 0: length = expression.matchedLength() self.setFormat(index, length, rule.format) index = expression.indexIn(text, index + length) self.setCurrentBlockState(0)
def set_position(self, center, radius, pixels_displacement): x = center[0] + pixels_displacement[0] y = center[1] + pixels_displacement[1] self.setRect(QtCore.QRectF(x, y, 2. * radius, 2. * radius)) import math self.setTransformOriginPoint(QPointF(x, y)) self.setRotation(math.degrees(self._rotation_in_radians))
def paintEvent(self, event): """ Paint the line numbers. """ with painter_on(self, antialias=True) as painter: if self.background_color is not None: painter.fillRect(event.rect(), self.background_color) cw = self.parent() pixels_per_block = self.height() / float(cw.blockCount()) for line in self.info_lines: painter.fillRect(QtCore.QRect(0, line * pixels_per_block, self.width(), 3), QtCore.Qt.green) for line in self.warn_lines: painter.fillRect(QtCore.QRect(0, line * pixels_per_block, self.width(), 3), QtCore.Qt.yellow) for line in self.error_lines: painter.fillRect(QtCore.QRect(0, line * pixels_per_block, self.width(), 3), QtCore.Qt.red)
def sizeHint(self): from pyvmmonitor_qt.qt.QtWidgets import QStyleOptionHeader from pyvmmonitor_qt.qt.QtWidgets import QStyle from pyvmmonitor_qt.qt.QtGui import QFontMetrics # Suggest a size that is 80 characters wide and 40 lines tall. style = self.style() opt = QStyleOptionHeader() font_metrics = QFontMetrics(self.document().defaultFont()) width = font_metrics.width(' ') * 80 width += self.line_number_widget.sizeHint().width() width += self.status_widget.sizeHint().width() width += style.pixelMetric(QStyle.PM_ScrollBarExtent, opt, self) height = font_metrics.height() * 40 return QtCore.QSize(width, height)
class OutputRedirect(QtCore.QObject): output = QtCore.Signal(str) def __init__(self, tee=True, parent=None): super(OutputRedirect, self).__init__(parent) self._handle = None self._tee = tee def __enter__(self): self._handle = sys.stdout sys.stdout = self return self def __exit__(self, type, value, traceback): sys.stdout = self._handle def write(self, msg): self.output.emit(msg) if self._tee: self._handle.write(msg)
def _set_expanded_nodes_tree(widget, nodes_tree=None, parent_index=None, data=QtCore.Qt.DisplayRole): ''' We have to find a tree subpath which matches the passed path (in nodes_tree) and expand it accordingly. :param NodesTree nodes_tree: ''' if not nodes_tree.children: return True model = widget.model() if parent_index is None: parent_index = QtCore.QModelIndex() row_count = model.rowCount(parent_index) found = 0 for row in compat.xrange(row_count): index = model.index(row, 0, parent_index) model_data = model.data(index, data) for node in nodes_tree.children: if node.data == model_data: # Ok, we have a match on this subtree (expand even if it means expanding # only partially). widget.setExpanded(index, True) # Ok, we have a possible match, let's go forward on this node if _set_expanded_nodes_tree(widget, nodes_tree=node, parent_index=index): found += 1 return found == len(nodes_tree.children)
def _get_expanded_nodes_tree(widget, parent_index=None, parent_node=None, data=QtCore.Qt.DisplayRole): ''' :return NodesTree: Returns a tree with the paths for the passed data. ''' model = widget.model() if parent_index is None: parent_index = QtCore.QModelIndex() row_count = model.rowCount(parent_index) if parent_node is None: parent_node = NodesTree() for row in compat.xrange(row_count): index = model.index(row, 0, parent_index) if not widget.isExpanded(index): continue node = parent_node.add_child(Node(model.data(index, data))) _get_expanded_nodes_tree(widget, parent_index=index, parent_node=node) return parent_node
def sizeHint(self): return QtCore.QSize(10, 0)
def sizeHint(self): return QtCore.QSize(self.min_width, 0)
def sizeHint(self): return QtCore.QSize(self.digits_width(), 0)
def iter_widget_captions_and_items( widget, parent_index=None, prefix='', cols=( 0, ), only_show_expanded=False, add_plus_to_new_level=True): from pyvmmonitor_qt.custom_close_tab_widget import CustomCloseTabWidget from pyvmmonitor_qt.qt.QtWidgets import QMdiArea from pyvmmonitor_qt.qt.QtWidgets import QTabWidget from pyvmmonitor_qt.qt.QtWidgets import QAbstractItemView if isinstance(widget, QMdiArea): for sub in widget.subWindowList(): yield sub.windowTitle(), sub elif isinstance(widget, (QTabWidget, CustomCloseTabWidget)): sz = widget.count() while sz > 0: sz -= 1 txt = widget.tabText(sz) yield txt, widget.widget(sz) elif isinstance(widget, QAbstractItemView): model = widget.model() if parent_index is None: parent_index = QtCore.QModelIndex() row_count = model.rowCount(parent_index) for row in compat.xrange(row_count): index = model.index(row, 0, parent_index) row_items = [] for col in cols: index_in_col = model.index(row, col, parent_index) data = model.data(index_in_col, QtCore.Qt.DisplayRole) if data is None: data = '' row_items.append( prefix + data) if len(cols) > 1: yield row_items, index else: yield row_items[0], index if only_show_expanded and hasattr(widget, 'isExpanded'): index = model.index(row, 0, parent_index) if not widget.isExpanded(index): continue for x in iter_widget_captions_and_items( widget, index, prefix + '+' if add_plus_to_new_level else prefix, cols, only_show_expanded=only_show_expanded, add_plus_to_new_level=add_plus_to_new_level): yield x else: raise AssertionError("Don't know how to list items for: %s" % (widget,))
class QtTermEntryWidget(QPlainTextEdit): traceback = QtCore.Signal(int) syntaxError = QtCore.Signal(int) def __init__(self, parent=None): super(QtTermEntryWidget, self).__init__(parent) self._termWidget = parent font = QFont("Monaco") font.setStyleHint(font.TypeWriter, font.PreferDefault) self.setFont(font) self._lineNumber = QtTermEntryLineNumberWidget(self) self._highlighter = PythonHighlighter(self) self.blockCountChanged.connect(self.updateLineNumberAreaWidth) self.updateRequest.connect(self.updateLineNumberArea) self.cursorPositionChanged.connect(self.highlightCurrentLine) self.updateLineNumberAreaWidth(0) self.highlightCurrentLine() self.executeAction = QAction('Execute Python', self) self.executeAction.setShortcut(QKeySequence("Ctrl+Return")) self.executeAction.triggered.connect(self.execute) self.addAction(self.executeAction) self.syntaxError.connect(self.displaySyntaxError) self.stdoutRedirect = OutputRedirect() self._locals = {} def displaySyntaxError(self, line): sel = QTextEdit.ExtraSelection() lineColor = QColor(QtCore.Qt.red).lighter(150) sel.format.setBackground(QBrush(lineColor, QtCore.Qt.SolidPattern)) sel.format.setProperty(QTextFormat.FullWidthSelection, True) sel.cursor = QTextCursor(self.document()) sel.cursor.movePosition(sel.cursor.NextBlock, QTextCursor.MoveAnchor, line - 1) sel.cursor.clearSelection() extraSelections = [sel] self.setExtraSelections(extraSelections) def execute(self): script = self.toPlainText() try: script_code = compile(script, '<interactive interpreter>', 'exec') except (SyntaxError) as e: self.syntaxError.emit(e.lineno) return with self.stdoutRedirect: try: exec(script_code, globals(), self._locals) except (StandardError) as e: # Which error should this be? type_, value_, traceback_ = sys.exc_info() tb = traceback.extract_tb(traceback_) index = self._termWidget.storeTraceback(tb) self.traceback.emit(index) def lineNumberAreaPaintEvent(self, event): painter = QPainter(self._lineNumber) painter.fillRect(event.rect(), QColor.fromRgb(200, 200, 200)) block = self.firstVisibleBlock() blockNumber = block.blockNumber() top = int( self.blockBoundingGeometry(block).translated( self.contentOffset()).top()) bottom = top + int(self.blockBoundingRect(block).height()) while block.isValid() and top <= event.rect().bottom(): if block.isVisible() and bottom >= event.rect().top(): number = str(blockNumber + 1) painter.setPen(QtCore.Qt.black) painter.drawText(0, top, self._lineNumber.width(), self.fontMetrics().height(), QtCore.Qt.AlignRight, number) block = block.next() top = bottom bottom = top + int(self.blockBoundingRect(block).height()) blockNumber += 1 def highlightCurrentLine(self): sel = QTextEdit.ExtraSelection() lineColor = QColor(QtCore.Qt.gray).lighter(150) sel.format.setBackground(QBrush(lineColor, QtCore.Qt.DiagCrossPattern)) sel.format.setProperty(QTextFormat.FullWidthSelection, True) sel.cursor = self.textCursor() sel.cursor.clearSelection() extraSelections = [sel] self.setExtraSelections(extraSelections) def updateLineNumberArea(self, area, num): if (num): self._lineNumber.scroll(0, num) else: self._lineNumber.update(0, area.y(), self._lineNumber.width(), area.height()) if area.contains(self.viewport().rect()): self.updateLineNumberAreaWidth(0) def updateLineNumberAreaWidth(self, num): self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) def lineNumberAreaWidth(self): digits = math.floor(math.log10(self.blockCount())) + 1 space = 3 + self.fontMetrics().width('9') * digits return space def resizeEvent(self, event): super(QtTermEntryWidget, self).resizeEvent(event) cr = self.contentsRect() nr = QtCore.QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()) self._lineNumber.setGeometry(nr)