def paint(self, painter, point): painter.save() size = QSize(self.width(), self.height()) painter.setBrush(self.bg_color) painter.drawRoundedRect(QRect(point, size), BLOCK_RADIUS, BLOCK_RADIUS) # Рисуем столбцы p = QPoint(point) + QPoint(0, BLOCK_RADIUS) for c in self.columns: p += QPoint(BLOCK_RADIUS, 0) c.paint(painter, p) p += QPoint(c.width(), 0) # Рисуем левые порты if len(self.left_pins): p = QPoint(point) p += QPoint(0, size.height() / (len(self.left_pins) + 1)) for lp in self.left_pins: lp.paint(painter, p) p += QPoint(0, size.height() / (len(self.left_pins) + 1)) # Рисуем правые порты if len(self.right_pins): p = QPoint(point) p += QPoint(size.width(), size.height() / (len(self.right_pins) + 1)) for rp in self.right_pins: rp.paint(painter, p) p += QPoint(0, size.height() / (len(self.right_pins) + 1)) painter.restore()
def save_image_to(self, path): TOP_MARGIN = 50 LEFT_MARGIN = 50 # Determine the size of the entire graph graph_size = self._graph_size() image_size = QSize(graph_size.width() + LEFT_MARGIN * 2, graph_size.height() + TOP_MARGIN * 2 ) image = QImage(image_size, QImage.Format_ARGB32) image.fill(Qt.white) # white background painter = QPainter(image) painter.translate(TOP_MARGIN, LEFT_MARGIN) painter.setRenderHint(QPainter.TextAntialiasing) self._paint(painter, QPoint(-TOP_MARGIN, -LEFT_MARGIN), QPoint(image_size.width(), image_size.height()) ) painter.end() image.save(path)
def read_settings(self): settings = get_settings() screen = QApplication.desktop().screenGeometry() h = min(screen.height() * 5 / 6., 900) size = QSize(min(screen.width() * 5 / 6., 1200), h) pos = settings.value("pos", None) savesize = settings.value("size", size) if savesize.width() > screen.width(): savesize.setWidth(size.width()) if savesize.height() > screen.height(): savesize.setHeight(size.height()) self.resize(savesize) if ((pos is None or pos.x() + savesize.width() > screen.width() or pos.y() + savesize.height() > screen.height())): self.move(screen.center() - self.rect().center()) else: self.move(pos)
class MarkersPanel(Panel): """ Panel used to draw a collection of marker. A marker is 16x16 icon placed at a specific line number. A marker is added/removed when the user click on the the widget or manually (using addMarker). Actually if there is no marker where the user clicked, the markerAddRequested signal is emitted as we don't known what kind of marker we must add. When a marker is removed by the user, the markerRemoved signal is emitted. .. note:: The markers position is updated whenever a line is added/removed. .. note:: The markers list is cleared when a new text is set on the text edit (see QCodeEdit.newTextSet signal) .. note:: If a marker goes out of documents (line number <= 0) the markerOutOfDoc is emitted. """ #: Stylesheet # QSS = """QToolTip { # background-color: %(back)s; # color: %(color)s; # border: 1px solid %(color)s; # padding: 2px; # opacity: 220; # } # """ #: Signal emitted with the line number where the marker must be added addMarkerRequested = Signal(Panel, int) #: Signal emitted when a marker is removed by the user. markerRemoved = Signal(Panel, Marker) #: Signal emitted when a marker is out of the document. This usually # happen when the user delete lines from the beginning of the doc. markerOutOfDoc = Signal(Panel, Marker) #: Signal emitted before clearing the markers when a new text is set to give # a chance to the user to save the markers list. clearingMarkers = Signal(Panel) def __init__(self, name, markersReadOnly=False, parent=None): Panel.__init__( self, name, "Display markers foreach line", parent) self.markers = [] #: prevent user from adding/removing markers with mouse click self.markersReadOnly = markersReadOnly self.setMouseTracking(True) self.timer = QTimer() self._tooltipPos = -1 self._prev_line = -1 self.logger = logging.getLogger( __name__ + "." + self.__class__.__name__) def addMarker(self, marker): """ Adds a marker to be displayed """ self.markers.append(marker) self.update() def removeMarker(self, marker): """ Removes a marker """ self.markers.remove(marker) self.update() def clearMarkers(self): """ Clears the markers list """ self.clearingMarkers.emit(self) self.markers[:] = [] self.update() def install(self, editor): Panel.install(self, editor) self.bc = self.editor.codeEdit.blockCount() self.__updateCursorPos() def _onStateChanged(self, state): Panel._onStateChanged(self, state) if state is True: self.editor.codeEdit.visibleBlocksChanged.connect(self.update) self.editor.codeEdit.blockCountChanged.connect(self.__onBlockCountChanged) self.editor.codeEdit.newTextSet.connect(self.__onNewTextSet) self.editor.codeEdit.keyPressed.connect(self.__updateCursorPos) else: self.editor.codeEdit.visibleBlocksChanged.disconnect(self.update) self.editor.codeEdit.blockCountChanged.disconnect(self.__onBlockCountChanged) self.editor.codeEdit.newTextSet.disconnect(self.__onNewTextSet) self.editor.codeEdit.keyPressed.disconnect(self.__updateCursorPos) def _onStyleChanged(self): """ Updates stylesheet and brushes. """ fm = QFontMetricsF(self.editor.codeEdit.font()) self.size_hint = QSize(fm.height(), fm.height()) style = self.currentStyle self.back_brush = QBrush(QColor(style.panelsBackgroundColor)) self.active_line_brush = QBrush(QColor(style.activeLineColor)) self.separator_pen = QPen(QColor(style.panelSeparatorColor)) # qss = self.QSS % {"back": style.activeLineColor, # "color": style.tokenColor(Text)} # self.setStyleSheet(qss) self.updateGeometry() def sizeHint(self): """ Returns the widget size hint (based on the editor font size) """ fm = QFontMetricsF(self.editor.codeEdit.font()) self.size_hint = QSize(fm.height(), fm.height()) if self.size_hint.width() > 16: self.size_hint.setWidth(16) return self.size_hint def __onNewTextSet(self): """ Clears markers when a new text is set """ self.clearMarkers() def __getMarkerForLine(self, l): """ Returns the marker for the line l if any, else return None """ for m in self.markers: if m.position == l: return m return None def __updateCursorPos(self): """ Updates cursor pos variables """ self.tcPos = self.editor.codeEdit.textCursor().blockNumber() + 1 self.tcPosInBlock = self.editor.codeEdit.textCursor().positionInBlock() def __onBlockCountChanged(self, num): """ Handles lines added/removed events """ # a l has beeen inserted or removed tcPos = self.editor.codeEdit.textCursor().blockNumber() + 1 tcPosInBlock = self.editor.codeEdit.textCursor().positionInBlock() bc = self.bc if bc < num: self.onLinesAdded(num - bc, tcPos, tcPosInBlock) else: self.onLinesRemoved(bc - num, tcPos, tcPosInBlock) self.tcPosInBlock = self.tcPosInBlock self.bc = num def onLinesAdded(self, nbLines, tcPos, tcPosInBlock): """ Offsets markers positions with the number of line added """ if self.tcPosInBlock > 0: self.tcPos += 1 # offset each l after the tcPos by nbLines for marker in self.markers: if marker.position >= self.tcPos: marker.position += nbLines self.tcPos = tcPos self.tcPosInBlock = tcPosInBlock self.update() def onLinesRemoved(self, nbLines, tcPos, tcPosInBlock): """ Offsets markers positions with the number of line removed """ for marker in self.markers: if marker.position >= self.tcPos: marker.position -= nbLines if marker.position < 1: self.markerOutOfDoc.emit(self, marker) self.tcPos = tcPos self.tcPosInBlock = tcPosInBlock self.update() def paintEvent(self, event): """ Paints the widget """ if self.enabled is False: return Panel.paintEvent(self, event) painter = QPainter(self) painter.fillRect(event.rect(), self.back_brush) for vb in self.editor.codeEdit.visible_blocks: l = vb.row marker = self.__getMarkerForLine(l) if marker: if marker.icon is not None: r = QRect() r.setX(vb.rect.x()) r.setY(vb.rect.y()) r.setWidth(self.size_hint.width()) r.setHeight(self.size_hint.height()) marker.icon.paint(painter, r) return def leaveEvent(self, event): """ Resets prev line to -1 when we leave otherwise the tooltip won't be shown next time we hover the marker """ if self.enabled is False: return self._prev_line = -1 def mouseMoveEvent(self, event): """ Shows a tooltip """ if self.enabled is False: return pos = event.pos() y = pos.y() for vb in self.editor.codeEdit.visible_blocks: top = vb.top height = vb.height if top < y < top + height: marker = self.__getMarkerForLine(vb.row) if (marker is not None and marker.tooltip is not None and marker.tooltip != ""): if self._prev_line != vb.row: self.timer.stop() self._tooltip = marker.tooltip centerX = self.size_hint.width() centerY = vb.rect.y() self._tooltipPos = QPoint(centerX, centerY) self._prev_line = vb.row self.timer.singleShot(1500, self.displayTooltip) return def mouseReleaseEvent(self, event): """ Adds/Remove markers on click """ if self.enabled is False: return if self.markersReadOnly is True: return pos = event.pos() y = pos.y() for vb in self.editor.codeEdit.visible_blocks: top = vb.top height = vb.height if top < y < top + height: marker = self.__getMarkerForLine(vb.row) if marker is not None: self.removeMarker(marker) self.markerRemoved.emit(self, marker) self.logger.debug("Marker removed") else: self.logger.debug("Marker add requested (l: %d)" % vb.row) self.addMarkerRequested.emit(self, vb.row) def displayTooltip(self): """ Display the tooltip """ QToolTip.showText(self.mapToGlobal(self._tooltipPos), self._tooltip, self)
class FoldPanel(Panel): """ This Panel display folding indicators and manage folding/unfolding a text The panel also handles line added/removed and update the indicators position automatically. .. note:: It does not parse the code to put fold indicators, this is the task of a code folder mode. Instead it provides an easy way for other modes to put fold indicators on the left margin. """ #: Panel identifier IDENTIFIER = "Folding" DESCRIPTION = "Display code folding indicators" def __init__(self, parent=None): Panel.__init__( self, self.IDENTIFIER, self.DESCRIPTION, parent) self.fold_indicators = [] self.setMouseTracking(True) self.logger = logging.getLogger( __name__ + "." + self.__class__.__name__) def addIndicator(self, start, end): """ Adds a fold indicator :param start: Start line (1 based) :param end: End line """ self.fold_indicators.append(FoldIndicator(start, end)) self.update() def removeIndicator(self, indicator): """ Remove a fold indicator :param indicator: Indicator to remove """ self.fold_indicators.remove(indicator) self.update() def clearIndicators(self): """ Remove all indicators """ self.fold_indicators[:] = [] self.update() def install(self, editor): """ Install the Panel on the editor """ Panel.install(self, editor) self.bc = self.editor.codeEdit.blockCount() self.__updateCursorPos() def _onStateChanged(self, state): Panel._onStateChanged(self, state) if state is True: self.editor.codeEdit.visibleBlocksChanged.connect(self.update) self.editor.codeEdit.blockCountChanged.connect(self.__onBlockCountChanged) self.editor.codeEdit.newTextSet.connect(self.__onNewTextSet) self.editor.codeEdit.keyPressed.connect(self.__updateCursorPos) else: self.editor.codeEdit.visibleBlocksChanged.disconnect(self.update) self.editor.codeEdit.blockCountChanged.disconnect(self.__onBlockCountChanged) self.editor.codeEdit.newTextSet.disconnect(self.__onNewTextSet) self.editor.codeEdit.keyPressed.disconnect(self.__updateCursorPos) def _onStyleChanged(self): """ Updates brushes and pens """ style = self.currentStyle self.font = QFont(self.currentStyle.fontName, 7) self.font.setBold(True) fm = QFontMetricsF(self.editor.codeEdit.font()) self.size_hint = QSize(16, 16) self.back_brush = QBrush(QColor(style.panelsBackgroundColor)) self.active_line_brush = QBrush(QColor(style.activeLineColor)) self.separator_pen = QPen(QColor(style.panelSeparatorColor)) self.normal_pen = QPen(QColor(style.lineNbrColor)) self.highlight_pen = QPen(QColor(style.tokenColor(Text))) self.repaint() def sizeHint(self): """ Returns a fixed size hint (16x16) """ self.size_hint = QSize(16, 16) return self.size_hint def __onNewTextSet(self): self.clearIndicators() def getIndicatorForLine(self, line): """ Returns the fold indicator whose start position equals the line :param line: Line nbr of the start position of the indicator to get. :return: FoldIndicator or None """ for m in self.fold_indicators: if m.start == line: return m return None def __updateCursorPos(self): """ Update tcPos and tcPosInBlock :return: """ self.tcPos = self.editor.codeEdit.textCursor().blockNumber() + 1 self.tcPosInBlock = self.editor.codeEdit.textCursor().positionInBlock() def __onBlockCountChanged(self, num=-1): """ Handles line added/removed event """ # a line has been inserted or removed tcPos = self.editor.codeEdit.textCursor().blockNumber() + 1 tcPosInBlock = self.editor.codeEdit.textCursor().positionInBlock() bc = self.bc if bc < num: self.__onLinesAdded(num - bc, tcPos, tcPosInBlock) else: self.__onLinesRemoved(bc - num, tcPos, tcPosInBlock) self.tcPosInBlock = self.tcPosInBlock self.bc = num def __onLinesAdded(self, nbLines, tcPos, tcPosInBlock): """ Offsets markers positions with the number of line added """ if self.tcPosInBlock > 0: self.tcPos += 1 # offset each line after the tcPos by nbLines for marker in self.fold_indicators: if marker.start >= self.tcPos: marker.start += nbLines if marker.end >= self.tcPos: marker.end += nbLines self.tcPos = tcPos self.tcPosInBlock = tcPosInBlock self.update() def __onLinesRemoved(self, nbLines, tcPos, tcPosInBlock): """ Offsets markers positions with the number of line removed """ for marker in self.fold_indicators: if marker.start >= self.tcPos: marker.start -= nbLines if marker.start < 1: self.removeIndicator(marker) if marker.end >= self.tcPos: marker.end -= nbLines if marker.end == marker.start: self.removeIndicator(marker) self.tcPos = tcPos self.tcPosInBlock = tcPosInBlock self.update() def paintEvent(self, event): """ Paints the widget """ if self.enabled is False: return Panel.paintEvent(self, event) painter = QPainter(self) painter.fillRect(event.rect(), self.back_brush) for vb in self.editor.codeEdit.visible_blocks: line = vb.row # paint marker for line marker = self.getIndicatorForLine(line) if marker is None: continue # use the normal pen to draw the fold indicator drawLines = False pen = self.normal_pen if marker.hover is True: pen = self.highlight_pen drawLines = True painter.setPen(pen) # get the text to draw txt = '-' if marker.folded is True: drawLines = False txt = '+' offset = 4 h = self.size_hint.height() fm = QFontMetricsF(self.editor.codeEdit.font()) hoffset = (fm.height() - h) / 2.0 r = QRect(vb.rect.x(), vb.rect.y() + hoffset, self.size_hint.width(), self.size_hint.height()) painter.setFont(self.font) painter.drawText(r, Qt.AlignVCenter | Qt.AlignHCenter, txt) w = self.size_hint.width() - 2 * offset h = self.size_hint.width() - 2 * offset hoffset = (fm.height() - h) / 2.0 r.setX(vb.rect.x() + offset) r.setY(vb.rect.y() + hoffset) r.setWidth(w) r.setHeight(h) painter.drawRect(r) if drawLines is True: top = (vb.rect.x() + self.size_hint.width() / 2.0, vb.rect.y() + hoffset + offset * 2) delta = ((marker.end - marker.start) * vb.height) # - (vb.rect.height() / 2.0) bottom = (top[0], top[1] + delta) painter.drawLine(top[0], top[1], bottom[0], bottom[1]) painter.drawLine(bottom[0], bottom[1], bottom[0] + self.size_hint.width() / 2.0, bottom[1]) return def leaveEvent(self, event): """ Clears indicator hover states and repaint """ for m in self.fold_indicators: m.hover = False self.repaint() def mouseMoveEvent(self, event): """ Detects indicator hover states """ if self.enabled is False: return pos = event.pos() y = pos.y() repaint = False for m in self.fold_indicators: if m.hover is True: m.hover = False repaint = True for vb in self.editor.codeEdit.visible_blocks: top = vb.top height = vb.height if top < y < top + height: marker = self.getIndicatorForLine(vb.row) if marker is not None: # mark it as hover and repaint marker.hover = True repaint = True if repaint is True: self.repaint() def mouseReleaseEvent(self, event): """ Folds/Unfolds code blocks """ if self.enabled is False: return pos = event.pos() y = pos.y() for vb in self.editor.codeEdit.visible_blocks: top = vb.top height = vb.height if top < y < top + height: marker = self.getIndicatorForLine(vb.row) if marker is not None: marker.folded = not marker.folded self.editor.codeEdit.fold(marker.start - 1, marker.end, marker.folded) self.repaint()