def text(self, text: str, pos: QRectF, color: QColor=None, size: int=20, shaded: int=0, bold: bool=False,shrinkToFit=10) -> None: if not isinstance(text,str): text = "{}".format(text) self._font.setPointSize(size) if bold: self._font.setWeight(QFont.Black) else: self._font.setWeight(QFont.Bold) self._painter.setFont(self._font) fm = QFontMetrics(self._font) if pos.width() == 0: pos.setWidth(fm.width(text)) if pos.height() == 0: pos.setHeight(fm.height()) if size > shrinkToFit: # if fm.width(text) > pos.width() or fm.height() > pos.height()+2: self.text(text,pos,color,size-1,shaded,bold,shrinkToFit) return if shaded: diff = size//4 if isinstance(shaded,bool) and shaded == True else shaded self._painter.setPen(self._fgs) pos2 = pos.translated(diff, diff) self._painter.drawText(pos2, Qt.AlignCenter, text) p = QPen(color if color is not None else self._fg) self._painter.setPen(p) self._painter.drawText(pos, Qt.AlignCenter, text)
def initUI(self): # load main ui window self.uic = uic.loadUi('main.ui', self) gr_rect = QRectF(0, 0, self.rect().width(), 20) self.scene = QGraphicsScene(gr_rect) self.scene.setBackgroundBrush(Qt.black) self.anim = NS_Animate(self.scene, gr_rect.width(), gr_rect.height(), QtGui.QColor.fromRgb(0, 32, 49)) self.horizontalLayout_anim.addWidget(self.anim.window) # self.anim.start() self.anim.window.resize(gr_rect.width(), gr_rect.height())
def validateRect(self, r: QRectF, resizeDirections): left = r.left() top = r.top() right = left + r.width() bottom = top + r.height() # The qBound() is used for enforcement of the minimum and maximum sizes. # It's derived after solving the following inequalities (example is for # left-resizing): # minWidth <= newWidth <= maxWidth # minWidth <= right-newLeft <= maxWidth # minWidth-right <= -newLeft <= maxWidth-right # right-minWidth >= newLeft >= right-maxWidth # Ditto for the other 3 directions. def qBound(minVal, current, maxVal): return max(min(current, maxVal), minVal) if resizeDirections.horizontal == ResizeDirectionHorizontal.Left: left = qBound(right - self.maximumSize.width(), left, right - self.minimumSize.width()) elif resizeDirections.horizontal == ResizeDirectionHorizontal.Right: right = qBound(self.minimumSize.width() + left, right, self.maximumSize.width() + left) if resizeDirections.vertical == ResizeDirectionVertical.Top: top = qBound(bottom - self.maximumSize.height(), top, bottom - self.minimumSize.height()) elif resizeDirections.vertical == ResizeDirectionVertical.Bottom: bottom = qBound(self.minimumSize.height() + top, bottom, self.maximumSize.height() + top) return QRectF(left, top, right - left, bottom - top)
def updateSelection(self, event): rect = QRectF(self.mStart, event.scenePos()).normalized() # Make sure the rect has some contents, otherwise intersects returns False rect.setWidth(max(1.0, rect.width())) rect.setHeight(max(1.0, rect.height())) oldSelection = self.mapScene().selectedObjectItems() if (oldSelection.isEmpty()): # Allow selecting some map objects only when there aren't any selected selectedItems = QSet() for item in self.mapScene().items(rect, Qt.IntersectsItemShape, Qt.DescendingOrder, viewTransform(event)): if type(item) == MapObjectItem: selectedItems.insert(item) newSelection = QSet() if (event.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)): newSelection = oldSelection | selectedItems else: newSelection = selectedItems self.mapScene().setSelectedObjectItems(newSelection) self.updateHandles() else: # Update the selected handles selectedHandles = QSet() for item in self.mapScene().items(rect, Qt.IntersectsItemShape, Qt.DescendingOrder, viewTransform(event)): if type(item) == PointHandle: selectedHandles.insert(item) if (event.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)): self.setSelectedHandles(self.mSelectedHandles | selectedHandles) else: self.setSelectedHandles(selectedHandles)
class BoxOutline(QGraphicsItem): def __init__(self, bg_color): super(BoxOutline, self).__init__() self.rect = QRectF() self.bg_color = bg_color def shape(self): path = QPainterPath() path.addRect(self.rect) return path def boundingRect(self): return self.rect def paint(self, painter, option, widget=None): p = QPen(Qt.black) if self.isSelected(): p.setWidth(5) painter.setPen(p) else: p.setWidth(1) painter.setPen(p) painter.setBrush(self.bg_color) r = self.rect.height() / 8.0 painter.drawRoundedRect(self.rect, r, r)
def updateSceneRect(self): #Is called when there is a change in the scene #Update scene size to fit the current layout of the graph if not self.lockScene: rect = self.itemsBoundingRect() rect = QRectF(rect.x() - 50, rect.y() - 50, rect.width() + 100, rect.height() + 100) self.setSceneRect(rect) else: self.lockScene = False
def setNetBoxDimensions(self, rect: QRectF): """ Set net box dimensions The net box is the rectangle that compose the network drawn to show the occupy matrix """ self.netRectHeight = rect.height() / self.netRows self.netRectWidth = rect.width() / self.netCols self.left = rect.left() self.top = rect.top()
def wait(self): array_data = self._arrayreq.wait() rectf = self.rectf if array_data.handedness_switched: # array_data should be of type slicingtools.ProjectedArray rectf = QRectF(rectf.height(), rectf.width()) from PyQt5.QtWidgets import QPainter img = QImage(QSize(self.rectf.width(), self.rectf.height()), QImage.Format_ARGB32_Premultiplied) img.fill(0xFFFFFFFF) p = QPainter(img) p.drawImage(0, 0, img) DummyItem(self.rectf).paint(p, None) return img
def activate_tracker(self, bounds: QtCore.QRectF): """ The main map tells us how large its view is (in terms of the game map) and where it is currently. :param bounds: """ # scale to scene width and height w = self.scene.width() h = self.scene.height() bounds = QtCore.QRectF(bounds.x() * w, bounds.y() * h, bounds.width() * w, bounds.height() * h) # set bounds of tracker and show self.tracker.setRect(bounds) self.tracker.show()
def updateSelection(self, pos, modifiers): rect = QRectF(self.mStart, pos).normalized() # Make sure the rect has some contents, otherwise intersects returns False rect.setWidth(max(1.0, rect.width())) rect.setHeight(max(1.0, rect.height())) selectedItems = QSet() for item in self.mapScene().items(rect): if type(item) == MapObjectItem: selectedItems.insert(item) if (modifiers & (Qt.ControlModifier | Qt.ShiftModifier)): selectedItems |= self.mapScene().selectedObjectItems() else: self.setMode(Mode.Resize) self.mapScene().setSelectedObjectItems(selectedItems)
def data2scene(self, data2scene): self._data2scene = data2scene self.scene2data, isInvertible = data2scene.inverted() assert isInvertible for patchNr in range(self._patchAccessor.patchCount): # the patch accessor uses the data coordinate system. # because the patch is drawn on the screen, its holds coordinates # corresponding to Qt's QGraphicsScene's system, which need to be # converted to scene coordinates # the image rectangle includes an overlap margin imageRectF = data2scene.mapRect(self._patchAccessor.patchRectF(patchNr, self.overlap)) # the patch rectangle has per default no overlap patchRectF = data2scene.mapRect(self._patchAccessor.patchRectF(patchNr, 0)) # add a little overlap when the overlap_draw setting is # activated if self._overlap_draw != 0: patchRectF = QRectF( patchRectF.x() - self._overlap_draw, patchRectF.y() - self._overlap_draw, patchRectF.width() + 2 * self._overlap_draw, patchRectF.height() + 2 * self._overlap_draw, ) patchRect = QRect( round(patchRectF.x()), round(patchRectF.y()), round(patchRectF.width()), round(patchRectF.height()) ) # the image rectangles of neighboring patches can overlap # slightly, to account for inaccuracies in sub-pixel # rendering of many ImagePatch objects imageRect = QRect( round(imageRectF.x()), round(imageRectF.y()), round(imageRectF.width()), round(imageRectF.height()) ) self.imageRectFs[patchNr] = imageRectF self.dataRectFs[patchNr] = imageRectF self.tileRectFs[patchNr] = patchRectF self.imageRects[patchNr] = imageRect self.tileRects[patchNr] = patchRect
def setSelected(self, value): """ Sets *value*-th glyph as the selected glyph, or none if *value* is None. *value* should be less than the number of glyphRecords present in the widget. """ self._selected = value if self._selected is not None and self._glyphRecordsRects is not None: scrollArea = self._scrollArea if scrollArea is not None: rect = None for r, indexRecord in self._glyphRecordsRects.items(): if indexRecord == self._selected: rect = QRectF(*r) break if rect is not None: center = rect.center() scrollArea.ensureVisible( center.x(), center.y(), .6 * rect.width(), .6 * rect.height()) self.update()
class DummyRasterRequest(object): """ For stupid tests. Uses DummyItem, but rasterizes it to turn it into a QImage. """ def __init__(self, arrayreq, rect): self.rectf = QRectF(rect) self._arrayreq = arrayreq def wait(self): array_data = self._arrayreq.wait() rectf = self.rectf if array_data.handedness_switched: # array_data should be of type slicingtools.ProjectedArray rectf = QRectF(rectf.height(), rectf.width()) from PyQt5.QtWidgets import QPainter img = QImage(QSize(self.rectf.width(), self.rectf.height()), QImage.Format_ARGB32_Premultiplied) img.fill(0xffffffff) p = QPainter(img) p.drawImage(0, 0, img) DummyItem(self.rectf).paint(p, None) return img
def itemChange(self, change, value): """ Check, whether the item is out of the scenes bounds """ if change == QGraphicsItem.ItemPositionChange: # get the new position newPos = value oldRect = self.sceneBoundingRect() newRect = QRectF(newPos.x(), newPos.y(), oldRect.width(), oldRect.height()) sceneRect = QRectF(self.scene().sceneRect()) # check if the left or right side of the new rect is outside the scenes bounds -> move it back inside if newRect.left() < sceneRect.left(): newPos.setX(sceneRect.left()) elif newRect.left() + newRect.width() > sceneRect.right(): newPos.setX(sceneRect.right() - newRect.width()) # check if the top or bottom side of the new rect is outside the scenes bounds -> move it back inside if newRect.bottom() > sceneRect.bottom(): newPos.setY(sceneRect.bottom() - newRect.height()) elif newRect.top() < sceneRect.top(): newPos.setY(sceneRect.top()) return newPos return super(NodeItem, self).itemChange(change, value)
class InCheckGraphicsItem(QGraphicsItem): def __init__(self, brush, rect): super().__init__() self.rect = QRectF(rect) self.brush = brush def boundingRect(self): return self.rect def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget_widget=None): QPainter.setClipRect(self.rect) center = QPointF(self.rect.width() / 2, self.rect.height() / 2) focalPoint = center grad = QRadialGradient(center, float(self.rect.width() * 0.58929), focalPoint) col = QColor(bConfig['checkColor']) grad.setColorAt(0, col) grad.setColorAt(1, self.brush.color()) col = QColor(bConfig['checkColor']) col.setAlphaF(float(bConfig['effectsAlpha'])) QPainter.setBrush(QBrush(col)) QPainter.setPen(QPen(Qt.NoPen)) QPainter.fillRect(self.rect, grad)
def setSelected(self, value): """ Sets *value*-th glyph as the selected glyph, or none if *value* is None. *value* should be less than the number of glyphRecords present in the widget. """ self._selected = value if self._selected is not None and self._glyphRecordsRects is not None: scrollArea = self._scrollArea if scrollArea is not None: rect = None for r, indexRecord in self._glyphRecordsRects.items(): if indexRecord == self._selected: rect = QRectF(*r) break if rect is not None: center = rect.center() scrollArea.ensureVisible(center.x(), center.y(), 0.6 * rect.width(), 0.6 * rect.height()) self.update()
def createRoundRectPath(rectF: QtCore.QRectF, r1: float, r2: float, r3: float, r4: float) -> QtGui.QPainterPath: """ create a rounded rectangle painter path :param r1: top left radius :param r2: top right radius :param r3: bottom right radius :param r4: bottom left radius """ r1 *= 2 r2 *= 2 r3 *= 2 r4 *= 2 path = QtGui.QPainterPath() width = rectF.width() height = rectF.height() x = rectF.x() y = rectF.y() path.moveTo(x + width - r2, y) path.lineTo(x + r1, y) path.arcTo(x, y, r1, r1, 90.0, 90.0) path.lineTo(x, y + height - r4) path.arcTo(x, y + height - r4, r4, r4, 180.0, 90.0) path.lineTo(x + width - r3, y + height) path.arcTo(x + width - r3, y + height - r3, r3, r3, 270.0, 90.0) path.lineTo(x + width, y + r2) path.arcTo(x + width - r2, y, r2, r2, 0.0, 90.0) path.closeSubpath() return path
class DummyRasterRequest(object): """ For stupid tests. Uses DummyItem, but rasterizes it to turn it into a QImage. """ def __init__(self, arrayreq, rect): self.rectf = QRectF(rect) self._arrayreq = arrayreq def wait(self): array_data = self._arrayreq.wait() rectf = self.rectf if array_data.handedness_switched: # array_data should be of type slicingtools.ProjectedArray rectf = QRectF(rectf.height(), rectf.width()) from PyQt5.QtWidgets import QPainter img = QImage(QSize(self.rectf.width(), self.rectf.height()), QImage.Format_ARGB32_Premultiplied) img.fill(0xFFFFFFFF) p = QPainter(img) p.drawImage(0, 0, img) DummyItem(self.rectf).paint(p, None) return img
def paintEvent(self, e: QPaintEvent): contRect = self.contentsRect() handleRadius = round(0.24 * contRect.height()) p = QPainter(self) p.setRenderHint(QPainter.Antialiasing) p.setPen(self._transparent_pen) barRect = QRectF(0, 0, contRect.width() - handleRadius, 0.40 * contRect.height()) barRect.moveCenter(contRect.center()) rounding = barRect.height() / 2 # the handle will move along this line trailLength = contRect.width() - 2 * handleRadius xPos = contRect.x( ) + handleRadius + trailLength * self._handle_position if self.isChecked(): p.setBrush(self._bar_checked_brush) p.drawRoundedRect(barRect, rounding, rounding) p.setBrush(self._handle_checked_brush) else: p.setBrush(self._bar_brush) p.drawRoundedRect(barRect, rounding, rounding) p.setPen(self._light_grey_pen) p.setBrush(self._handle_brush) p.drawEllipse(QPointF(xPos, barRect.center().y()), handleRadius, handleRadius) p.end()
def paint_text_rect(self, painter): p = painter.pen() font = QFont() font.setPointSizeF(self.dw()*3) painter.setFont(font) painter.setBrush(self.brush()) fm = QFontMetrics(font) w = fm.width(self.parentItem().name) h = fm.height() br = self.rect.bottomRight() text_rect = QRectF(QPointF(br.x()-w, br.y()-h), br) if text_rect.width() < self.boundingRect().width()/2 and\ text_rect.height() < self.boundingRect().height()/2: painter.drawRect(text_rect) pen = self.pen() pen.setColor(QColor("black")) painter.setPen(pen) painter.drawText(text_rect, Qt.AlignCenter, self.parentItem().name) painter.setPen(p)
class InstantPrintTool(QgsMapTool, InstantPrintDialog): def __init__(self, iface, populateCompositionFz=None): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface projectInstance = QgsProject.instance() self.projectLayoutManager = projectInstance.layoutManager() self.rubberband = None self.oldrubberband = None self.pressPos = None self.printer = QPrinter() self.mapitem = None self.populateCompositionFz = populateCompositionFz self.dialog = InstantPrintDialog(self.iface.mainWindow()) self.dialogui = Ui_InstantPrintDialog() self.dialogui.setupUi(self.dialog) self.dialogui.addScale.setIcon(QIcon(":/images/themes/default/mActionAdd.svg")) self.dialogui.deleteScale.setIcon(QIcon(":/images/themes/default/symbologyRemove.svg")) self.dialog.hidden.connect(self.__onDialogHidden) self.exportButton = self.dialogui.buttonBox.addButton(self.tr("Export"), QDialogButtonBox.ActionRole) self.printButton = self.dialogui.buttonBox.addButton(self.tr("Print"), QDialogButtonBox.ActionRole) self.helpButton = self.dialogui.buttonBox.addButton(self.tr("Help"), QDialogButtonBox.HelpRole) self.dialogui.comboBox_fileformat.addItem("PDF", self.tr("PDF Document (*.pdf);;")) self.dialogui.comboBox_fileformat.addItem("JPG", self.tr("JPG Image (*.jpg);;")) self.dialogui.comboBox_fileformat.addItem("BMP", self.tr("BMP Image (*.bmp);;")) self.dialogui.comboBox_fileformat.addItem("PNG", self.tr("PNG Image (*.png);;")) self.iface.layoutDesignerOpened.connect(lambda view: self.__reloadLayouts()) self.iface.layoutDesignerWillBeClosed.connect(self.__reloadLayouts) self.dialogui.comboBox_layouts.currentIndexChanged.connect(self.__selectLayout) self.dialogui.comboBox_scale.lineEdit().textChanged.connect(self.__changeScale) self.dialogui.comboBox_scale.scaleChanged.connect(self.__changeScale) self.exportButton.clicked.connect(self.__export) self.printButton.clicked.connect(self.__print) self.helpButton.clicked.connect(self.__help) self.dialogui.buttonBox.button(QDialogButtonBox.Close).clicked.connect(lambda: self.dialog.hide()) self.dialogui.addScale.clicked.connect(self.add_new_scale) self.dialogui.deleteScale.clicked.connect(self.remove_scale) self.deactivated.connect(self.__cleanup) self.setCursor(Qt.OpenHandCursor) settings = QSettings() if settings.value("instantprint/geometry") is not None: self.dialog.restoreGeometry(settings.value("instantprint/geometry")) if settings.value("instantprint/scales") is not None: for scale in settings.value("instantprint/scales").split(";"): if scale: self.retrieve_scales(scale) self.check_scales() def __onDialogHidden(self): self.setEnabled(False) self.iface.mapCanvas().unsetMapTool(self) QSettings().setValue("instantprint/geometry", self.dialog.saveGeometry()) list = [] for i in range(self.dialogui.comboBox_scale.count()): list.append(self.dialogui.comboBox_scale.itemText(i)) QSettings().setValue("instantprint/scales", ";".join(list)) def retrieve_scales(self, checkScale): if self.dialogui.comboBox_scale.findText(checkScale) == -1: self.dialogui.comboBox_scale.addItem(checkScale) def add_new_scale(self): new_layout = self.dialogui.comboBox_scale.currentText() if self.dialogui.comboBox_scale.findText(new_layout) == -1: self.dialogui.comboBox_scale.addItem(new_layout) self.check_scales() def remove_scale(self): layout_to_delete = self.dialogui.comboBox_scale.currentIndex() self.dialogui.comboBox_scale.removeItem(layout_to_delete) self.check_scales() def setEnabled(self, enabled): if enabled: self.dialog.setVisible(True) self.__reloadLayouts() self.iface.mapCanvas().setMapTool(self) else: self.dialog.setVisible(False) self.iface.mapCanvas().unsetMapTool(self) def __changeScale(self): if not self.mapitem: return newscale = self.dialogui.comboBox_scale.scale() if abs(newscale) < 1E-6: return extent = self.mapitem.extent() center = extent.center() newwidth = extent.width() / self.mapitem.scale() * newscale newheight = extent.height() / self.mapitem.scale() * newscale x1 = center.x() - 0.5 * newwidth y1 = center.y() - 0.5 * newheight x2 = center.x() + 0.5 * newwidth y2 = center.y() + 0.5 * newheight self.mapitem.setExtent(QgsRectangle(x1, y1, x2, y2)) self.__createRubberBand() self.check_scales() def __selectLayout(self): if not self.dialog.isVisible(): return activeIndex = self.dialogui.comboBox_layouts.currentIndex() if activeIndex < 0: return layoutView = self.dialogui.comboBox_layouts.itemData(activeIndex) maps = [] layout_name = self.dialogui.comboBox_layouts.currentText() layout = self.projectLayoutManager.layoutByName(layout_name) for item in layoutView.items(): if isinstance(item, QgsLayoutItemMap): maps.append(item) if len(maps) != 1: QMessageBox.warning(self.iface.mainWindow(), self.tr("Invalid layout"), self.tr("The layout must have exactly one map item.")) self.exportButton.setEnabled(False) self.iface.mapCanvas().scene().removeItem(self.rubberband) self.rubberband = None self.dialogui.comboBox_scale.setEnabled(False) return self.dialogui.comboBox_scale.setEnabled(True) self.exportButton.setEnabled(True) self.layoutView = layoutView self.mapitem = layout.referenceMap() self.dialogui.comboBox_scale.setScale(self.mapitem.scale()) self.__createRubberBand() def __createRubberBand(self): self.__cleanup() extent = self.mapitem.extent() center = self.iface.mapCanvas().extent().center() self.corner = QPointF(center.x() - 0.5 * extent.width(), center.y() - 0.5 * extent.height()) self.rect = QRectF(self.corner.x(), self.corner.y(), extent.width(), extent.height()) self.mapitem.setExtent(QgsRectangle(self.rect)) self.rubberband = QgsRubberBand(self.iface.mapCanvas(), QgsWkbTypes.PolygonGeometry) self.rubberband.setToCanvasRectangle(self.__canvasRect(self.rect)) self.rubberband.setColor(QColor(127, 127, 255, 127)) self.pressPos = None def __cleanup(self): if self.rubberband: self.iface.mapCanvas().scene().removeItem(self.rubberband) if self.oldrubberband: self.iface.mapCanvas().scene().removeItem(self.oldrubberband) self.rubberband = None self.oldrubberband = None self.pressPos = None def canvasPressEvent(self, e): if not self.rubberband: return r = self.__canvasRect(self.rect) if e.button() == Qt.LeftButton and self.__canvasRect(self.rect).contains(e.pos()): self.oldrect = QRectF(self.rect) self.oldrubberband = QgsRubberBand(self.iface.mapCanvas(), QgsWkbTypes.PolygonGeometry) self.oldrubberband.setToCanvasRectangle(self.__canvasRect(self.oldrect)) self.oldrubberband.setColor(QColor(127, 127, 255, 31)) self.pressPos = (e.x(), e.y()) self.iface.mapCanvas().setCursor(Qt.ClosedHandCursor) def canvasMoveEvent(self, e): if not self.pressPos: return mup = self.iface.mapCanvas().mapSettings().mapUnitsPerPixel() x = self.corner.x() + (e.x() - self.pressPos[0]) * mup y = self.corner.y() + (self.pressPos[1] - e.y()) * mup snaptol = 10 * mup # Left edge matches with old right if abs(x - (self.oldrect.x() + self.oldrect.width())) < snaptol: x = self.oldrect.x() + self.oldrect.width() # Right edge matches with old left elif abs(x + self.rect.width() - self.oldrect.x()) < snaptol: x = self.oldrect.x() - self.rect.width() # Left edge matches with old left elif abs(x - self.oldrect.x()) < snaptol: x = self.oldrect.x() # Bottom edge matches with old top if abs(y - (self.oldrect.y() + self.oldrect.height())) < snaptol: y = self.oldrect.y() + self.oldrect.height() # Top edge matches with old bottom elif abs(y + self.rect.height() - self.oldrect.y()) < snaptol: y = self.oldrect.y() - self.rect.height() # Bottom edge matches with old bottom elif abs(y - self.oldrect.y()) < snaptol: y = self.oldrect.y() self.rect = QRectF( x, y, self.rect.width(), self.rect.height() ) self.rubberband.setToCanvasRectangle(self.__canvasRect(self.rect)) def canvasReleaseEvent(self, e): if e.button() == Qt.LeftButton and self.pressPos: self.corner = QPointF(self.rect.x(), self.rect.y()) self.pressPos = None self.iface.mapCanvas().setCursor(Qt.OpenHandCursor) self.iface.mapCanvas().scene().removeItem(self.oldrubberband) self.oldrect = None self.oldrubberband = None self.mapitem.setExtent(QgsRectangle(self.rect)) def __canvasRect(self, rect): mtp = self.iface.mapCanvas().mapSettings().mapToPixel() p1 = mtp.transform(QgsPoint(rect.left(), rect.top())) p2 = mtp.transform(QgsPoint(rect.right(), rect.bottom())) return QRect(p1.x(), p1.y(), p2.x() - p1.x(), p2.y() - p1.y()) def __export(self): settings = QSettings() format = self.dialogui.comboBox_fileformat.itemData(self.dialogui.comboBox_fileformat.currentIndex()) filepath = QFileDialog.getSaveFileName( self.iface.mainWindow(), self.tr("Export Layout"), settings.value("/instantprint/lastfile", ""), format ) if not all(filepath): return # Ensure output filename has correct extension filename = os.path.splitext(filepath[0])[0] + "." + self.dialogui.comboBox_fileformat.currentText().lower() settings.setValue("/instantprint/lastfile", filepath[0]) if self.populateCompositionFz: self.populateCompositionFz(self.layoutView.composition()) success = False layout_name = self.dialogui.comboBox_layouts.currentText() layout_item = self.projectLayoutManager.layoutByName(layout_name) exporter = QgsLayoutExporter(layout_item) if filename[-3:].lower() == u"pdf": success = exporter.exportToPdf(filepath[0], QgsLayoutExporter.PdfExportSettings()) else: success = exporter.exportToImage(filepath[0], QgsLayoutExporter.ImageExportSettings()) if success != 0: QMessageBox.warning(self.iface.mainWindow(), self.tr("Export Failed"), self.tr("Failed to export the layout.")) def __print(self): layout_name = self.dialogui.comboBox_layouts.currentText() layout_item = self.projectLayoutManager.layoutByName(layout_name) actual_printer = QgsLayoutExporter(layout_item) printdialog = QPrintDialog(self.printer) if printdialog.exec_() != QDialog.Accepted: return success = actual_printer.print(self.printer, QgsLayoutExporter.PrintExportSettings()) if success != 0: QMessageBox.warning(self.iface.mainWindow(), self.tr("Print Failed"), self.tr("Failed to print the layout.")) def __reloadLayouts(self, removed=None): if not self.dialog.isVisible(): # Make it less likely to hit the issue outlined in https://github.com/qgis/QGIS/pull/1938 return self.dialogui.comboBox_layouts.blockSignals(True) prev = None if self.dialogui.comboBox_layouts.currentIndex() >= 0: prev = self.dialogui.comboBox_layouts.currentText() self.dialogui.comboBox_layouts.clear() active = 0 for layout in self.projectLayoutManager.layouts(): if layout != removed and layout.name(): cur = layout.name() self.dialogui.comboBox_layouts.addItem(cur, layout) if prev == cur: active = self.dialogui.comboBox_layouts.count() - 1 self.dialogui.comboBox_layouts.setCurrentIndex(-1) # Ensure setCurrentIndex below actually changes an index self.dialogui.comboBox_layouts.blockSignals(False) if self.dialogui.comboBox_layouts.count() > 0: self.dialogui.comboBox_layouts.setCurrentIndex(active) self.dialogui.comboBox_scale.setEnabled(True) self.exportButton.setEnabled(True) else: self.exportButton.setEnabled(False) self.dialogui.comboBox_scale.setEnabled(False) def __help(self): manualPath = os.path.join(os.path.dirname(__file__), "help", "documentation.pdf") QDesktopServices.openUrl(QUrl.fromLocalFile(manualPath)) def scaleFromString(self, scaleText): locale = QLocale() parts = [locale.toInt(part) for part in scaleText.split(":")] try: if len(parts) == 2 and parts[0][1] and parts[1][1] and parts[0][0] != 0 and parts[1][0] != 0: return float(parts[0][0]) / float(parts[1][0]) else: return None except ZeroDivisionError: return def check_scales(self): predefScalesStr = QSettings().value("Map/scales", PROJECT_SCALES).split(",") predefScales = [self.scaleFromString(scaleString) for scaleString in predefScalesStr] comboScalesStr = [self.dialogui.comboBox_scale.itemText(i) for i in range(self.dialogui.comboBox_scale.count())] comboScales = [self.scaleFromString(scaleString) for scaleString in comboScalesStr] currentScale = self.scaleFromString(self.dialogui.comboBox_scale.currentText()) if not currentScale: self.dialogui.comboBox_scale.lineEdit().setStyleSheet("background: #FF7777; color: #FFFFFF;") self.dialogui.addScale.setVisible(True) self.dialogui.addScale.setEnabled(False) self.dialogui.deleteScale.setVisible(False) else: self.dialogui.comboBox_scale.lineEdit().setStyleSheet("") if currentScale in comboScales: # If entry scale is already in the list, allow removing it unless it is a predefined scale self.dialogui.addScale.setVisible(False) self.dialogui.deleteScale.setVisible(True) self.dialogui.deleteScale.setEnabled(currentScale not in predefScales) else: # Otherwise, show button to add it self.dialogui.addScale.setVisible(True) self.dialogui.addScale.setEnabled(True) self.dialogui.deleteScale.setVisible(False)
class Callout(QGraphicsItem): def __init__(self, chart): super().__init__(chart) self.m_chart = chart self.m_text = "" self.m_textRect = QRectF() self.m_rect = QRectF() self.m_anchor = QPointF() self.m_font = QFont() def boundingRect(self): anchor = self.mapFromParent(self.m_chart.mapToPosition(self.m_anchor)) rect = QRectF() rect.setLeft(min(self.m_rect.left(), anchor.x())) rect.setRight(max(self.m_rect.right(), anchor.x())) rect.setTop(min(self.m_rect.top(), anchor.y())) rect.setBottom(max(self.m_rect.bottom(), anchor.y())) return rect def paint(self, painter, option, widget=None): path = QPainterPath() path.addRoundedRect(self.m_rect, 5, 5) anchor = self.mapFromParent(self.m_chart.mapToPosition(self.m_anchor)) if not self.m_rect.contains(anchor): point1 = QPointF() point2 = QPointF() # establish the position of the anchor point in relation to m_rect above = anchor.y() <= self.m_rect.top() aboveCenter = (anchor.y() > self.m_rect.top() and anchor.y() <= self.m_rect.center().y()) belowCenter = (anchor.y() > self.m_rect.center().y() and anchor.y() <= self.m_rect.bottom()) below = anchor.y() > self.m_rect.bottom() onLeft = anchor.x() <= self.m_rect.left() leftOfCenter = (anchor.x() > self.m_rect.left() and anchor.x() <= self.m_rect.center().x()) rightOfCenter = (anchor.x() > self.m_rect.center().x() and anchor.x() <= self.m_rect.right()) onRight = anchor.x() > self.m_rect.right() # get the nearest m_rect corner. x = (onRight + rightOfCenter) * self.m_rect.width() y = (below + belowCenter) * self.m_rect.height() cornerCase = ((above and onLeft) or (above and onRight) or (below and onLeft) or (below and onRight)) vertical = abs(anchor.x() - x) > abs(anchor.y() - y) x1 = (x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * int(not vertical) * (onLeft * 10 - onRight * 20)) y1 = (y + aboveCenter * 10 - belowCenter * 20 + cornerCase * int(vertical) * (above * 10 - below * 20)) point1.setX(x1) point1.setY(y1) x2 = (x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * int(not vertical) * (onLeft * 20 - onRight * 10)) y2 = (y + aboveCenter * 20 - belowCenter * 10 + cornerCase * int(vertical) * (above * 20 - below * 10)) point2.setX(x2) point2.setY(y2) path.moveTo(point1) path.lineTo(anchor) path.lineTo(point2) path = path.simplified() painter.setBrush(QColor(255, 255, 255)) painter.drawPath(path) painter.drawText(self.m_textRect, self.m_text) def mousePressEvent(self, event): event.setAccepted(True) def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: self.setPos( self.mapToParent(event.pos() - event.buttonDownPos(Qt.LeftButton))) event.setAccepted(True) else: event.setAccepted(False) def setText(self, text): self.m_text = text metrics = QFontMetrics(self.m_font) self.m_textRect = QRectF( metrics.boundingRect(QRect(0, 0, 150, 150), Qt.AlignLeft, self.m_text)) self.m_textRect.translate(5, 5) self.prepareGeometryChange() self.m_rect = self.m_textRect.adjusted(-5, -5, 5, 5) def setAnchor(self, point): self.m_anchor = point def updateGeometry(self): self.prepareGeometryChange() self.setPos( self.m_chart.mapToPosition(self.m_anchor) + QPoint(10, -50))
def interactiveResize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QPointF """ scene = self.scene() snap = scene.mainwindow.snapToGrid size = scene.GridSize offset = self.handleSize + self.handleMove moved = self.label.moved R = QRectF(self.boundingRect()) D = QPointF(0, 0) minBoundW = self.minwidth + offset * 2 minBoundH = self.minheight + offset * 2 self.prepareGeometryChange() if self.mousePressHandle == self.handleTL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setLeft(R.left()) self.background.setTop(R.top()) self.selection.setLeft(R.left()) self.selection.setTop(R.top()) self.polygon.setLeft(R.left() + offset) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleTM: fromY = self.mousePressBound.top() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, -offset, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setTop(R.top()) self.selection.setTop(R.top()) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleTR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setRight(R.right()) self.background.setTop(R.top()) self.selection.setRight(R.right()) self.selection.setTop(R.top()) self.polygon.setRight(R.right() - offset) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleML: fromX = self.mousePressBound.left() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, -offset, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) self.background.setLeft(R.left()) self.selection.setLeft(R.left()) self.polygon.setLeft(R.left() + offset) elif self.mousePressHandle == self.handleMR: fromX = self.mousePressBound.right() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, +offset, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) self.background.setRight(R.right()) self.selection.setRight(R.right()) self.polygon.setRight(R.right() - offset) elif self.mousePressHandle == self.handleBL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setLeft(R.left()) self.background.setBottom(R.bottom()) self.selection.setLeft(R.left()) self.selection.setBottom(R.bottom()) self.polygon.setLeft(R.left() + offset) self.polygon.setBottom(R.bottom() - offset) elif self.mousePressHandle == self.handleBM: fromY = self.mousePressBound.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, +offset, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setBottom(R.bottom()) self.selection.setBottom(R.bottom()) self.polygon.setBottom(R.bottom() - offset) elif self.mousePressHandle == self.handleBR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setRight(R.right()) self.background.setBottom(R.bottom()) self.selection.setRight(R.right()) self.selection.setBottom(R.bottom()) self.polygon.setRight(R.right() - offset) self.polygon.setBottom(R.bottom() - offset) self.updateHandles() self.updateTextPos(moved=moved) self.updateAnchors(self.mousePressData, D)
class cheeseWidget(QWidget): def __init__(self,winParent, pose3d): super(cheeseWidget, self).__init__() self.winParent=winParent self.rectangle = QRectF(0.0, 0.0, 300.0, 300.0) self.pose3d = pose3d def drawRedZones(self, painter): self.setStyle(painter, QColor(255,70,70),QColor(255,70,70),1) startAngle = 0 * 16 spanAngle = 45 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) startAngle = 135 * 16 spanAngle = 45 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) startAngle = 180 * 16 spanAngle = 180 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) def drawOrangeZones(self, painter): self.setStyle(painter, QColor(255,220,23),QColor(255,220,23),1) startAngle = 45 * 16 spanAngle = 30 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) startAngle = 105 * 16 spanAngle = 30 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) def drawGreenZones(self, painter): self.setStyle(painter, QColor(117,240,154),QColor(117,240,154),1) startAngle = 75 * 16 spanAngle = 15 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) startAngle = 90 * 16 spanAngle = 15 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) def drawArrow(self, painter, angle=90): radius = 130 yawRad = self.pose3d.getYaw() angle = -(yawRad + pi/2) # PI/2 para centrar la aguja origx = self.rectangle.width() / 2 origy = self.rectangle.height() / 2 finx = radius * math.cos(angle) + origx finy = radius * math.sin(angle) + origy self.setStyle(painter, Qt.black,Qt.black,3) painter.drawLine(QPoint(origx,origy), QPoint(finx,finy)) painter.drawEllipse(145,145, 10, 10) def resetPen(self, painter): pen = QPen(Qt.black, 1) brush = QBrush() painter.setPen(pen) painter.setBrush(brush) def setStyle(self, painter, fillColor, penColor, stroke): brush = QBrush() pen = QPen(penColor, stroke) brush.setColor(fillColor) brush.setStyle(Qt.SolidPattern) painter.setBrush(brush) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) def paintEvent(self, event): painter = QPainter(self) self.drawRedZones(painter) self.drawOrangeZones(painter) self.drawGreenZones(painter) self.drawArrow(painter,120) def updateG(self): self.update()
class timeAnalogWidget(QWidget): time = pyqtSignal() def __init__(self,winParent): super(timeAnalogWidget, self).__init__() self.winParent=winParent self.rectangle = QRectF(0.0, 0.0, 300.0, 300.0) self.angle = -pi/2 self.angleMinutes = -pi/2 self.seconds = 900 self.accountant = 0 self.minutes = 0 timer = QTimer(self) timer.start(1000) timer.timeout.connect(self.accountantTime) def drawWhiteZones(self, painter): self.setStyle(painter, QColor(255,255,255),QColor(255,255,255),1) startAngle = 0 * 16 spanAngle = 360 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) def drawCLockLines(self, painter): radius = 130 angle = -pi/2 for i in range (0, 60): origx = self.rectangle.width() / 2 + (radius-1) * math.cos(angle) origy = self.rectangle.height() / 2 + (radius-1) * math.sin(angle) finx = 6 * math.cos(angle) + origx finy = 6 * math.sin(angle) + origy self.setStyle(painter, Qt.black,Qt.black,3) painter.drawLine(QPoint(origx,origy), QPoint(finx,finy)) angle = angle + (6*pi/180) def drawArrows(self, painter): radius = 130 origx = self.rectangle.width() / 2 origy = self.rectangle.height() / 2 finx = radius * math.cos(self.angle) + origx finy = radius * math.sin(self.angle) + origy finMinutesx = radius/2 * math.cos(self.angleMinutes) + origx finMinutesy = radius/2 * math.sin(self.angleMinutes) + origy self.setStyle(painter, Qt.black,Qt.black,3) painter.drawLine(QPoint(origx,origy), QPoint(finx,finy)) painter.drawLine(QPoint(origx,origy), QPoint(finMinutesx,finMinutesy)) painter.drawEllipse(145,145, 10, 10) def accountantTime(self): if self.accountant < self.seconds: self.accountant += 1 self.angle = self.angle + (6*pi/180) if self.accountant % 60 == 0: self.minutes += 1 self.angleMinutes = self.angleMinutes + (6*pi/180) def resetPen(self, painter): pen = QPen(Qt.black, 1) brush = QBrush() painter.setPen(pen) painter.setBrush(brush) def setStyle(self, painter, fillColor, penColor, stroke): brush = QBrush() pen = QPen(penColor, stroke) brush.setColor(fillColor) brush.setStyle(Qt.SolidPattern) painter.setBrush(brush) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) def paintEvent(self, event): painter = QPainter(self) self.drawWhiteZones(painter) self.drawArrows(painter) self.drawCLockLines(painter) def updateG(self): self.update()
class BboxEditor(QObject): changed = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.bbox01 = QRectF() self.bboxDrawing = QRect() self.color = QColor() self.isActive = False self.posPress = QPoint() self.bbox01Press = QRect() self.isDragging = False self.prevWheelEventTimestamp = datetime.datetime.now() self.scalingMultiplier = 1 self.targetRatio = 1 self.stayInside = True def setTargetRatio(self, ratio): self.targetRatio = ratio self.setBbox01(self.bbox01) def setStayInside(self, stay): self.stayInside = stay self.setBbox01(self.bbox01) def bbox01KeepInsideDrawingArea(self): # r = w0*w / h0*h # h0*h = w0*w / r # h0 = w0*w / r / h w = self.bbox01.width() * self.bboxDrawing.width() h = self.bbox01.height() * self.bboxDrawing.height() print('bbox01KeepInsideDrawingArea() bboxDrawing:', self.bboxDrawing, 'w:', w, 'h:', h) if h == 0: return if w / h != self.targetRatio: print('bbox01KeepInsideDrawingArea() bbox01 a:', self.bbox01, 'targetRatio:', self.targetRatio) self.bbox01.setHeight(w / self.targetRatio / self.bboxDrawing.height()) print('bbox01KeepInsideDrawingArea() bbox01 b:', self.bbox01) if self.stayInside: if self.bbox01.width() > 1: self.bbox01.setWidth(1) if self.bbox01.height() > 1: self.bbox01.setHeight(1) if self.bbox01.x() < 0: self.bbox01.translate(-self.bbox01.x(), 0) if self.bbox01.y() < 0: self.bbox01.translate(0, -self.bbox01.y()) if self.bbox01.x() + self.bbox01.width() > 1: self.bbox01.translate( 1 - (self.bbox01.x() + self.bbox01.width()), 0) if self.bbox01.y() + self.bbox01.height() > 1: self.bbox01.translate( 0, 1 - (self.bbox01.y() + self.bbox01.height())) print('bbox01KeepInsideDrawingArea() bbox01 c:', self.bbox01, 'toPixels:', self.bbox01ToPixels()) def bbox01ToPixels(self): r = QRect( self.bboxDrawing.x() + self.bbox01.x() * self.bboxDrawing.width(), self.bboxDrawing.y() + self.bbox01.y() * self.bboxDrawing.height(), self.bbox01.width() * self.bboxDrawing.width(), self.bbox01.width() * self.bboxDrawing.width() / self.targetRatio) #self.bbox01.height()*self.bboxDrawing.height()) print('bbox01ToPixels() r:', r.width(), r.height(), 'bboxDrawing:', self.bboxDrawing, 'bbox01:', self.bbox01) return r def paint(self, qpainter): r = self.bbox01ToPixels() print('paint() bboxDrawing:', self.bboxDrawing, ' bbox01:', self.bbox01, 'r:', r) c = self.color w = 3 if self.isActive else 1 #c.setAlpha(255 if self.isActive else 127) # Outer white rect pen = QPen(QColor(255, 255, 255)) pen.setWidth(w + 2) qpainter.setPen(pen) qpainter.drawRect(r) # Main colored rect pen = QPen(c) pen.setWidth(w) qpainter.setPen(pen) qpainter.drawRect(r) def setColor(self, color): self.color = color self.changed.emit() def setDrawingArea(self, qrect): self.bboxDrawing = qrect self.changed.emit() def setBbox01(self, qrect): self.bbox01 = qrect self.bbox01KeepInsideDrawingArea() self.changed.emit() def getBbox01(self): return self.bbox01 def setActive(self, active): self.isActive = active self.changed.emit() def mouseMoveEvent(self, e): if not self.isActive: return if self.isDragging: diff = e.pos() - self.posPress diff01 = QPointF(diff.x() / self.bboxDrawing.width(), diff.y() / self.bboxDrawing.height()) print('mouseMoveEvent() bbox01:', self.bbox01, 'diff:', diff, 'diff01:', diff01) self.setBbox01(self.bbox01Press.translated(diff01)) def mousePressEvent(self, e): if not self.isActive: return bb = self.bbox01ToPixels() if (e.pos().x() >= bb.topLeft().x()) and \ (e.pos().y() >= bb.topLeft().y()) and \ e.pos().x() <= bb.bottomRight().x() and \ e.pos().y() <= bb.bottomRight().y() \ : self.isDragging = True self.posPress = e.pos() self.bbox01Press = self.bbox01 def mouseReleaseEvent(self, e): if not self.isActive: return self.isDragging = False def wheelEvent(self, e): if not self.isActive: return t_diff = (datetime.datetime.now() - self.prevWheelEventTimestamp).total_seconds() if t_diff < 0.1: self.scalingMultiplier *= 1.5 else: self.scalingMultiplier = 1 print('wheelEvent() t:', self.prevWheelEventTimestamp, '->', datetime.datetime.now(), 't_diff:', t_diff, 'angleDelta:', e.angleDelta(), 'scalingMultiplier:', self.scalingMultiplier) #sign = -1 if e.angleDelta().y() < 0 else 1 #scale = 1 + 0.1*sign scale = 0.01 scale *= self.scalingMultiplier scale += 1 if e.angleDelta().y() > 0: scale = 1 / scale diff_x = (self.bbox01.width() * (1 - scale)) / 2 diff_y = (self.bbox01.height() * (1 - scale)) / 2 print('wheelEvent() scale:', scale, 'diff:', diff_x, diff_y) self.setBbox01(self.bbox01.adjusted(diff_x, diff_y, -diff_x, -diff_y)) self.prevWheelEventTimestamp = datetime.datetime.now() def getBbox01(self): return self.bbox01
def paint(self, painter, xxx, xxx2): pos = self.toCanvasCoordinates(self.map_pos) self.setPos(pos) if self.marker_mode: mode = self.config.csettings["canvas_marker_mode"] if mode == 'auto': self.set_size(20) transform = self.canvas.getCoordinateTransform() start_point = transform.toMapCoordinates(pos.x(), pos.y()) map_end_point_width = endpoint(start_point, self.width, 90 + math.degrees(self.heading)) map_end_point_length = endpoint(start_point, self.length, math.degrees(self.heading)) # to canvas coordinates canvas_end_point_width = self.toCanvasCoordinates(map_end_point_width) canvas_end_point_length = self.toCanvasCoordinates(map_end_point_length) width = magnitude(self.toCanvasCoordinates(start_point), QgsPointXY(canvas_end_point_width)) height = magnitude(self.toCanvasCoordinates(start_point), QgsPointXY(canvas_end_point_length)) if width < self.size and height < self.size: self.changing_scale = self.canvas.scale() else: self.changing_scale = self.canvas.scale() * 2 elif mode == 'manual': self.changing_scale = self.config.csettings["canvas_marker_scale"] else: self.changing_scale = 400 if self.svg is None or self.canvas.scale() >= self.changing_scale: self.set_size(20) half_size = self.size / 2.0 rect = QRectF(0 - half_size, 0 - half_size, self.size, self.size) painter.setRenderHint(QPainter.Antialiasing) self.pointpen.setColor(Qt.black) self.pointpen.setWidth(2) self.pointbrush.setColor(self.color) painter.setBrush(self.pointbrush) painter.setPen(self.pointpen) y = 0 - half_size x = rect.width() / 2 - half_size line = QLine(x, y, x, rect.height() - half_size) y = rect.height() / 2 - half_size x = 0 - half_size line2 = QLine(x, y, rect.width() - half_size, y) # Arrow p = QPolygonF() p.append(QPoint(0 - half_size, 0)) p.append(QPoint(0, -self.size)) x = rect.width() - half_size p.append(QPoint(x, 0)) p.append(QPoint(0, 0)) offsetp = QPolygonF() offsetp.append(QPoint(0 - half_size, 0)) offsetp.append(QPoint(0, -self.size)) x = rect.width() - half_size offsetp.append(QPoint(x, 0)) offsetp.append(QPoint(0, 0)) painter.save() painter.rotate(math.degrees(self.heading) + self.canvas.rotation()) if self.orientation: path = QPainterPath() path.addPolygon(p) painter.drawPath(path) painter.restore() painter.drawEllipse(rect) painter.drawLine(line) painter.drawLine(line2) # svg valid elif self.svg is not None and self.svg.isValid(): # get rotation rotation = self.canvas.rotation() painter.save() transform = self.canvas.getCoordinateTransform() start_point = transform.toMapCoordinates(pos.x(), pos.y()) map_end_point_width = endpoint(start_point, self.width, 90 + math.degrees(self.heading)) map_end_point_length = endpoint(start_point, self.length, math.degrees(self.heading)) # to canvas coordinates canvas_end_point_width = self.toCanvasCoordinates(map_end_point_width) canvas_end_point_length = self.toCanvasCoordinates(map_end_point_length) width = magnitude(self.toCanvasCoordinates(start_point), QgsPointXY(canvas_end_point_width)) height = magnitude(self.toCanvasCoordinates(start_point), QgsPointXY(canvas_end_point_length)) if width > height: self.set_size(width) else: self.set_size(height) if width != 0 and height != 0: center_x = width / 2.0 center_y = height / 2.0 # work out how to shift the image so that it rotates # properly about its center # ( x cos a + y sin a - x, -x sin a + y cos a - y) myradians = math.radians(rotation + math.degrees(self.heading)) xshift = int(((center_x * math.cos(myradians)) + (center_y * math.sin(myradians))) - center_x) yshift = int(((-center_x * math.sin(myradians)) + (center_y * math.cos(myradians))) - center_y) painter.translate(-width / 2, -height / 2) painter.rotate(math.degrees(self.heading) + self.canvas.rotation()) self.svg.render(painter, QRectF(xshift, yshift, width, height)) painter.restore()
class DomainView(QGraphicsItem): """ The basic and only interactive unit of the view. Paints a domain. We have an `is_selected` variable. This is independent and used in lieu of either Qt's selection or the selection on the groot form. """ def __init__(self, domain: gr.UserDomain, gene_view: "GeneView", positional_index: int, precursor: Optional["DomainView"]) -> None: """ CONSTRUCTOR :param domain: Domain to view :param gene_view: Owning view :param positional_index: Index of domain within gene :param precursor: Previous domain, or None """ assert isinstance(domain, gr.UserDomain) # # SUPER # super().__init__() self.setZValue(DRAWING.Z_GENE) # # FIELDS # self.gene_view = gene_view self.model_view = gene_view.model_view self.sibling_next: DomainView = None self.sibling_previous: DomainView = precursor self.domain: gr.UserDomain = domain self.mousedown_original_pos: QPointF = None self.mousemove_label: str = None self.mousemove_snapline: Tuple[int, int] = None self.mousedown_move_all = False self.index = positional_index self.is_selected = False self.colour = DRAWING.DEFAULT_COLOUR # # POSITION # table = gene_view.model_view.lookup_table self.rect = QRectF(0, 0, domain.length * table.letter_size, table.gene_height) self.load_state() # # PRECURSOR # if precursor: precursor.sibling_next = self # # COMPONENTS # self.components: List[ gr. Component] = self.model_view.model.components.find_components_for_minor_domain( self.domain) def get_x_for_site(self, site): offset = site - self.domain.start offset *= self.model_view.lookup_table.letter_size return self.x() + offset @property def options(self) -> groot.data.config.GlobalOptions: return groot.data.config.options() @property def model(self) -> gr.Model: return self.model_view.model def load_state(self): """ Loads the state (position and colour) of this domain view from the options. If there is no saved state, the default is applied. """ ac = (self.domain.gene.index, self.domain.start) position = self.model.lego_domain_positions.get(ac) if not isinstance(position, dict): self.reset_state() return x = position.get("x", 0) y = position.get("y", 0) c = position.get("c", DRAWING.DEFAULT_COLOUR.colour.name()) self.setPos(x, y) self.colour = ColourBlock(QColor(c)) def save_state(self): """ Saves the state (position) of this domain view to the options. """ ac = (self.domain.gene.index, self.domain.start) self.model.lego_domain_positions[ac] = { "x": self.pos().x(), "y": self.pos().y(), "c": self.colour.colour.name() } def reset_state(self): """ Resets the state (position and colour) of this domain view to the default. The reset state is automatically saved to the options. """ table = self.gene_view.model_view.lookup_table precursor = self.sibling_previous domain = self.domain if precursor: x = precursor.window_rect().right() y = precursor.window_rect().top() else: x = domain.start * table.letter_size y = domain.gene.index * (table.gene_ysep + table.gene_height) self.setPos(x, y) self.colour = DRAWING.DEFAULT_COLOUR self.save_state() @override def boundingRect(self) -> QRectF: return self.rect @override def paint(self, painter: QPainter, *args, **kwargs): """ Paint the domains """ r = self.rect painter.setBrush(self.colour.brush) painter.setPen(self.colour.pen) painter.drawRect(r) is_selected = self.is_selected # Movement is allowed if we have enabled it move_enabled = misc_helper.coalesce( self.options.lego_move_enabled, self.gene_view.model_view.user_move_enabled) # Draw the piano roll unless we're moving if self.options.lego_view_piano_roll is False or move_enabled: draw_piano_roll = False elif self.options.lego_view_piano_roll is None: draw_piano_roll = is_selected else: draw_piano_roll = not is_selected # Draw the selection bars, unless the piano roll is indicative of this already draw_sel_bars = is_selected and not draw_piano_roll # Selection bars # (A blue box inside the gene box) if draw_sel_bars: self.__paint_selection_rect(painter) # Movement bars # (The same as the selection bars but dotted in red and cyan) if move_enabled and is_selected: self.__paint_movement_rect(painter) # Piano roll # (A piano roll for genes) if draw_piano_roll: lookup_table = self.model_view.lookup_table letter_size = lookup_table.letter_size painter.setPen(Qt.NoPen) painter.setBrush( DRAWING.PIANO_ROLL_SELECTED_BACKGROUND if is_selected else DRAWING.PIANO_ROLL_UNSELECTED_BACKGROUND) OFFSET_X = letter_size rect_width = self.rect.width() rect_height = lookup_table.count * letter_size painter.drawRect(0, OFFSET_X, rect_width, rect_height) array = self.domain.site_array if not array: painter.setPen(Pens.RED) painter.drawLine(0, 0, rect_width, rect_height) painter.drawLine(0, rect_height, rect_width, 0) else: for i, c in enumerate(array): pos = lookup_table.letter_order_table.get(c) if pos is not None: painter.setPen( lookup_table.letter_colour_table.get( c, DRAWING.GENE_DEFAULT_FG)) painter.drawEllipse(i * letter_size, pos * letter_size + OFFSET_X, letter_size, letter_size) # Snap-lines, when moving if self.mousemove_snapline: x = self.mousemove_snapline[0] - self.pos().x() y = self.mousemove_snapline[1] - self.pos().y() painter.setPen(DRAWING.SNAP_LINE_2) painter.drawLine(x, self.boundingRect().height() / 2, x, y) painter.setPen(DRAWING.SNAP_LINE) painter.drawLine(x, self.boundingRect().height() / 2, x, y) if not self.mousemove_label.startswith("<"): x -= QFontMetrics(painter.font()).width(self.mousemove_label) if y < 0: y = self.rect.top() - DRAWING.TEXT_MARGIN else: y = self.rect.bottom() + DRAWING.TEXT_MARGIN + QFontMetrics( painter.font()).xHeight() painter.setPen(DRAWING.TEXT_LINE) painter.drawText(QPointF(x, y), self.mousemove_label) # Mouse snapline position elif self.mousemove_label: painter.setPen(DRAWING.TEXT_LINE) painter.drawText( QPointF(self.rect.left() + DRAWING.TEXT_MARGIN, self.rect.top() - DRAWING.TEXT_MARGIN), self.mousemove_label) # Mouse position if not move_enabled: # Positions (when not in move mode) if misc_helper.coalesce(self.options.lego_view_positions, is_selected): # Draw position if self.sibling_previous is None or self.sibling_next is None or self.sibling_previous.rect.width( ) > 32: self.__draw_position(painter) # Domains (when not in move mode) if misc_helper.coalesce(self.options.lego_view_components, is_selected): self.__draw_component_name(painter) def __draw_component_name(self, painter: QPainter): text = ", ".join(str(x) for x in self.components) x = (self.rect.left() + self.rect.right()) / 2 - QFontMetrics( painter.font()).width(text) / 2 y = self.rect.top() - DRAWING.TEXT_MARGIN painter.setPen(DRAWING.COMPONENT_PEN) painter.setBrush(0) painter.drawText(QPointF(x, y), text) def __draw_position(self, painter: QPainter): text = str(self.domain.start) lx = self.rect.left() - QFontMetrics(painter.font()).width(text) / 2 painter.setPen(DRAWING.POSITION_TEXT) painter.drawText(QPointF(lx, self.rect.top() - DRAWING.TEXT_MARGIN), text) def __paint_movement_rect(self, painter: QPainter): r = self.rect MARGIN = 4 painter.setBrush(0) painter.setPen(DRAWING.MOVE_LINE) painter.drawRect(r.left() + MARGIN, r.top() + MARGIN, r.width() - MARGIN * 2, r.height() - MARGIN * 2) painter.setPen(DRAWING.MOVE_LINE_SEL) painter.drawRect(r.left() + MARGIN, r.top() + MARGIN, r.width() - MARGIN * 2, r.height() - MARGIN * 2) # Black start/end when in movement mode if domain isn't adjacent to its siblings if self.sibling_next and self.sibling_next.window_rect().left( ) != self.window_rect().right(): MARGIN = 8 painter.setPen(DRAWING.DISJOINT_LINE) painter.drawLine(r.right(), r.top() - MARGIN, r.right(), r.bottom() + MARGIN) if self.sibling_previous and self.sibling_previous.window_rect().right( ) != self.window_rect().left(): MARGIN = 8 painter.setPen(DRAWING.DISJOINT_LINE) painter.drawLine(r.left(), r.top() - MARGIN, r.left(), r.bottom() + MARGIN) def __paint_selection_rect(self, painter: QPainter): r = self.rect MARGIN = 4 painter.setBrush(0) painter.setPen(DRAWING.SELECTION_LINE) painter.drawRect(r.left() + MARGIN, r.top() + MARGIN, r.width() - MARGIN * 2, r.height() - MARGIN * 2) def __is_draw_position(self, is_selected): return misc_helper.coalesce(self.options.lego_view_positions, is_selected) def __draw_next_sibling_position(self, is_selected): ns = self.sibling_next if ns is None: return False if not ns.__is_draw_position(is_selected): return False return ns.pos().x() == self.window_rect().right() def window_rect(self) -> QRectF: result = self.boundingRect().translated(self.scenePos()) assert result.left() == self.pos().x(), "{} {}".format( self.window_rect().left(), self.pos().x()) # todo: remove assert result.top() == self.pos().y() return result def mousePressEvent(self, m: QGraphicsSceneMouseEvent): """ OVERRIDE Mouse press on domain view i.e. Use clicks a domain """ if m.buttons() & Qt.LeftButton: # Remember the initial position items in case we drag stuff # - do this for all items because it's still possible for the selection to change post-mouse-down for item in self.gene_view.domain_views.values(): item.mousedown_original_pos = item.pos() # If ctrl or meta is down, add to the selection if (m.modifiers() & Qt.ControlModifier) or (m.modifiers() & Qt.MetaModifier): toggle = True else: toggle = False if self.is_selected: # If we are selected stop, this confuses with dragging from a design perspective return self.model_view.handle_domain_clicked(self.domain, toggle) def mouseDoubleClickEvent(self, m: QGraphicsSceneMouseEvent): """ OVERRIDE Double click Just toggles "move enabled" """ self.model_view.user_move_enabled = not self.model_view.user_move_enabled self.model_view.scene.setBackgroundBrush(QBrush(QColor(255, 255, 0))) self.model_view.scene.update() def focusInEvent(self, QFocusEvent): self.setZValue(DRAWING.Z_FOCUS) def focusOutEvent(self, QFocusEvent): self.setZValue(DRAWING.Z_GENE) def snaps(self): for gene_view in self.gene_view.model_view.gene_views.values(): for domain_view in gene_view.domain_views.values(): if domain_view is not self: left_snap = domain_view.scenePos().x() right_snap = domain_view.scenePos().x( ) + domain_view.boundingRect().width() yield left_snap, "Start of {}[{}]".format( domain_view.domain.gene, domain_view.domain.start), domain_view.scenePos().y() yield right_snap, "End of {}[{}]".format( domain_view.domain.gene, domain_view.domain.end), domain_view.scenePos().y() def mouseMoveEvent(self, m: QGraphicsSceneMouseEvent) -> None: if m.buttons() & Qt.LeftButton: if not misc_helper.coalesce( self.options.lego_move_enabled, self.model_view. user_move_enabled) or self.mousedown_original_pos is None: return new_pos: QPointF = self.mousedown_original_pos + ( m.scenePos() - m.buttonDownScenePos(Qt.LeftButton)) new_x = new_pos.x() new_y = new_pos.y() new_x2 = new_x + self.boundingRect().width() self.mousemove_label = "({0} {1})".format(new_pos.x(), new_pos.y()) self.mousemove_snapline = None x_snap_enabled = misc_helper.coalesce( self.options.lego_x_snap, not bool(m.modifiers() & Qt.ControlModifier)) y_snap_enabled = misc_helper.coalesce( self.options.lego_y_snap, not bool(m.modifiers() & Qt.AltModifier)) if x_snap_enabled: for snap_x, snap_label, snap_y in self.snaps(): if (snap_x - 8) <= new_x <= (snap_x + 8): new_x = snap_x self.mousemove_label = "<-- " + snap_label self.mousemove_snapline = snap_x, snap_y break elif (snap_x - 8) <= new_x2 <= (snap_x + 8): new_x = snap_x - self.boundingRect().width() self.mousemove_label = snap_label + " -->" self.mousemove_snapline = snap_x, snap_y break if y_snap_enabled: ysep = self.rect.height() yy = (self.rect.height() + ysep) new_y += yy / 2 new_y = new_y - new_y % yy new_pos.setX(new_x) new_pos.setY(new_y) self.setPos(new_pos) self.save_state() delta_x = new_x - self.mousedown_original_pos.x() delta_y = new_y - self.mousedown_original_pos.y() selected_items = self.model_view.get_selected_userdomain_views() for selected_item in selected_items: if selected_item is not self and selected_item.mousedown_original_pos is not None: selected_item.setPos( selected_item.mousedown_original_pos.x() + delta_x, selected_item.mousedown_original_pos.y() + delta_y) selected_item.save_state() self.model_view.overlay_view.update() def mouseReleaseEvent(self, m: QGraphicsSceneMouseEvent): self.mousemove_label = None self.mousemove_snapline = None self.update() pass # suppress default mouse handling implementation def __repr__(self): return "<<View of '{}' at ({},{})>>".format(self.domain, self.window_rect().left(), self.window_rect().top())
def draw(self, painter, draw=True): if draw: painter.save() painter.setOpacity(self.opacity) if draw and self.renderer is not None: # if self.img is not None: # qsource = QRectF(0,0,self.img.get_width(), self.img.get_height()) # painter.drawImage(self, self.qimage , qsource) # , flags=QtCore.Qt.AutoColor # else: # painter.drawRect(self) # print('size of drawing', self) # print('view box',self.renderer.viewBoxF()) # viewbox = self.renderer.viewBoxF() # --> vraiment presque ça # does not really work neo = QRectF(self) # neo.setX(self.x()-10) # neo.setY(self.y()-10) # neo.setHeight(self.height()+30) # neo.setWidth(self.width()+30) # nb this will create a shear if one dim is not cropped as the other --> really not great in fact maybe deactivate for now ???? or compute AR and adapt it # TODO just warn that it's buggy and should not be used for SVG files only, ok for other stuff though # can I preserve AR ??? if self.__crop_left is not None: neo.setX(neo.x() - self.__crop_left) neo.setWidth(neo.width() + self.__crop_left) if self.__crop_top is not None: neo.setY(neo.y() - self.__crop_top) neo.setHeight(neo.height() + self.__crop_top) if self.__crop_bottom is not None: neo.setHeight(neo.height() + self.__crop_bottom) if self.__crop_right is not None: neo.setWidth(neo.width() + self.__crop_right) # maintenant ça a l'air bon... # --> ça c'est ok --> c'est le clip rect qui merde du coup # print('view box neo', neo) # self.renderer.setViewBox(neo) # le clipping marche mais faut le combiner avec autre chose # painter.setClipRect(self.x()+10, self.y()+10, self.width()-30,self.height()-30)#, Qt::ClipOperation operation = Qt::ReplaceClip # TODO KEEP UNFORTUNATELY unfortunately cropping does not work when saved as svg but works when saved as raster... see https://bugreports.qt.io/browse/QTBUG-28636 # maybe do masque d'ecretage in illustrator or inkscape https://linuxgraphic.org/forums/viewtopic.php?f=6&t=6437 # TODO KEEP IT PROBABLY ALSO CREATES A ZOOM THAT WILL MESS WITH THE FONTS AND LINE SIZE... painter.setClipRect(self) # , Qt::ClipOperation operation = Qt::ReplaceClip , operation=Qt.ReplaceClip self.renderer.render(painter, neo) # the stuff is a qrectf so that should work painter.restore() # self.renderer.setViewBox(viewbox) # then need to draw the letter if self.letter is not None: self.letter.set_P1(self.get_P1()) self.letter.draw(painter) if self.annotation is not None and self.annotation: for annot in self.annotation: annot.draw(draw=draw)
class MainWindow(QQuickWindow): def __init__(self, parent = None): super(MainWindow, self).__init__(parent) self._background_color = QColor(204, 204, 204, 255) self.setClearBeforeRendering(False) self.beforeRendering.connect(self._render, type = Qt.DirectConnection) self._mouse_device = QtMouseDevice(self) self._mouse_device.setPluginId("qt_mouse") self._key_device = QtKeyDevice() self._key_device.setPluginId("qt_key") self._previous_focus = None # type: Optional["QQuickItem"] self._app = QCoreApplication.instance() # Remove previously added input devices (if any). This can happen if the window was re-loaded. self._app.getController().removeInputDevice("qt_mouse") self._app.getController().removeInputDevice("qt_key") self._app.getController().addInputDevice(self._mouse_device) self._app.getController().addInputDevice(self._key_device) self._app.getController().getScene().sceneChanged.connect(self._onSceneChanged) self._preferences = Application.getInstance().getPreferences() self._preferences.addPreference("general/window_width", 1280) self._preferences.addPreference("general/window_height", 720) self._preferences.addPreference("general/window_left", 50) self._preferences.addPreference("general/window_top", 50) self._preferences.addPreference("general/window_state", Qt.WindowNoState) # Restore window geometry self.setWidth(int(self._preferences.getValue("general/window_width"))) self.setHeight(int(self._preferences.getValue("general/window_height"))) self.setPosition(int(self._preferences.getValue("general/window_left")), int(self._preferences.getValue("general/window_top"))) # Make sure restored geometry is not outside the currently available screens screen_found = False for s in range(0, self._app.desktop().screenCount()): if self.geometry().intersects(self._app.desktop().availableGeometry(s)): screen_found = True break if not screen_found: self.setPosition(50, 50) self.setWindowState(int(self._preferences.getValue("general/window_state"))) self._mouse_x = 0 self._mouse_y = 0 self._mouse_pressed = False self._viewport_rect = QRectF(0, 0, 1.0, 1.0) self.closing.connect(self.preClosing) Application.getInstance().setMainWindow(self) self._fullscreen = False self._allow_resize = True # This event is triggered before hideEvent(self, event) event and might prevent window closing if # does not pass the check, for example if USB printer is printing # The implementation is in Cura.qml preClosing = pyqtSignal("QQuickCloseEvent*", arguments = ["close"]) def setAllowResize(self, allow_resize: bool): if self._allow_resize != allow_resize: if not allow_resize: self.setMaximumHeight(self.height()) self.setMinimumHeight(self.height()) self.setMaximumWidth(self.width()) self.setMinimumWidth(self.width()) else: self.setMaximumHeight(16777215) self.setMinimumHeight(0) self.setMaximumWidth(16777215) self.setMinimumWidth(0) self._allow_resize = allow_resize @pyqtSlot() def toggleFullscreen(self): if self._fullscreen: self.setVisibility(QQuickWindow.Windowed) # Switch back to windowed else: self.setVisibility(QQuickWindow.FullScreen) # Go to fullscreen self._fullscreen = not self._fullscreen def getBackgroundColor(self): return self._background_color def setBackgroundColor(self, color): self._background_color = color self._app.getRenderer().setBackgroundColor(color) backgroundColor = pyqtProperty(QColor, fget=getBackgroundColor, fset=setBackgroundColor) mousePositionChanged = pyqtSignal() @pyqtProperty(int, notify = mousePositionChanged) def mouseX(self): return self._mouse_x @pyqtProperty(int, notify = mousePositionChanged) def mouseY(self): return self._mouse_y def setViewportRect(self, rect): if rect != self._viewport_rect: self._viewport_rect = rect self._updateViewportGeometry(self.width() * self.devicePixelRatio(), self.height() * self.devicePixelRatio()) self.viewportRectChanged.emit() viewportRectChanged = pyqtSignal() @pyqtProperty(QRectF, fset = setViewportRect, notify = viewportRectChanged) def viewportRect(self): return self._viewport_rect # Warning! Never reimplemented this as a QExposeEvent can cause a deadlock with QSGThreadedRender due to both trying # to claim the Python GIL. # def event(self, event): def mousePressEvent(self, event): super().mousePressEvent(event) if event.isAccepted(): return if self.activeFocusItem() is not None and self.activeFocusItem() != self._previous_focus: self.activeFocusItem().setFocus(False) self._previous_focus = self.activeFocusItem() self._mouse_device.handleEvent(event) self._mouse_pressed = True def mouseMoveEvent(self, event): self._mouse_x = event.x() self._mouse_y = event.y() if self._mouse_pressed: self.mousePositionChanged.emit() super().mouseMoveEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def mouseReleaseEvent(self, event): super().mouseReleaseEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) self._mouse_pressed = False def keyPressEvent(self, event): super().keyPressEvent(event) if event.isAccepted(): return self._key_device.handleEvent(event) def keyReleaseEvent(self, event): super().keyReleaseEvent(event) if event.isAccepted(): return self._key_device.handleEvent(event) def wheelEvent(self, event): super().wheelEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def moveEvent(self, event): QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection) def resizeEvent(self, event): super().resizeEvent(event) win_w = event.size().width() * self.devicePixelRatio() win_h = event.size().height() * self.devicePixelRatio() self._updateViewportGeometry(win_w, win_h) QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection) def hideEvent(self, event): if Application.getInstance().getMainWindow() == self: Application.getInstance().windowClosed() renderCompleted = Signal(type = Signal.Queued) def _render(self): renderer = self._app.getRenderer() view = self._app.getController().getActiveView() renderer.beginRendering() view.beginRendering() renderer.render() view.endRendering() renderer.endRendering() self.renderCompleted.emit() def _onSceneChanged(self, object): self.update() @pyqtSlot() def _onWindowGeometryChanged(self): self._preferences.setValue("general/window_width", self.width()) self._preferences.setValue("general/window_height", self.height()) self._preferences.setValue("general/window_left", self.x()) self._preferences.setValue("general/window_top", self.y()) # This is a workaround for QTBUG-30085 if self.windowState() in (Qt.WindowNoState, Qt.WindowMaximized): self._preferences.setValue("general/window_state", self.windowState()) def _updateViewportGeometry(self, width: int, height: int): view_width = width * self._viewport_rect.width() view_height = height * self._viewport_rect.height() for camera in self._app.getController().getScene().getAllCameras(): camera.setWindowSize(width, height) if camera.getAutoAdjustViewPort(): camera.setViewportSize(view_width, view_height) projection_matrix = Matrix() if camera.isPerspective(): if view_width is not 0: projection_matrix.setPerspective(30, view_width / view_height, 1, 500) else: projection_matrix.setOrtho(-view_width / 2, view_width / 2, -view_height / 2, view_height / 2, -500, 500) camera.setProjectionMatrix(projection_matrix) self._app.getRenderer().setViewportSize(view_width, view_height) self._app.getRenderer().setWindowSize(width, height)
class DesignItem(QGraphicsItem): positionChanged = pyqtSignal(int, int) def __init__(self, scene, is_scene_rect=False): QGraphicsItem.__init__(self) self.scene = scene self.is_scene_rect = is_scene_rect self.id = "" self.xscale = 1 self.yscale = 1 self.scaleX = 1.0 self.scaleY = 1.0 self.pen = QPen() self.brush = QBrush() self.hasHandles = False self.handles = [None, None, None, None, None, None, None, None] self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) def setId(self, id): self.id = id def setBrush(self, brush): self.brush = brush def setPen(self, pen): self.pen = pen def setRect(self, x, y, w, h): self.rect = QRectF(x, y, w, h) def setWidth(self, value): self.rect.setWidth(value) def setHeight(self, value): self.rect.setHeight(value) def width(self): return self.rect.width() def height(self): return self.rect.height() def boundingRect(self): return self.rect def isSceneRect(self): return self.is_scene_rect def drawHighlightSelected(self, painter, option): itemPenWidth = self.pen.widthF() pad = itemPenWidth / 2 penWidth = 0 fgcolor = option.palette.windowText().color() if fgcolor.red() > 127: r = 0 else: r = 255 if fgcolor.green() > 127: g = 0 else: g = 255 if fgcolor.blue() > 127: b = 0 else: b = 255 bgcolor = QColor(r, g, b) painter.setOpacity(1.0) painter.setPen(QPen(bgcolor, penWidth, Qt.SolidLine)) painter.setBrush(Qt.NoBrush) painter.drawRect(self.boundingRect().adjusted(pad, pad, -pad, -pad)) painter.setPen(QPen(option.palette.windowText(), 0, Qt.DashLine)) painter.setBrush(Qt.NoBrush) painter.drawRect(self.boundingRect().adjusted(pad, pad, -pad, -pad)) def scaleObjects(self): pass def setHandlePositions(self): if not self.hasHandles: return halfwidth = self.handles[0].width / 2.0 self.handles[0].setPos(-halfwidth, -halfwidth) self.handles[1].setPos(self.rect.width() - halfwidth, -halfwidth) self.handles[2].setPos(self.rect.width() - halfwidth, self.rect.height() - halfwidth) self.handles[3].setPos(-halfwidth, self.rect.height() - halfwidth) self.handles[4].setPos(self.rect.width() / 2 - halfwidth, -halfwidth) self.handles[5].setPos(self.rect.width() - halfwidth, self.rect.height() / 2 - halfwidth) self.handles[6].setPos(self.rect.width() / 2 - halfwidth, self.rect.height() - halfwidth) self.handles[7].setPos(-halfwidth, self.rect.height() / 2 - halfwidth) self.scene.update(self.x() - halfwidth - 5, self.y() - halfwidth - 5, self.x() + self.rect.width() + halfwidth * 2 + 5, self.y() + self.rect.height() + halfwidth * 2 + 5) def sceneEventFilter(self, watched, event): if isinstance(watched, ItemHandle): handle = watched else: return False if isinstance(event, QGraphicsSceneMouseEvent): mevent = event else: return False if mevent.type() == QEvent.GraphicsSceneMousePress: self.oldx = self.pos().x() self.oldy = self.pos().y() self.oldwidth = self.rect.width() self.oldheight = self.rect.height() handle.setMouseState(ItemHandle.MOUSE_DOWN) handle.mouseDownX = mevent.pos().x() handle.mouseDownY = mevent.pos().y() elif mevent.type() == QEvent.GraphicsSceneMouseRelease: if self.oldx != self.pos().x() and self.oldy != self.pos().y( ) and self.oldwidth != self.rect.width( ) and self.oldheight != self.rect.height(): undostack = self.scene.undostack cmd = ScaleItemCommand(self.pos().x(), self.pos().y(), self.rect.width(), self.rect.height(), self.oldx, self.oldy, self.oldwidth, self.oldheight, self.scene, self) undoStack.push(cmd) handle.setMouseState(ItemHandle.MOUSE_RELEASED) elif mevent.type() == QEvent.GraphicsSceneMouseMove: handle.setMouseState(ItemHandle.MOUSE_MOVING) else: return False if handle.getMouseState() == ItemHandle.MOUSE_MOVING: x = mevent.pos().x() y = mevent.pos().y() XaxisSign = 0 YaxisSign = 0 if handle.getCorner() == 0: XaxisSign = +1 YaxisSign = +1 elif handle.getCorner() == 1: XaxisSign = -1 YaxisSign = +1 elif handle.getCorner() == 2: XaxisSign = -1 YaxisSign = -1 elif handle.getCorner() == 3: XaxisSign = +1 YaxisSign = -1 elif handle.getCorner() == 4: YaxisSign = +1 elif handle.getCorner() == 5: XaxisSign = -1 elif handle.getCorner() == 6: YaxisSign = -1 elif handle.getCorner() == 7: XaxisSign = +1 xMoved = handle.mouseDownX - x yMoved = handle.mouseDownY - y newWidth = self.rect.width() + (XaxisSign * xMoved) if newWidth < 20: newWidth = 20 newHeight = self.rect.height() + (YaxisSign * yMoved) if newHeight < 20: newHeight = 20 deltaWidth = newWidth - self.rect.width() deltaHeight = newHeight - self.rect.height() shiftPressed = False controlPressed = False modifiers = QGuiApplication.keyboardModifiers() if modifiers == Qt.ShiftModifier: shiftPressed = True elif modifiers == Qt.ControlModifier: controlPressed = True elif modifiers == (Qt.ControlModifier | Qt.ShiftModifier): shiftPressed = True controlPressed = True if controlPressed: # keep ratio ratio = self.rect.width() / self.rect.height() if handle.getCorner() < 4: # corners if newWidth > newHeight: deltaWidth = int(deltaHeight * ratio) else: deltaHeight = int(deltaWidth / ratio) else: if handle.getCorner() == 4 or handle.getCorner( ) == 6: # top | bottom deltaWidth = deltaHeight * ratio else: # left | right deltaHeight = deltaWidth / ratio self.setRect(0, 0, self.rect.width() + deltaWidth, self.rect.height() + deltaHeight) self.scaleObjects() deltaWidth *= (-1) deltaHeight *= (-1) newXpos = self.pos().x() newYpos = self.pos().y() if handle.getCorner() == 0: # top left if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 else: newXpos = self.pos().x() + deltaWidth newYpos = self.pos().y() + deltaHeight elif handle.getCorner() == 1: # top right if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 else: newYpos = self.pos().y() + deltaHeight elif handle.getCorner() == 2: # bottom right if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 elif handle.getCorner() == 3: # bottom left if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 else: newXpos = self.pos().x() + deltaWidth elif handle.getCorner() == 4: # top if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 elif controlPressed: newYpos = self.pos().y() + deltaHeight newXpos = self.pos().x() + deltaWidth / 2 else: newYpos = self.pos().y() + deltaHeight elif handle.getCorner() == 5: # right if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 elif controlPressed: newYpos = self.pos().y() + deltaHeight / 2 elif handle.getCorner() == 6: # bottom if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 elif controlPressed: newXpos = self.pos().x() + deltaWidth / 2 elif handle.getCorner() == 7: # left if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 elif controlPressed: newXpos = self.pos().x() + deltaWidth newYpos = self.pos().y() + deltaHeight / 2 else: newXpos = self.pos().x() + deltaWidth if newXpos != self.pos().x() or newYpos != self.pos().y(): self.setPos(newXpos, newYpos) self.posChanged(newXpos, newYpos) self.setHandlePositions() self.update() return True def itemChange(self, change, value): if change == QGraphicsItem.ItemSelectedChange: if value: if not self.hasHandles: for i in range(8): self.handles[i] = ItemHandle(self, i, self.scene.scaling) self.handles[i].installSceneEventFilter(self) self.hasHandles = True self.setHandlePositions() else: for i in range(8): self.scene.removeItem(self.handles[i]) self.handles[i] = None self.hasHandles = False elif change == QGraphicsItem.ItemPositionHasChanged: if self.isSelected(): newPos = value self.posChanged(newPos.x(), newPos.y()) self.setHandlePositions() return super().itemChange(change, value) def posChanged(self, x, y): pass #self.positionChanged.emit(x, y) def contextMenuEvent(self, event): if not self.is_scene_rect: self.scene.clearSelection() self.setSelected(True) #self.contextMenu.exec(event.screenPos()) delAct = QAction("Delete") delAct.setShortcut("Delete") delAct.triggered.connect(self.deleteItemAction) bringToFrontAct = QAction("Bring to front") bringToFrontAct.triggered.connect(self.bringToFrontAction) sendToBackAct = QAction("Send to back") sendToBackAct.triggered.connect(self.sendToBackAction) raiseAct = QAction("Raise") raiseAct.triggered.connect(self.raiseAction) lowerAct = QAction("Lower") lowerAct.triggered.connect(self.lowerAction) contextMenu = QMenu() contextMenu.addAction(delAct) contextMenu.addSeparator() contextMenu.addAction(bringToFrontAct) contextMenu.addAction(raiseAct) contextMenu.addAction(lowerAct) contextMenu.addAction(sendToBackAct) contextMenu.exec(event.screenPos()) def deleteItemAction(self): self.scene.deleteItem(self) def lowerAction(self): cmd = LowerItemCommand(self) self.scene.undostack.push(cmd) def raiseAction(self): cmd = RaiseItemCommand(self) self.scene.undostack.push(cmd) def sendToBackAction(self): cmd = SendItemToBackCommand(self) self.scene.undostack.push(cmd) def bringToFrontAction(self): cmd = BringItemToFrontCommand(self) self.scene.undostack.push(cmd) def lowerItem(self): pos = self.scene.items().index(self) for i in range(pos + 1, len(self.scene.items())): item = self.scene.items()[i] if isinstance(item, DesignItem) and not item.is_scene_rect: self.stackBefore(item) break # trick to repaint item self.setSelected(False) self.setSelected(True) def raiseItem(self): pos = self.scene.items().index(self) for i in range(pos - 1, -1, -1): item = self.scene.items()[i] if isinstance(item, DesignItem): item.stackBefore(self) break # trick to repaint item self.setSelected(False) self.setSelected(True) def bringToFront(self): pos = self.scene.items().index(self) for i in range(pos - 1, -1, -1): item = self.scene.items()[i] if isinstance(item, DesignItem): item.stackBefore(self) # trick to repaint item self.setSelected(False) self.setSelected(True) def sendToBack(self): pos = self.scene.items().index(self) for i in range(pos + 1, len(self.scene.items())): item = self.scene.items()[i] if isinstance(item, DesignItem) and not item.is_scene_rect: self.stackBefore(item) # trick to repaint item self.setSelected(False) self.setSelected(True)
class MainWindow(QQuickWindow): def __init__(self, parent = None): super(MainWindow, self).__init__(parent) self._background_color = QColor(204, 204, 204, 255) self.setClearBeforeRendering(False) self.beforeRendering.connect(self._render, type=Qt.DirectConnection) self._mouse_device = QtMouseDevice(self) self._mouse_device.setPluginId("qt_mouse") self._key_device = QtKeyDevice() self._key_device.setPluginId("qt_key") self._previous_focus = None self._app = QCoreApplication.instance() self._app.getController().addInputDevice(self._mouse_device) self._app.getController().addInputDevice(self._key_device) self._app.getController().getScene().sceneChanged.connect(self._onSceneChanged) self._preferences = Preferences.getInstance() self._preferences.addPreference("general/window_width", 1280) self._preferences.addPreference("general/window_height", 720) self._preferences.addPreference("general/window_left", 50) self._preferences.addPreference("general/window_top", 50) self._preferences.addPreference("general/window_state", Qt.WindowNoState) # Restore window geometry self.setWidth(int(self._preferences.getValue("general/window_width"))) self.setHeight(int(self._preferences.getValue("general/window_height"))) self.setPosition(int(self._preferences.getValue("general/window_left")), int(self._preferences.getValue("general/window_top"))) # Make sure restored geometry is not outside the currently available screens if not self.geometry().intersects(self.screen().availableGeometry()): self.setPosition(50,50) self.setWindowState(int(self._preferences.getValue("general/window_state"))) self._mouse_x = 0 self._mouse_y = 0 self._viewport_rect = QRectF(0, 0, 1.0, 1.0) Application.getInstance().setMainWindow(self) self._fullscreen = False @pyqtSlot() def toggleFullscreen(self): if self._fullscreen: self.setVisibility(QQuickWindow.Windowed) # Switch back to windowed else: self.setVisibility(QQuickWindow.FullScreen) # Go to fullscreen self._fullscreen = not self._fullscreen def getBackgroundColor(self): return self._background_color def setBackgroundColor(self, color): self._background_color = color self._app.getRenderer().setBackgroundColor(color) backgroundColor = pyqtProperty(QColor, fget=getBackgroundColor, fset=setBackgroundColor) mousePositionChanged = pyqtSignal() @pyqtProperty(int, notify = mousePositionChanged) def mouseX(self): return self._mouse_x @pyqtProperty(int, notify = mousePositionChanged) def mouseY(self): return self._mouse_y def setViewportRect(self, rect): if rect != self._viewport_rect: self._viewport_rect = rect self._updateViewportGeometry(self.width() * self.devicePixelRatio(), self.height() * self.devicePixelRatio()) self.viewportRectChanged.emit() viewportRectChanged = pyqtSignal() @pyqtProperty(QRectF, fset = setViewportRect, notify = viewportRectChanged) def viewportRect(self): return self._viewport_rect # Warning! Never reimplemented this as a QExposeEvent can cause a deadlock with QSGThreadedRender due to both trying # to claim the Python GIL. # def event(self, event): def mousePressEvent(self, event): super().mousePressEvent(event) if event.isAccepted(): return if self.activeFocusItem() != None and self.activeFocusItem() != self._previous_focus: self.activeFocusItem().setFocus(False) self._previous_focus = self.activeFocusItem() self._mouse_device.handleEvent(event) def mouseMoveEvent(self, event): self._mouse_x = event.x() self._mouse_y = event.y() self.mousePositionChanged.emit() super().mouseMoveEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def mouseReleaseEvent(self, event): super().mouseReleaseEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def keyPressEvent(self, event): super().keyPressEvent(event) if event.isAccepted(): return self._key_device.handleEvent(event) def keyReleaseEvent(self, event): super().keyReleaseEvent(event) if event.isAccepted(): return self._key_device.handleEvent(event) def wheelEvent(self, event): super().wheelEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def moveEvent(self, event): QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection); def resizeEvent(self, event): super().resizeEvent(event) win_w = event.size().width() * self.devicePixelRatio() win_h = event.size().height() * self.devicePixelRatio() self._updateViewportGeometry(win_w, win_h) QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection); def hideEvent(self, event): Application.getInstance().windowClosed() def _render(self): renderer = self._app.getRenderer() view = self._app.getController().getActiveView() renderer.beginRendering() view.beginRendering() renderer.render() view.endRendering() renderer.endRendering() def _onSceneChanged(self, object): self.update() @pyqtSlot() def _onWindowGeometryChanged(self): if self.windowState() == Qt.WindowNoState: self._preferences.setValue("general/window_width", self.width()) self._preferences.setValue("general/window_height", self.height()) self._preferences.setValue("general/window_left", self.x()) self._preferences.setValue("general/window_top", self.y()) self._preferences.setValue("general/window_state", Qt.WindowNoState) elif self.windowState() == Qt.WindowMaximized: self._preferences.setValue("general/window_state", Qt.WindowMaximized) def _updateViewportGeometry(self, width, height): view_w = width * self._viewport_rect.width() view_h = height * self._viewport_rect.height() for camera in self._app.getController().getScene().getAllCameras(): camera.setViewportSize(view_w, view_h) camera.setWindowSize(width, height) proj = Matrix() if camera.isPerspective(): proj.setPerspective(30, view_w / view_h, 1, 500) else: proj.setOrtho(-view_w / 2, view_w / 2, -view_h / 2, view_h / 2, -500, 500) camera.setProjectionMatrix(proj) self._app.getRenderer().setViewportSize(view_w, view_h) self._app.getRenderer().setWindowSize(width, height)
def paintEvent(self, event): painter = QPainter(self.viewport()) painter.fillRect(0, 0, self.viewport().width(), self.viewport().height(), QColor('#181818')) self.pos = self.verticalScrollBar().value() data_start = 0 data_end = 0 if len(self.data) > self.visible_lines(): data_start = self.pos data_end = self.pos + self.visible_lines() else: data_end = len(self.data) drawing_pos_y = 10 trace_depth = 0 fontMetrics = QFontMetrics(QFont(self.font)) text_options = QTextOption() text_options.setAlignment(Qt.AlignLeft) text_options.setWrapMode(QTextOption.WrapAtWordBoundaryOrAnywhere) for i, line in enumerate(self.data): if i == self.pos: break if line['event'] == 'leave': trace_depth -= 1 elif line['event'] == 'enter': trace_depth += 1 for i, line in enumerate(self.data[data_start:data_end]): if i > self.visible_lines(): break is_obj = False if isinstance(line['data'], str) and line['data'].startswith('{'): is_obj = True line['data'] = json.loads(line['data']) drawing_pos_x = 10 painter.setPen(QColor('#fff')) if line['event'] == 'leave': if trace_depth: trace_depth -= 1 drawing_pos_x += (trace_depth * 20) painter.setPen(QColor('crimson')) painter.setBrush(QColor('#222')) polygon = QPolygon() polygon.append(QPoint(drawing_pos_x - 6, drawing_pos_y + (self._char_height * 0.5))) polygon.append(QPoint(drawing_pos_x + 10, drawing_pos_y - (self._char_height * 0.5))) polygon.append(QPoint(self.viewport().width() - 21, drawing_pos_y - (self._char_height * 0.5))) polygon.append(QPoint(self.viewport().width() - 21, drawing_pos_y + self._char_height + (self._char_height * 0.5))) polygon.append(QPoint(drawing_pos_x + 10, drawing_pos_y + self._char_height + (self._char_height * 0.5))) polygon.append(QPoint(drawing_pos_x - 6, drawing_pos_y + (self._char_height * 0.5))) painter.drawPolygon(polygon) elif line['event'] == 'enter': trace_depth += 1 drawing_pos_x += (trace_depth * 20) painter.setPen(QColor('yellowgreen')) painter.setBrush(QColor('#222')) polygon = QPolygon() polygon.append(QPoint(drawing_pos_x + 6, drawing_pos_y - (self._char_height * 0.5))) polygon.append(QPoint(int(floor(self.viewport().width())) - 21, drawing_pos_y - (self._char_height * 0.5))) polygon.append(QPoint(int(floor(self.viewport().width())) - 5, drawing_pos_y + (self._char_height * 0.5))) polygon.append(QPoint(int(floor(self.viewport().width())) - 21, drawing_pos_y + self._char_height + (self._char_height * 0.5))) polygon.append(QPoint(drawing_pos_x + 6, drawing_pos_y + self._char_height + (self._char_height * 0.5))) #polygon.append(QPoint(drawing_pos_x + 21, drawing_pos_y + (self._char_height * 0.5))) polygon.append(QPoint(drawing_pos_x + 6, drawing_pos_y - (self._char_height * 0.5))) painter.drawPolygon(polygon) drawing_pos_x += 20 rect = QRectF(drawing_pos_x, drawing_pos_y, self.viewport().width() - 25 - drawing_pos_x, self._char_height + 10) if line['event'] == 'enter': arg_str = '(' for a in range(len(line['data'])): arg_str += 'arg_{0}, '.format(a) if len(line['data']): arg_str = arg_str[:-2] arg_str += ')' painter.drawText(rect, line['class'] + arg_str, option=text_options) else: painter.drawText(rect, line['class'], option=text_options) drawing_pos_y += self._char_height + 15 if isinstance(line['data'], str): if line['data']: rect = fontMetrics.boundingRect(drawing_pos_x, drawing_pos_y, self.viewport().width() - drawing_pos_x - 25, 0, Qt.AlignLeft | Qt.TextWordWrap | Qt.TextWrapAnywhere, line['data']) rect = QRectF(drawing_pos_x, drawing_pos_y, rect.width(), rect.height()) painter.setPen(QColor('#888')) painter.drawText(rect, line['data'], option=text_options) drawing_pos_y += rect.height() + 5 else: width = int(floor(self.viewport().width() - drawing_pos_x - (5 * self._char_width) - 35)) max_chars = int(floor(width / self._char_width)) hold_x = drawing_pos_x + 5 width -= 20 painter.setPen(QColor('#888')) for data in line['data']: drawing_pos_x = hold_x if isinstance(line['data'][data], int): text = '{0:d}'.format(line['data'][data]) elif isinstance(line['data'][data], str): text = line['data'][data] elif isinstance(line['data'][data], list): text = str(line['data'][data]) else: text = str(line['data'][data]) if line['event'] == 'enter': arg = 'arg_{0}: '.format(data) painter.drawText(drawing_pos_x, drawing_pos_y + self._base_line, arg) drawing_pos_x += len(arg) * self._char_width elif line['event'] == 'leave': retval = data + ': ' painter.drawText(drawing_pos_x, drawing_pos_y + self._base_line, retval) drawing_pos_x += len(retval) * self._char_width if len(text) * self._char_width < width: painter.drawText(drawing_pos_x, drawing_pos_y + self._base_line, text) drawing_pos_y += self._char_height + 5 else: rect = fontMetrics.boundingRect(drawing_pos_x, drawing_pos_y, width, 0, Qt.AlignLeft | Qt.TextWordWrap | Qt.TextWrapAnywhere, text) rect = QRectF(rect) painter.drawText(rect, text, option=text_options) drawing_pos_y += rect.height() + 5 drawing_pos_y += self._char_height + 5
def paintEvent(self, event): # based on # http://qt.gitorious.org/qt/qt/blobs/master/src/gui/widgets/qslider.cpp painter = QPainter(self) style = self.style() opt = QStyleOptionSlider() self.initStyleOption(opt) groove_rect = style.subControlRect(style.CC_Slider, opt, QStyle.SC_SliderGroove, self) handle_rect = style.subControlRect(style.CC_Slider, opt, QStyle.SC_SliderHandle, self) slider_space = style.pixelMetric(style.PM_SliderSpaceAvailable, opt) range_x = style.sliderPositionFromValue(self.minimum(), self.maximum(), self.value(), slider_space) range_height = 4 groove_rect = QRectF(groove_rect.x(), handle_rect.center().y() - (range_height / 2), groove_rect.width(), range_height) range_rect = QRectF(groove_rect.x(), handle_rect.center().y() - (range_height / 2), range_x, range_height) if style.metaObject().className() != 'QMacStyle': # Paint groove for Fusion and Windows styles cur_brush = painter.brush() cur_pen = painter.pen() painter.setBrush(QBrush(QColor(169, 169, 169))) painter.setPen(Qt.NoPen) # painter.drawRect(groove_rect) painter.drawRoundedRect(groove_rect, groove_rect.height() / 2, groove_rect.height() / 2) painter.setBrush(cur_brush) painter.setPen(cur_pen) cur_brush = painter.brush() cur_pen = painter.pen() painter.setBrush(QBrush(QColor(18, 141, 148))) painter.setPen(Qt.NoPen) painter.drawRect(range_rect) painter.setBrush(cur_brush) painter.setPen(cur_pen) opt = QStyleOptionSlider() self.initStyleOption(opt) opt.subControls = QStyle.SC_SliderHandle if self.tickPosition() != self.NoTicks: opt.subControls |= QStyle.SC_SliderTickmarks if self.isSliderDown(): opt.state |= QStyle.State_Sunken else: opt.state |= QStyle.State_Active opt.activeSubControls = QStyle.SC_None opt.sliderPosition = self.value() opt.sliderValue = self.value() style.drawComplexControl(QStyle.CC_Slider, opt, painter, self)
def paint(self, painter, option, widget): lod = option.levelOfDetailFromTransform(painter.worldTransform()) if lod > 0.15: if lod > 1 or self.rowLength < 3: valueSize = len(str(self.value)) painter.setPen(QColor(0, 0, 0)) painter.setBrush(self.tokenColor) rectValue = self.getTokenRect() if self.hover: #Make token larger when the mouse hovers over it rectValue = self.getTokenRectHover() painter.drawEllipse(rectValue) #Determine font size based on size of token content if self.hover: #Larger text when hovering if valueSize == 1: rectValue = QRectF(rectValue.x(), rectValue.x() + 1, rectValue.width(), rectValue.height()) painter.setFont(QFont("Lucida Console", 13)) elif valueSize == 2: painter.setFont(QFont("Lucida Console", 11)) elif valueSize == 3: painter.setFont(QFont("Lucida Console", 8)) elif valueSize > 3: painter.setFont(QFont("Lucida Console", 6)) else: #Normal size text when not hovering if valueSize == 1: rectValue = QRectF(rectValue.x(), rectValue.x() + 1, rectValue.width(), rectValue.height()) painter.setFont(QFont("Lucida Console", 9)) elif valueSize == 2: painter.setFont(QFont("Lucida Console", 7)) elif valueSize == 3: painter.setFont(QFont("Lucida Console", 5)) elif valueSize > 3: painter.setFont(QFont("Lucida Console", 4)) if lod > 0.4: painter.drawText(rectValue, Qt.AlignCenter, str(self.value)) #Add dots to indicate that not the entire contents of the token is displayed if valueSize > 5: rect = rectValue rect.setY(rect.y() + 5) painter.drawText(rect, Qt.AlignCenter, '..')
def redraw(self): self.graphicsScene.clear() # draw screenshot self.graphicsScene.addPixmap(self.screenPixel) # prepare for drawing selected area rect = QRectF(self.selectedArea) rect = rect.normalized() topLeftPoint = rect.topLeft() topRightPoint = rect.topRight() bottomLeftPoint = rect.bottomLeft() bottomRightPoint = rect.bottomRight() topMiddlePoint = (topLeftPoint + topRightPoint) / 2 leftMiddlePoint = (topLeftPoint + bottomLeftPoint) / 2 bottomMiddlePoint = (bottomLeftPoint + bottomRightPoint) / 2 rightMiddlePoint = (topRightPoint + bottomRightPoint) / 2 # draw the picture mask mask = QColor(0, 0, 0, 155) if self.selectedArea == QRect(): self.graphicsScene.addRect(0, 0, self.screenPixel.width(), self.screenPixel.height(), QPen(Qt.NoPen), mask) else: self.graphicsScene.addRect(0, 0, self.screenPixel.width(), topRightPoint.y(), QPen(Qt.NoPen), mask) self.graphicsScene.addRect(0, topLeftPoint.y(), topLeftPoint.x(), rect.height(), QPen(Qt.NoPen), mask) self.graphicsScene.addRect( topRightPoint.x(), topRightPoint.y(), self.screenPixel.width() - topRightPoint.x(), rect.height(), QPen(Qt.NoPen), mask) self.graphicsScene.addRect( 0, bottomLeftPoint.y(), self.screenPixel.width(), self.screenPixel.height() - bottomLeftPoint.y(), QPen(Qt.NoPen), mask) # draw the toolBar if self.action != ACTION_SELECT: spacing = 5 # show the toolbar first, then move it to the correct position # because the width of it may be wrong if this is the first time it shows self.tooBar.show() dest = QPointF(rect.bottomRight() - QPointF(self.tooBar.width(), 0) - QPointF(spacing, -spacing)) if dest.x() < spacing: dest.setX(spacing) pen_set_bar_height = self.penSetBar.height( ) if self.penSetBar is not None else 0 if dest.y() + self.tooBar.height( ) + pen_set_bar_height >= self.height(): if rect.top() - self.tooBar.height( ) - pen_set_bar_height < spacing: dest.setY(rect.top() + spacing) else: dest.setY(rect.top() - self.tooBar.height() - pen_set_bar_height - spacing) self.tooBar.move(dest.toPoint()) if self.penSetBar is not None: self.penSetBar.show() self.penSetBar.move(dest.toPoint() + QPoint(0, self.tooBar.height() + spacing)) if self.action == ACTION_TEXT: self.penSetBar.showFontWidget() else: self.penSetBar.showPenWidget() else: self.tooBar.hide() if self.penSetBar is not None: self.penSetBar.hide() # draw the list for step in self.drawListResult: self.drawOneStep(step) if self.drawListProcess is not None: self.drawOneStep(self.drawListProcess) if self.action != ACTION_TEXT: self.drawListProcess = None if self.selectedArea != QRect(): self.itemsToRemove = [] # draw the selected rectangle pen = QPen(QColor(0, 255, 255), 2) self.itemsToRemove.append(self.graphicsScene.addRect(rect, pen)) # draw the drag point radius = QPoint(3, 3) brush = QBrush(QColor(0, 255, 255)) self.itemsToRemove.append( self.graphicsScene.addEllipse( QRectF(topLeftPoint - radius, topLeftPoint + radius), pen, brush)) self.itemsToRemove.append( self.graphicsScene.addEllipse( QRectF(topMiddlePoint - radius, topMiddlePoint + radius), pen, brush)) self.itemsToRemove.append( self.graphicsScene.addEllipse( QRectF(topRightPoint - radius, topRightPoint + radius), pen, brush)) self.itemsToRemove.append( self.graphicsScene.addEllipse( QRectF(leftMiddlePoint - radius, leftMiddlePoint + radius), pen, brush)) self.itemsToRemove.append( self.graphicsScene.addEllipse( QRectF(rightMiddlePoint - radius, rightMiddlePoint + radius), pen, brush)) self.itemsToRemove.append( self.graphicsScene.addEllipse( QRectF(bottomLeftPoint - radius, bottomLeftPoint + radius), pen, brush)) self.itemsToRemove.append( self.graphicsScene.addEllipse( QRectF(bottomMiddlePoint - radius, bottomMiddlePoint + radius), pen, brush)) self.itemsToRemove.append( self.graphicsScene.addEllipse( QRectF(bottomRightPoint - radius, bottomRightPoint + radius), pen, brush)) # draw the textedit if self.textPosition is not None: textSpacing = 50 position = QPoint() if self.textPosition.x() + self.textInput.width( ) >= self.screenPixel.width(): position.setX(self.textPosition.x() - self.textInput.width()) else: position.setX(self.textPosition.x()) if self.textRect is not None: if self.textPosition.y() + self.textInput.height( ) + self.textRect.height() >= self.screenPixel.height(): position.setY(self.textPosition.y() - self.textInput.height() - self.textRect.height()) else: position.setY(self.textPosition.y() + self.textRect.height()) else: if self.textPosition.y() + self.textInput.height( ) >= self.screenPixel.height(): position.setY(self.textPosition.y() - self.textInput.height()) else: position.setY(self.textPosition.y()) self.textInput.move(position) self.textInput.show() # self.textInput.getFocus() # draw the magnifier if self.action == ACTION_SELECT: self.drawMagnifier() if self.mousePressed: self.drawSizeInfo() if self.action == ACTION_MOVE_SELECTED: self.drawSizeInfo()
class RegionSelector: '''Class to handle coordinating multiple selector windows for a multimonitor setup. ''' def __init__(self, x, y, w, h, pixmap): self.moving = self.drawing = self.resizing = False self.bounds = QRectF(x, y, w, h) self.selectors = [] self.selection = None for screen in QApplication.screens(): # make a new selector for this screen # TODO: Support limited subset of screens? geo = screen.geometry() sel = Selector(geo.x(), geo.y(), geo.width(), geo.height(), pixmap, self) self.selectors.append(sel) #endfor #enddef def exec_(self): cPos = QCursor.pos() QCursor.setPos( 0, 0 ) # XFCE workaround. In XCFE, window position is relative to the cursor position for sel in self.selectors: sel.show() QCursor.setPos(cPos) self.selectors[-1].exec_() #enddef def updateSelection(self): for s in self.selectors: s.updateSelection() #enddef def mousePressEvent(self, e): pos = e.globalPos() self.moving = self.drawing = self.resizing = False if self.selection is None or not self.selection.contains(pos): self.drawing = True self.selection = None self.selectionStart = pos #elif in corner #elif on line else: # in self.moving = True self.selBeforeMove = self.selection self.moveOrigin = pos #endif self.updateSelection() #enddef def mouseMoveEvent(self, e): pos = e.globalPos() if self.drawing: if pos.x() != self.selectionStart.x() and pos.y( ) != self.selectionStart.y(): self.selection = QRectF(QPointF(self.selectionStart), QPointF(pos)).normalized() self.selection = self.selection.intersected(self.bounds) self.updateSelection() #endif elif self.moving: delta = pos - self.moveOrigin moved = self.selBeforeMove.translated(delta) # TODO: this cause the bounds to be "sticky", since trying to move back after pushing it 50px off the edge # means we must re-traverse the other 49 "off screen" pixels before we get some visible movement minX, maxX = self.bounds.x( ), self.bounds.x() + self.bounds.width() - moved.width() minY, maxY = self.bounds.y( ), self.bounds.y() + self.bounds.height() - moved.height() moved.moveTo(min(max(minX, moved.x()), maxX), min(max(minY, moved.y()), maxY)) self.selection = moved.intersected(self.bounds) self.updateSelection() #endif #enddef def mouseReleaseEvent(self, e): self.moving = self.drawing = self.resizing = False self.updateSelection() #enddef def keyReleaseEvent(self, event): if event.key() == Qt.Key_Escape: self.selection = None self.close() elif event.key() in (Qt.Key_Enter, Qt.Key_Return): if self.selection: self.close() #endif #endif #enddef def close(self): for s in self.selectors: s.close()
class Viewer(QGraphicsView): def __init__(self, gridsize_label, position_label, help_label): QGraphicsView.__init__(self) self.gridsize_label = gridsize_label self.position_label = position_label self.help_label = help_label # Create a QGraphicsScene which this view looks at self.scene = QGraphicsScene(self) self.scene.setSceneRect(QRectF()) self.setScene(self.scene) # Customize QGraphicsView self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setInteractive(False) self.scale(1, -1) # Flips around the Y axis # Use OpenGL http://ralsina.me/stories/BBS53.html # self.setViewport(QtOpenGL.QGLWidget()) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.pen = QPen(QtCore.Qt.black, 0) self.portpen = QPen(PORT_COLOR, 3) self.portpen.setCosmetic(True) # Makes constant width self.portfont = QtGui.QFont('Arial', pointSize=14) self.portfontcolor = PORT_COLOR self.subportpen = QPen(SUBPORT_COLOR, 3) self.subportpen.setCosmetic(True) # Makes constant width self.subportfont = QtGui.QFont('Arial', pointSize=14) self.subportfontcolor = SUBPORT_COLOR # Tracking ports # Various status variables self._mousePressed = None self._rb_origin = QPoint() self.zoom_factor_total = 1 # Grid variables self.gridpen = QPen(QtCore.Qt.black, 0) self.gridpen.setStyle(QtCore.Qt.DotLine) self.gridpen.setDashPattern([1, 4]) self.gridpen.setColor(QtGui.QColor(0, 0, 0, 125)) # self.gridpen = QPen(QtCore.Qt.black, 1) # self.gridpen.setCosmetic(True) # Makes constant width self.scene_polys = [] self.initialize() def add_polygons(self, polygons, color='#A8F22A', alpha=1): qcolor = QColor() qcolor.setNamedColor(color) qcolor.setAlphaF(alpha) for points in polygons: qpoly = QPolygonF([QPointF(p[0], p[1]) for p in points]) scene_poly = self.scene.addPolygon(qpoly) scene_poly.setBrush(qcolor) scene_poly.setPen(self.pen) self.scene_polys.append(scene_poly) # Update custom bounding box sr = scene_poly.sceneBoundingRect() if len(self.scene_polys) == 1: self.scene_xmin = sr.left() self.scene_xmax = sr.right() self.scene_ymin = sr.top() self.scene_ymax = sr.bottom() else: self.scene_xmin = min(self.scene_xmin, sr.left()) self.scene_xmax = max(self.scene_xmax, sr.right()) self.scene_ymin = min(self.scene_ymin, sr.top()) self.scene_ymax = max(self.scene_ymax, sr.bottom()) def reset_view(self): # The SceneRect controls how far you can pan, make it larger than # just the bounding box so middle-click panning works panning_rect = QRectF(self.scene_bounding_rect) panning_rect_center = panning_rect.center() panning_rect_size = max(panning_rect.width(), panning_rect.height()) * 3 panning_rect.setSize(QSizeF(panning_rect_size, panning_rect_size)) panning_rect.moveCenter(panning_rect_center) self.setSceneRect(panning_rect) self.fitInView(self.scene_bounding_rect, Qt.KeepAspectRatio) self.zoom_view(0.8) self.update_grid() def add_port(self, port, is_subport=False): if (port.width is None) or (port.width == 0): x, y = port.midpoint cs = 1 # cross size pn = QPointF(x, y + cs) ps = QPointF(x, y - cs) pe = QPointF(x + cs, y) pw = QPointF(x - cs, y) qline1 = self.scene.addLine(QLineF(pn, ps)) qline2 = self.scene.addLine(QLineF(pw, pe)) port_shapes = [qline1, qline2] else: point1, point2 = port.endpoints point1 = QPointF(point1[0], point1[1]) point2 = QPointF(point2[0], point2[1]) qline = self.scene.addLine(QLineF(point1, point2)) arrow_points = np.array([[0, 0], [10, 0], [6, 4], [6, 2], [0, 2] ]) / (40) * port.width arrow_qpoly = QPolygonF( [QPointF(p[0], p[1]) for p in arrow_points]) port_scene_poly = self.scene.addPolygon(arrow_qpoly) port_scene_poly.setRotation(port.orientation) port_scene_poly.moveBy(port.midpoint[0], port.midpoint[1]) port_shapes = [qline, port_scene_poly] qtext = self.scene.addText(str(port.name), self.portfont) port_items = port_shapes + [qtext] rad = port.orientation * np.pi / 180 x, y = port.endpoints[0] * 1 / 4 + port.endpoints[ 1] * 3 / 4 + np.array([np.cos(rad), np.sin(rad)]) * port.width / 8 # x,y = port.midpoint[0], port.midpoint[1] # x,y = x - qtext.boundingRect().width()/2, y - qtext.boundingRect().height()/2 qtext.setPos(QPointF(x, y)) qtext.setFlag(QGraphicsItem.ItemIgnoresTransformations) if not is_subport: [shape.setPen(self.portpen) for shape in port_shapes] qtext.setDefaultTextColor(self.portfontcolor) self.portitems += port_items else: [shape.setPen(self.subportpen) for shape in port_shapes] qtext.setDefaultTextColor(self.subportfontcolor) self.subportitems += port_items # self.portlabels.append(qtext) def add_aliases(self, aliases): for name, ref in aliases.items(): qtext = self.scene.addText(str(name), self.portfont) x, y = ref.center qtext.setPos(QPointF(x, y)) qtext.setFlag(QGraphicsItem.ItemIgnoresTransformations) self.aliasitems += [qtext] def set_port_visibility(self, visible=True): for item in self.portitems: item.setVisible(visible) self.ports_visible = visible def set_subport_visibility(self, visible=True): for item in self.subportitems: item.setVisible(visible) self.subports_visible = visible def set_alias_visibility(self, visible=True): for item in self.aliasitems: item.setVisible(visible) self.aliases_visible = visible def initialize(self): self.scene.clear() self.polygons = {} self.portitems = [] self.subportitems = [] self.aliasitems = [] self.aliases_visible = True self.ports_visible = True self.subports_visible = True self.mouse_position = [0, 0] self.grid_size_snapped = 0 self.setMouseTracking(True) self.scene_bounding_rect = None self.scene_polys = [] self.scene_xmin = 0 self.scene_xmax = 1 self.scene_ymin = 0 self.scene_ymax = 1 def finalize(self): self.scene_bounding_rect = QRectF( QPointF(self.scene_xmin, self.scene_ymin), QPointF(self.scene_xmax, self.scene_ymax)) # self.scene_center = [self.scene_bounding_rect.center().x(), self.scene_bounding_rect.center().y()] self.scene_size = [ self.scene_bounding_rect.width(), self.scene_bounding_rect.height() ] self.create_grid() self.update_grid() #============================================================================== # Grid creation #============================================================================== def update_grid(self): grid_pixels = 50 grid_snaps = [1, 2, 4] # Number of pixels in the viewer view_width, view_height = self.rect().width(), self.rect().height() # Rectangle of viewport in terms of scene coordinates r = self.mapToScene(self.rect()).boundingRect() width, height = r.width(), r.height() xmin, ymin, xmax, ymax = r.x(), r.y(), r.x() + width, r.y() + height grid_size = grid_pixels * (width / view_width) exponent = np.floor(np.log10(grid_size)) digits = round(grid_size / 10**(exponent), 2) digits_snapped = min(grid_snaps, key=lambda x: abs(x - digits)) grid_size_snapped = digits_snapped * 10**(exponent) # Starting coordinates for gridlines x = round((xmin - 2 * width) / grid_size_snapped) * grid_size_snapped y = round((ymin - 2 * height) / grid_size_snapped) * grid_size_snapped for gl in self.gridlinesx: gl.setLine(x, -1e10, x, 1e10) x += grid_size_snapped for gl in self.gridlinesy: gl.setLine(-1e10, y, 1e10, y) y += grid_size_snapped self.grid_size_snapped = grid_size_snapped self.update_gridsize_label() def update_gridsize_label(self): self.gridsize_label.setText('grid size = ' + str(self.grid_size_snapped)) self.gridsize_label.move(QPoint(5, self.height() - 25)) def update_mouse_position_label(self): self.position_label.setText( 'X = %0.4f / Y = %0.4f' % (self.mouse_position[0], self.mouse_position[1])) self.position_label.move(QPoint(self.width() - 250, self.height() - 25)) def update_help_label(self): self.help_label.setText('Press "?" key for help') self.help_label.move(QPoint(self.width() - 175, 0)) def create_grid(self): self.gridlinesx = [ self.scene.addLine(-10, -10, 10, 10, self.gridpen) for n in range(300) ] self.gridlinesy = [ self.scene.addLine(-10, -10, 10, 10, self.gridpen) for n in range(300) ] self.update_grid() #============================================================================== # Mousewheel zoom, taken from http://stackoverflow.com/a/29026916 #============================================================================== def wheelEvent(self, event): # Zoom Factor zoom_percentage = 1.4 # Set Anchors self.setTransformationAnchor(QGraphicsView.NoAnchor) self.setResizeAnchor(QGraphicsView.NoAnchor) # Save the scene pos oldPos = self.mapToScene(event.pos()) # Zoom mousewheel_rotation = event.angleDelta().y( ) # Typically = 120 on most mousewheels zoom_factor = zoom_percentage**(mousewheel_rotation / 120) zoom_factor = np.clip(zoom_factor, 0.5, 2.0) # Check to make sure we're not overzoomed min_width = 0.01 min_height = 0.01 window_width = self.rect().width() window_height = self.rect().height() scene_upper_left_corner = self.mapToScene(QPoint(0, 0)) scene_bottom_right_corner = self.mapToScene( QPoint(window_width, window_height)) scene_width = (scene_bottom_right_corner - scene_upper_left_corner).x() scene_height = (scene_upper_left_corner - scene_bottom_right_corner).y() max_width = self.scene_bounding_rect.width() * 3 max_height = self.scene_bounding_rect.height() * 3 if ((scene_width > max_width) and (scene_height > max_height)) and (zoom_factor < 1): pass elif ((scene_width < min_width) and (scene_height < min_height)) and (zoom_factor > 1): pass else: self.zoom_view(zoom_factor) # Get the new position and move scene to old position newPos = self.mapToScene(event.pos()) delta = newPos - oldPos self.translate(delta.x(), delta.y()) self.update_grid() def zoom_view(self, zoom_factor): old_center = self.mapToScene(self.rect().center()) self.scale(zoom_factor, zoom_factor) self.centerOn(old_center) self.zoom_factor_total *= zoom_factor def resizeEvent(self, event): super(QGraphicsView, self).resizeEvent(event) if self.scene_bounding_rect is not None: self.reset_view() self.update_gridsize_label() self.update_mouse_position_label() self.update_help_label() def mousePressEvent(self, event): super(QGraphicsView, self).mousePressEvent(event) #============================================================================== # Zoom to rectangle, from # https://wiki.python.org/moin/PyQt/Selecting%20a%20region%20of%20a%20widget #============================================================================== if event.button() == Qt.RightButton: self._mousePressed = Qt.RightButton self._rb_origin = QPoint(event.pos()) self.rubberBand.setGeometry(QRect(self._rb_origin, QSize())) self.rubberBand.show() #============================================================================== # Mouse panning, taken from # http://stackoverflow.com/a/15043279 #============================================================================== elif event.button() == Qt.MidButton: self._mousePressed = Qt.MidButton self._mousePressedPos = event.pos() self.setCursor(QtCore.Qt.ClosedHandCursor) self._dragPos = event.pos() def mouseMoveEvent(self, event): super(QGraphicsView, self).mouseMoveEvent(event) # # Useful debug # try: # self.debug_label.setText(str(itemsBoundingRect_nogrid().width())) # except: # print('Debug statement failed') # Update the X,Y label indicating where the mouse is on the geometry mouse_position = self.mapToScene(event.pos()) self.mouse_position = [mouse_position.x(), mouse_position.y()] self.update_mouse_position_label() if not self._rb_origin.isNull( ) and self._mousePressed == Qt.RightButton: self.rubberBand.setGeometry( QRect(self._rb_origin, event.pos()).normalized()) # Middle-click-to-pan if self._mousePressed == Qt.MidButton: newPos = event.pos() diff = newPos - self._dragPos self._dragPos = newPos self.horizontalScrollBar().setValue( self.horizontalScrollBar().value() - diff.x()) self.verticalScrollBar().setValue( self.verticalScrollBar().value() - diff.y()) # event.accept() def mouseReleaseEvent(self, event): if event.button() == Qt.RightButton: self.rubberBand.hide() rb_rect = QRect(self._rb_origin, event.pos()) rb_center = rb_rect.center() rb_size = rb_rect.size() if abs(rb_size.width()) > 3 and abs(rb_size.height()) > 3: viewport_size = self.viewport().geometry().size() zoom_factor_x = abs(viewport_size.width() / rb_size.width()) zoom_factor_y = abs(viewport_size.height() / rb_size.height()) new_center = self.mapToScene(rb_center) zoom_factor = min(zoom_factor_x, zoom_factor_y) self.zoom_view(zoom_factor) self.centerOn(new_center) self.update_grid() if event.button() == Qt.MidButton: self.setCursor(Qt.ArrowCursor) self._mousePressed = None self.update_grid() def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.reset_view() if event.key() == Qt.Key_F1: self.set_alias_visibility(not self.aliases_visible) if event.key() == Qt.Key_F2: self.set_port_visibility(not self.ports_visible) if event.key() == Qt.Key_F3: self.set_subport_visibility(not self.subports_visible) if event.key() == Qt.Key_Question: help_str = """ Mouse control: Mousewheel: Zoom in and out Right-click & drag: Zoom to rectangle Middle-click & drag: Pan Keyboard shortcuts: Esc: Reset view F1: Show/hide alias names F2: Show/hide ports F3: Show/hide subports (ports in underlying references) """ msg = QMessageBox.about(self, 'PHIDL Help', help_str) msg.raise_()
class cheeseWidget(QWidget): def __init__(self, winParent, pose3d): super(cheeseWidget, self).__init__() self.winParent = winParent self.rectangle = QRectF(0.0, 0.0, 300.0, 300.0) self.pose3d = pose3d def drawRedZones(self, painter): self.setStyle(painter, QColor(255, 70, 70), QColor(255, 70, 70), 1) startAngle = 0 * 16 spanAngle = 45 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) startAngle = 135 * 16 spanAngle = 45 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) startAngle = 180 * 16 spanAngle = 180 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) def drawOrangeZones(self, painter): self.setStyle(painter, QColor(255, 220, 23), QColor(255, 220, 23), 1) startAngle = 45 * 16 spanAngle = 30 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) startAngle = 105 * 16 spanAngle = 30 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) def drawGreenZones(self, painter): self.setStyle(painter, QColor(117, 240, 154), QColor(117, 240, 154), 1) startAngle = 75 * 16 spanAngle = 15 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) startAngle = 90 * 16 spanAngle = 15 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) def drawArrow(self, painter, angle=90): radius = 130 yawRad = self.pose3d.getYaw() angle = -(yawRad + pi / 2) # PI/2 para centrar la aguja origx = self.rectangle.width() / 2 origy = self.rectangle.height() / 2 finx = radius * math.cos(angle) + origx finy = radius * math.sin(angle) + origy self.setStyle(painter, Qt.black, Qt.black, 3) painter.drawLine(QPoint(origx, origy), QPoint(finx, finy)) painter.drawEllipse(145, 145, 10, 10) def resetPen(self, painter): pen = QPen(Qt.black, 1) brush = QBrush() painter.setPen(pen) painter.setBrush(brush) def setStyle(self, painter, fillColor, penColor, stroke): brush = QBrush() pen = QPen(penColor, stroke) brush.setColor(fillColor) brush.setStyle(Qt.SolidPattern) painter.setBrush(brush) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) def paintEvent(self, event): painter = QPainter(self) self.drawRedZones(painter) self.drawOrangeZones(painter) self.drawGreenZones(painter) self.drawArrow(painter, 120) def updateG(self): self.update()
# self.setWidth(width) # self.setHeight(height) # # def dilate(self, nb_dilation=1): # self.erode(nb_erosion=-nb_dilation) if __name__ == '__main__': # ça marche --> voici deux examples de shapes test = Rect2D(0, 0, 100, 100) rect = QRectF(0, 0, 125, 256) print(rect.x()) print(rect.y()) print(rect.width()) print(rect.height()) rect.translate(10, 20) # ça marche print(rect) (test.x(), test.y(), test.width(), test.height()) print(test.contains(QPointF(50, 50))) print(test.contains(QPointF(-1, -1))) print(test.contains(QPointF(0, 0))) print(test.contains(QPointF(100, 100))) print(test.contains(QPointF(100, 100.1))) point = QPointF(50, 50) point.x() # p1 = test.p1()
class FloatingGradientItem(QGraphicsItem): def __init__(self, rect, finishCallback=None, parent=None): super(FloatingGradientItem, self).__init__(parent) self.__boundingRect = QRectF(0, 0, rect.width(), rect.height()) self.setPos(rect.x(), rect.y()) self.__front = 0.0 self.__rear = 0.0 self.__frontSpeed = 1.0 self.__rearSpeed = 0 self.__backwards = True self.__frontColor = QColor.fromRgb(0, 255, 0, 255) # defualt green self.__rearColor = QColor.fromRgb(0, 0, 0, 0) # default transparent self.__timer = QTimer() self.__timer.timeout.connect(self.__moveGradient) self.__timer.start(17) self.__finishCallback = finishCallback def setFrontSpeed(self, value): if value < 0 or value > 100: raise ValueError(f"value is {value}. Accepted [0, 100]") self.__frontSpeed = value def setRearSpeed(self, value): if value < 0 or value > 100: raise ValueError(f"value is {value}. Accepted [0, 100]") self.__rearSpeed = value def setBackwards(self, value): if not isinstance(value, bool): raise ValueError(f"value is not a bool") self.__backwards = value def setWidth(self, width): self.__boundingRect.setWidth(width) def setHeight(self, height): self.__boundingRect.setHeight(height) def setSize(self, width, height): self.setWidth(width) self.setHeight(height) def __moveGradient(self): self.__front += self.__frontSpeed if self.__front > 100: self.__front = 100 self.__rear += self.__rearSpeed if self.__rear > 100: self.__front = 0 self.__rear = 0 self.__rearSpeed = 0 self.__rearSpeed += 2 * self.__frontSpeed**2 / 100 self.update(self.__boundingRect) if self.__front < 0.00001 and self.__finishCallback: self.__finishCallback() def boundingRect(self): return self.__boundingRect def paint(self, painter, style_option, widget): frontPos = self.__boundingRect.height() * self.__front / 100 rearPos = self.__boundingRect.height() * self.__rear / 100 width = self.__boundingRect.width() height = self.__boundingRect.height() if self.__backwards: frontPos = height - frontPos rearPos = height - rearPos gradient = QLinearGradient(width / 2, frontPos, width / 2, rearPos) gradient.setColorAt(0, self.__frontColor) gradient.setColorAt(1, self.__rearColor) painter.fillRect(QRectF(0, rearPos, width, frontPos - rearPos), gradient)
def paint(self, painter): painter.setRenderHint(QPainter.Antialiasing) pixelRatio = self.devicePixelRatioF() rect = QRectF(0, 0, self.width() * pixelRatio, self.height() * pixelRatio) sz = QSizeF(self.width() * pixelRatio, self.height() * pixelRatio).toSize() self.resizePixmap(sz) yOffset = rect.toRect().topLeft().y() + (100 - self.value() - 10) * sz.height() / 100 # draw water waterImage = QImage(sz, QImage.Format_ARGB32_Premultiplied) waterPainter = QPainter() waterPainter.begin(waterImage) waterPainter.setRenderHint(QPainter.Antialiasing) waterPainter.setCompositionMode(QPainter.CompositionMode_Source) pointStart = QPointF(sz.width() / 2, 0) pointEnd = QPointF(sz.width() / 2, sz.height()) linear = QLinearGradient(pointStart, pointEnd) startColor = QColor('#1F08FF') startColor.setAlphaF(1) endColor = QColor('#50FFF7') endColor.setAlphaF(0.28) linear.setColorAt(0, startColor) linear.setColorAt(1, endColor) linear.setSpread(QGradient.PadSpread) waterPainter.setPen(Qt.NoPen) waterPainter.setBrush(linear) waterPainter.drawEllipse(waterImage.rect().center(), sz.width() / 2 + 1, sz.height() / 2 + 1) waterPainter.setCompositionMode(QPainter.CompositionMode_SourceOver) waterPainter.drawImage(int(self.backXOffset), yOffset, self.waterBackImage) waterPainter.drawImage( int(self.backXOffset) - self.waterBackImage.width(), yOffset, self.waterBackImage) waterPainter.drawImage(int(self.frontXOffset), yOffset, self.waterFrontImage) waterPainter.drawImage( int(self.frontXOffset) - self.waterFrontImage.width(), yOffset, self.waterFrontImage) # draw pop if self.value() > 30: for pop in self.pops: popPath = QPainterPath() popPath.addEllipse(pop.xOffset * sz.width() / 100, (100 - pop.yOffset) * sz.height() / 100, pop.size * sz.width() / 100, pop.size * sz.height() / 100) waterPainter.fillPath(popPath, QColor(255, 255, 255, 255 * 0.3)) if self.isTextVisible(): font = waterPainter.font() rectValue = QRect() progressText = self.text().strip('%') if progressText == '100': font.setPixelSize(sz.height() * 35 / 100) waterPainter.setFont(font) rectValue.setWidth(sz.width() * 60 / 100) rectValue.setHeight(sz.height() * 35 / 100) rectValue.moveCenter(rect.center().toPoint()) waterPainter.setPen(Qt.white) waterPainter.drawText(rectValue, Qt.AlignCenter, progressText) else: font.setPixelSize(sz.height() * 40 / 100) waterPainter.setFont(font) rectValue.setWidth(sz.width() * 45 / 100) rectValue.setHeight(sz.height() * 40 / 100) rectValue.moveCenter(rect.center().toPoint()) rectValue.moveLeft(rect.left() + rect.width() * 0.45 * 0.5) waterPainter.setPen(Qt.white) waterPainter.drawText(rectValue, Qt.AlignCenter, progressText) font.setPixelSize(font.pixelSize() / 2) waterPainter.setFont(font) rectPerent = QRect( QPoint(rectValue.right(), rectValue.bottom() - rect.height() * 20 / 100), QPoint(rectValue.right() + rect.width() * 20 / 100, rectValue.bottom())) waterPainter.drawText(rectPerent, Qt.AlignCenter, '%') waterPainter.end() maskPixmap = QPixmap(sz) maskPixmap.fill(Qt.transparent) path = QPainterPath() path.addEllipse(QRectF(0, 0, sz.width(), sz.height())) maskPainter = QPainter() maskPainter.begin(maskPixmap) maskPainter.setRenderHint(QPainter.Antialiasing) maskPainter.setPen(QPen(Qt.white, 1)) maskPainter.fillPath(path, QBrush(Qt.white)) maskPainter.end() mode = QPainter.CompositionMode_SourceIn contentImage = QImage(sz, QImage.Format_ARGB32_Premultiplied) contentPainter = QPainter() contentPainter.begin(contentImage) contentPainter.setCompositionMode(QPainter.CompositionMode_Source) contentPainter.fillRect(contentImage.rect(), Qt.transparent) contentPainter.setCompositionMode(QPainter.CompositionMode_SourceOver) contentPainter.drawImage(0, 0, maskPixmap.toImage()) contentPainter.setCompositionMode(mode) contentPainter.drawImage(0, 0, waterImage) contentPainter.setCompositionMode( QPainter.CompositionMode_DestinationOver) contentPainter.end() contentImage.setDevicePixelRatio(pixelRatio) painter.drawImage(self.rect(), contentImage)
class DisplaySpriteObject(QGraphicsItem): def __init__(self): super(DisplaySpriteObject, self).__init__() self._sprite = None self._displayFrameIndex = -1 self.prepareGeometryChange() self._boundingRect = QRectF() self._backgroundPixmap = None self._enableOnionSkin = True @property def sprite(self): return self._sprite @property def active_surface(self): return self._sprite.active_surface @property def active_surface_pixel_data(self): return self._sprite.active_surface_pixel_data @property def display_frame_index(self): return self._displayFrameIndex @display_frame_index.setter def display_frame_index(self, value): self._displayFrameIndex = value @property def background_pixmap(self): return self._backgroundPixmap @property def is_empty(self): return self._sprite is None @background_pixmap.setter def background_pixmap(self, value): self._backgroundPixmap = value def boundingRect(self): return self._boundingRect @property def bounding_rect_i(self): return QRect(self._boundingRect.left(), self._boundingRect.top(), self._boundingRect.width(), self._boundingRect.height()) @property def area_rect(self): return QRect(0, 0, self._boundingRect.width(), self._boundingRect.height()) @property def width(self): return self.boundingRect().width() @property def height(self): return self.boundingRect().height() @property def enable_onion_skin(self): return self._enableOnionSkin @enable_onion_skin.setter def enable_onion_skin(self, value): self._enableOnionSkin = value def set_sprite(self, sprite): self._sprite = sprite self.update_bounding_rect() def update_bounding_rect(self): if self._boundingRect.size != self._sprite.size: self.prepareGeometryChange() self._boundingRect = QRectF(-self._sprite.width / 2, -self._sprite.height / 2, self._sprite.width, self._sprite.height) def unload_sprite(self): self._sprite = None self._displayFrameIndex = -1 self.prepareGeometryChange() self._boundingRect = QRectF() def paint(self, painter, option, widget=None): painter.setClipRect(option.exposedRect) if self._backgroundPixmap is not None: painter.drawTiledPixmap(option.rect, self._backgroundPixmap) if self._sprite is not None: frame_index = self._displayFrameIndex \ if self._displayFrameIndex != -1 \ else self._sprite.current_animation.current_frame_index if self._enableOnionSkin: last_frame_index = frame_index - 1 if last_frame_index >= 0: last_frame_layers = self._sprite.current_animation. \ frame_at(last_frame_index).surfaces painter.setOpacity(0.2) for layer in last_frame_layers: painter.drawImage(option.rect, layer.image) painter.setOpacity(1.0) layers = self._sprite.current_animation.frame_at( frame_index).surfaces for layer in layers: painter.drawImage(option.rect, layer.image)
class timeAnalogWidget(QWidget): time = pyqtSignal() def __init__(self, winParent): super(timeAnalogWidget, self).__init__() self.winParent = winParent self.rectangle = QRectF(0.0, 0.0, 300.0, 300.0) self.angle = -pi / 2 self.angleMinutes = -pi / 2 self.seconds = 900 self.accountant = 0 self.minutes = 0 timer = QTimer(self) timer.start(1000) timer.timeout.connect(self.accountantTime) def drawWhiteZones(self, painter): self.setStyle(painter, QColor(255, 255, 255), QColor(255, 255, 255), 1) startAngle = 0 * 16 spanAngle = 360 * 16 painter.drawPie(self.rectangle, startAngle, spanAngle) def drawCLockLines(self, painter): radius = 130 angle = -pi / 2 for i in range(0, 60): origx = self.rectangle.width() / 2 + (radius - 1) * math.cos(angle) origy = self.rectangle.height() / 2 + (radius - 1) * math.sin(angle) finx = 6 * math.cos(angle) + origx finy = 6 * math.sin(angle) + origy self.setStyle(painter, Qt.black, Qt.black, 3) painter.drawLine(QPoint(origx, origy), QPoint(finx, finy)) angle = angle + (6 * pi / 180) def drawArrows(self, painter): radius = 130 origx = self.rectangle.width() / 2 origy = self.rectangle.height() / 2 finx = radius * math.cos(self.angle) + origx finy = radius * math.sin(self.angle) + origy finMinutesx = radius / 2 * math.cos(self.angleMinutes) + origx finMinutesy = radius / 2 * math.sin(self.angleMinutes) + origy self.setStyle(painter, Qt.black, Qt.black, 3) painter.drawLine(QPoint(origx, origy), QPoint(finx, finy)) painter.drawLine(QPoint(origx, origy), QPoint(finMinutesx, finMinutesy)) painter.drawEllipse(145, 145, 10, 10) def accountantTime(self): if self.accountant < self.seconds: self.accountant += 1 self.angle = self.angle + (6 * pi / 180) if self.accountant % 60 == 0: self.minutes += 1 self.angleMinutes = self.angleMinutes + (6 * pi / 180) def resetPen(self, painter): pen = QPen(Qt.black, 1) brush = QBrush() painter.setPen(pen) painter.setBrush(brush) def setStyle(self, painter, fillColor, penColor, stroke): brush = QBrush() pen = QPen(penColor, stroke) brush.setColor(fillColor) brush.setStyle(Qt.SolidPattern) painter.setBrush(brush) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) def paintEvent(self, event): painter = QPainter(self) self.drawWhiteZones(painter) self.drawArrows(painter) self.drawCLockLines(painter) def updateG(self): self.update()
class Node: width = 120 def __init__(self, icon: str): super(Node, self) self.windowRect = QRectF(0, 0, Node.width, 20) self.item = None self.inputs = [] self.icon = QPixmap(icon) def setPos(self, pos: QPointF): self.item.setPos(pos) def GetTitle(self): return "" def Inputs(self): return [] def Outputs(self): return [] def GetInputPoint(self, ids: int): ic = len(self.Inputs()) oc = len(self.Outputs()) m = max(ic, oc) * 10 io = 25 + m - ic * 10 + ids * 20 return self.item.pos() + QPointF(-5, io + 5) def GetOutputPoint(self, ids: int): ic = len(self.Inputs()) oc = len(self.Outputs()) m = max(ic, oc) * 10 io = 25 + m - oc * 10 + ids * 20 return self.item.pos() + QPointF(self.windowRect.width() + 5, io + 5) def GetInputId(self, pos: QPointF): pos = pos - self.item.pos() ic = len(self.Inputs()) oc = len(self.Outputs()) m = max(ic, oc) * 10 io = 20 + m - ic * 10 ids = int((pos.y() - io) / 20.0) if (ids < 0 or ids >= ic or pos.x() > self.windowRect.width() / 2): return -1 return ids def GetOutputId(self, pos: QPointF): pos = pos - self.item.pos() ic = len(self.Inputs()) oc = len(self.Outputs()) m = max(ic, oc) * 10 io = 20 + m - oc * 10 ids = int((pos.y() - io) / 20.0) if (ids < 0 or ids >= oc or pos.x() < self.windowRect.width() / 2): return -1 return ids def ItemsInit(self, scene: QGraphicsScene): ic = len(self.Inputs()) oc = len(self.Outputs()) m = max(ic, oc) * 10 self.windowRect.setHeight(m * 2 + 20) ins = self.Inputs() ino = 20 + m - ic * 10 ouo = 20 + m - oc * 10 pix = scene.addPixmap(self.icon) pix.setPos( (self.windowRect.width() - self.icon.width()) * 0.5, 20 + (self.windowRect.height() - 20 - self.icon.height()) * 0.5) pix.setParentItem(self.item) for i in range(0, ic): ti = scene.addText(ins[i]) ti.setPos(0, i * 20 + ino) ti.setParentItem(self.item) ie = scene.addEllipse( QRectF(QPointF(-5, i * 20 + 5 + ino), QSizeF(10, 10))) ie.setParentItem(self.item) ie.setFlag(QGraphicsItem.ItemNegativeZStacksBehindParent) ie.setZValue(-1) ins = self.Outputs() for i in range(0, oc): ti = scene.addText(ins[i]) ti.setPos( QPointF(self.windowRect.width() - ti.boundingRect().width(), i * 20 + ouo)) ti.setParentItem(self.item) ie = scene.addEllipse( QRectF(QPointF(self.windowRect.width() - 5, i * 20 + 5 + ouo), QSizeF(10, 10))) ie.setParentItem(self.item) ie.setFlag(QGraphicsItem.ItemNegativeZStacksBehindParent) ie.setZValue(-1) self.inputs = [] for i in range(0, ic): self.inputs.append(NodeConnector())
def paint(self, painter, option, index): model = index.model() tile = model.tileAt(index) if (not tile): return tileImage = tile.image() if self.mTilesetView.drawGrid(): _x = 1 else: _x = 0 extra = _x zoom = self.mTilesetView.scale() tileSize = tileImage.size() * zoom # Compute rectangle to draw the image in: bottom- and left-aligned targetRect = option.rect.adjusted(0, 0, -extra, -extra) targetRect.setTop(targetRect.bottom() - tileSize.height() + 1) targetRect.setRight(targetRect.left() + tileSize.width() - 1) # Draw the tile image zoomable = self.mTilesetView.zoomable() if zoomable: if (zoomable.smoothTransform()): painter.setRenderHint(QPainter.SmoothPixmapTransform) painter.drawPixmap(targetRect, tileImage) # Overlay with film strip when animated if (self.mTilesetView.markAnimatedTiles() and tile.isAnimated()): painter.save() scale = min(tileImage.width() / 32.0, tileImage.height() / 32.0) painter.setClipRect(targetRect) painter.translate(targetRect.right(), targetRect.bottom()) painter.scale(scale * zoom, scale * zoom) painter.translate(-18, 3) painter.rotate(-45) painter.setOpacity(0.8) strip = QRectF(0, 0, 32, 6) painter.fillRect(strip, Qt.black) painter.setRenderHint(QPainter.Antialiasing) painter.setBrush(Qt.white) painter.setPen(Qt.NoPen) hole = QRectF(0, 0, strip.height() * 0.6, strip.height() * 0.6) step = (strip.height() - hole.height()) + hole.width() margin = (strip.height() - hole.height()) / 2 x = (step - hole.width()) / 2 while x < strip.right(): hole.moveTo(x, margin) painter.drawRoundedRect(hole, 25, 25, Qt.RelativeSize) x += step painter.restore() # Overlay with highlight color when selected if (option.state & QStyle.State_Selected): opacity = painter.opacity() painter.setOpacity(0.5) painter.fillRect(targetRect, option.palette.highlight()) painter.setOpacity(opacity) if (self.mTilesetView.isEditTerrain()): terrain = tile.terrain() paintTerrainOverlay(painter, terrain, self.mTilesetView.terrainId(), targetRect, option.palette.highlight().color()) # Overlay with terrain corner indication when hovered if (index == self.mTilesetView.hoveredIndex()): pos = QPoint() x = self.mTilesetView.hoveredCorner() if x == 0: pos = targetRect.topLeft() elif x == 1: pos = targetRect.topRight() elif x == 2: pos = targetRect.bottomLeft() elif x == 3: pos = targetRect.bottomRight() painter.save() painter.setBrush(option.palette.highlight()) painter.setClipRect(targetRect) painter.setRenderHint(QPainter.Antialiasing) pen = QPen(option.palette.highlight().color().darker(), 2) painter.setPen(pen) painter.drawEllipse(pos, targetRect.width() / 3, targetRect.height() / 3) painter.restore()
def paint(self, painter, option, index): model = index.model() tile = model.tileAt(index) if (not tile): return tileImage = tile.image() if self.mTilesetView.drawGrid(): _x = 1 else: _x = 0 extra = _x zoom = self.mTilesetView.scale() tileSize = tileImage.size() * zoom # Compute rectangle to draw the image in: bottom- and left-aligned targetRect = option.rect.adjusted(0, 0, -extra, -extra) targetRect.setTop(targetRect.bottom() - tileSize.height() + 1) targetRect.setRight(targetRect.left() + tileSize.width() - 1) # Draw the tile image zoomable = self.mTilesetView.zoomable() if zoomable: if (zoomable.smoothTransform()): painter.setRenderHint(QPainter.SmoothPixmapTransform) painter.drawPixmap(targetRect, tileImage) # Overlay with film strip when animated if (self.mTilesetView.markAnimatedTiles() and tile.isAnimated()): painter.save() scale = min(tileImage.width() / 32.0, tileImage.height() / 32.0) painter.setClipRect(targetRect) painter.translate(targetRect.right(), targetRect.bottom()) painter.scale(scale * zoom, scale * zoom) painter.translate(-18, 3) painter.rotate(-45) painter.setOpacity(0.8) strip = QRectF(0, 0, 32, 6) painter.fillRect(strip, Qt.black) painter.setRenderHint(QPainter.Antialiasing) painter.setBrush(Qt.white) painter.setPen(Qt.NoPen) hole = QRectF(0, 0, strip.height() * 0.6, strip.height() * 0.6) step = (strip.height() - hole.height()) + hole.width() margin = (strip.height() - hole.height()) / 2 x = (step - hole.width()) / 2 while x < strip.right(): hole.moveTo(x, margin) painter.drawRoundedRect(hole, 25, 25, Qt.RelativeSize) x += step painter.restore() # Overlay with highlight color when selected if (option.state & QStyle.State_Selected): opacity = painter.opacity() painter.setOpacity(0.5) painter.fillRect(targetRect, option.palette.highlight()) painter.setOpacity(opacity) if (self.mTilesetView.isEditTerrain()): terrain = tile.terrain() paintTerrainOverlay(painter, terrain, self.mTilesetView.terrainId(), targetRect, option.palette.highlight().color()) # Overlay with terrain corner indication when hovered if (index == self.mTilesetView.hoveredIndex()): pos = QPoint() x = self.mTilesetView.hoveredCorner() if x==0: pos = targetRect.topLeft() elif x==1: pos = targetRect.topRight() elif x==2: pos = targetRect.bottomLeft() elif x==3: pos = targetRect.bottomRight() painter.save() painter.setBrush(option.palette.highlight()) painter.setClipRect(targetRect) painter.setRenderHint(QPainter.Antialiasing) pen = QPen(option.palette.highlight().color().darker(), 2) painter.setPen(pen) painter.drawEllipse(pos, targetRect.width() / 3, targetRect.height() / 3) painter.restore()
class MainWindow(QQuickWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self._background_color = QColor(204, 204, 204, 255) self.setClearBeforeRendering(False) self.beforeRendering.connect(self._render, type=Qt.DirectConnection) self._mouse_device = QtMouseDevice(self) self._mouse_device.setPluginId("qt_mouse") self._key_device = QtKeyDevice() self._key_device.setPluginId("qt_key") self._previous_focus = None # type: Optional["QQuickItem"] self._app = QCoreApplication.instance() # Remove previously added input devices (if any). This can happen if the window was re-loaded. self._app.getController().removeInputDevice("qt_mouse") self._app.getController().removeInputDevice("qt_key") self._app.getController().addInputDevice(self._mouse_device) self._app.getController().addInputDevice(self._key_device) self._app.getController().getScene().sceneChanged.connect( self._onSceneChanged) self._preferences = Application.getInstance().getPreferences() self._preferences.addPreference("general/window_width", 1280) self._preferences.addPreference("general/window_height", 720) self._preferences.addPreference("general/window_left", 50) self._preferences.addPreference("general/window_top", 50) self._preferences.addPreference("general/window_state", Qt.WindowNoState) # Restore window geometry self.setWidth(int(self._preferences.getValue("general/window_width"))) self.setHeight(int( self._preferences.getValue("general/window_height"))) self.setPosition( int(self._preferences.getValue("general/window_left")), int(self._preferences.getValue("general/window_top"))) # Make sure restored geometry is not outside the currently available screens screen_found = False for s in range(0, self._app.desktop().screenCount()): if self.geometry().intersects( self._app.desktop().availableGeometry(s)): screen_found = True break if not screen_found: self.setPosition(50, 50) self.setWindowState( int(self._preferences.getValue("general/window_state"))) self._mouse_x = 0 self._mouse_y = 0 self._mouse_pressed = False self._viewport_rect = QRectF(0, 0, 1.0, 1.0) self.closing.connect(self.preClosing) Application.getInstance().setMainWindow(self) self._fullscreen = False self._allow_resize = True # This event is triggered before hideEvent(self, event) event and might prevent window closing if # does not pass the check, for example if USB printer is printing # The implementation is in Cura.qml preClosing = pyqtSignal("QQuickCloseEvent*", arguments=["close"]) def setAllowResize(self, allow_resize: bool): if self._allow_resize != allow_resize: if not allow_resize: self.setMaximumHeight(self.height()) self.setMinimumHeight(self.height()) self.setMaximumWidth(self.width()) self.setMinimumWidth(self.width()) else: self.setMaximumHeight(16777215) self.setMinimumHeight(0) self.setMaximumWidth(16777215) self.setMinimumWidth(0) self._allow_resize = allow_resize @pyqtSlot() def toggleFullscreen(self): if self._fullscreen: self.setVisibility( QQuickWindow.Windowed) # Switch back to windowed else: self.setVisibility(QQuickWindow.FullScreen) # Go to fullscreen self._fullscreen = not self._fullscreen def getBackgroundColor(self): return self._background_color def setBackgroundColor(self, color): self._background_color = color self._app.getRenderer().setBackgroundColor(color) backgroundColor = pyqtProperty(QColor, fget=getBackgroundColor, fset=setBackgroundColor) mousePositionChanged = pyqtSignal() @pyqtProperty(int, notify=mousePositionChanged) def mouseX(self): return self._mouse_x @pyqtProperty(int, notify=mousePositionChanged) def mouseY(self): return self._mouse_y def setViewportRect(self, rect): if rect != self._viewport_rect: self._viewport_rect = rect self._updateViewportGeometry( self.width() * self.devicePixelRatio(), self.height() * self.devicePixelRatio()) self.viewportRectChanged.emit() viewportRectChanged = pyqtSignal() @pyqtProperty(QRectF, fset=setViewportRect, notify=viewportRectChanged) def viewportRect(self): return self._viewport_rect # Warning! Never reimplemented this as a QExposeEvent can cause a deadlock with QSGThreadedRender due to both trying # to claim the Python GIL. # def event(self, event): def mousePressEvent(self, event): super().mousePressEvent(event) if event.isAccepted(): return if self.activeFocusItem( ) is not None and self.activeFocusItem() != self._previous_focus: self.activeFocusItem().setFocus(False) self._previous_focus = self.activeFocusItem() self._mouse_device.handleEvent(event) self._mouse_pressed = True def mouseMoveEvent(self, event): self._mouse_x = event.x() self._mouse_y = event.y() if self._mouse_pressed: self.mousePositionChanged.emit() super().mouseMoveEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def mouseReleaseEvent(self, event): super().mouseReleaseEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) self._mouse_pressed = False def keyPressEvent(self, event): super().keyPressEvent(event) if event.isAccepted(): return self._key_device.handleEvent(event) def keyReleaseEvent(self, event): super().keyReleaseEvent(event) if event.isAccepted(): return self._key_device.handleEvent(event) def wheelEvent(self, event): super().wheelEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def moveEvent(self, event): QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection) def resizeEvent(self, event): super().resizeEvent(event) win_w = event.size().width() * self.devicePixelRatio() win_h = event.size().height() * self.devicePixelRatio() self._updateViewportGeometry(win_w, win_h) QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection) def hideEvent(self, event): if Application.getInstance().getMainWindow() == self: Application.getInstance().windowClosed() renderCompleted = Signal(type=Signal.Queued) def _render(self): renderer = self._app.getRenderer() view = self._app.getController().getActiveView() renderer.beginRendering() view.beginRendering() renderer.render() view.endRendering() renderer.endRendering() self.renderCompleted.emit() def _onSceneChanged(self, object): self.update() @pyqtSlot() def _onWindowGeometryChanged(self): if self.windowState() == Qt.WindowNoState: self._preferences.setValue("general/window_width", self.width()) self._preferences.setValue("general/window_height", self.height()) self._preferences.setValue("general/window_left", self.x()) self._preferences.setValue("general/window_top", self.y()) self._preferences.setValue("general/window_state", Qt.WindowNoState) elif self.windowState() == Qt.WindowMaximized: self._preferences.setValue("general/window_state", Qt.WindowMaximized) def _updateViewportGeometry(self, width: int, height: int): view_width = width * self._viewport_rect.width() view_height = height * self._viewport_rect.height() for camera in self._app.getController().getScene().getAllCameras(): camera.setWindowSize(width, height) if camera.getAutoAdjustViewPort(): camera.setViewportSize(view_width, view_height) projection_matrix = Matrix() if camera.isPerspective(): if view_width is not 0: projection_matrix.setPerspective( 30, view_width / view_height, 1, 500) else: projection_matrix.setOrtho(-view_width / 2, view_width / 2, -view_height / 2, view_height / 2, -500, 500) camera.setProjectionMatrix(projection_matrix) self._app.getRenderer().setViewportSize(view_width, view_height) self._app.getRenderer().setWindowSize(width, height)
class MainWindow(QQuickWindow): def __init__(self, parent = None): super(MainWindow, self).__init__(parent) self._background_color = QColor(204, 204, 204, 255) self.setClearBeforeRendering(False) self.beforeRendering.connect(self._render, type=Qt.DirectConnection) self._mouse_device = QtMouseDevice(self) self._mouse_device.setPluginId("qt_mouse") self._key_device = QtKeyDevice() self._key_device.setPluginId("qt_key") self._app = QCoreApplication.instance() self._app.getController().addInputDevice(self._mouse_device) self._app.getController().addInputDevice(self._key_device) self._app.getController().getScene().sceneChanged.connect(self._onSceneChanged) self._preferences = Preferences.getInstance() self._preferences.addPreference("general/window_width", 1280) self._preferences.addPreference("general/window_height", 720) self._preferences.addPreference("general/window_left", 50) self._preferences.addPreference("general/window_top", 50) self._preferences.addPreference("general/window_state", Qt.WindowNoState) # Restore window geometry self.setWidth(int(self._preferences.getValue("general/window_width"))) self.setHeight(int(self._preferences.getValue("general/window_height"))) self.setPosition(int(self._preferences.getValue("general/window_left")), int(self._preferences.getValue("general/window_top"))) # Make sure restored geometry is not outside the currently available screens if not self.geometry().intersects(self.screen().availableGeometry()): self.setPosition(50,50) self.setWindowState(int(self._preferences.getValue("general/window_state"))) self._mouse_x = 0 self._mouse_y = 0 self._viewport_rect = QRectF(0, 0, 1.0, 1.0) Application.getInstance().setMainWindow(self) self._fullscreen = False @pyqtSlot() def toggleFullscreen(self): if self._fullscreen: self.setVisibility(QQuickWindow.Windowed) # Switch back to windowed else: self.setVisibility(QQuickWindow.FullScreen) # Go to fullscreen self._fullscreen = not self._fullscreen def getBackgroundColor(self): return self._background_color def setBackgroundColor(self, color): self._background_color = color self._app.getRenderer().setBackgroundColor(color) backgroundColor = pyqtProperty(QColor, fget=getBackgroundColor, fset=setBackgroundColor) mousePositionChanged = pyqtSignal() @pyqtProperty(int, notify = mousePositionChanged) def mouseX(self): return self._mouse_x @pyqtProperty(int, notify = mousePositionChanged) def mouseY(self): return self._mouse_y def setViewportRect(self, rect): if rect != self._viewport_rect: self._viewport_rect = rect self._updateViewportGeometry(self.width() * self.devicePixelRatio(), self.height() * self.devicePixelRatio()) self.viewportRectChanged.emit() viewportRectChanged = pyqtSignal() @pyqtProperty(QRectF, fset = setViewportRect, notify = viewportRectChanged) def viewportRect(self): return self._viewport_rect # Warning! Never reimplemented this as a QExposeEvent can cause a deadlock with QSGThreadedRender due to both trying # to claim the Python GIL. # def event(self, event): def mousePressEvent(self, event): super().mousePressEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def mouseMoveEvent(self, event): self._mouse_x = event.x() self._mouse_y = event.y() self.mousePositionChanged.emit() super().mouseMoveEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def mouseReleaseEvent(self, event): super().mouseReleaseEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def keyPressEvent(self, event): super().keyPressEvent(event) if event.isAccepted(): return self._key_device.handleEvent(event) def keyReleaseEvent(self, event): super().keyReleaseEvent(event) if event.isAccepted(): return self._key_device.handleEvent(event) def wheelEvent(self, event): super().wheelEvent(event) if event.isAccepted(): return self._mouse_device.handleEvent(event) def moveEvent(self, event): QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection); def resizeEvent(self, event): super().resizeEvent(event) win_w = event.size().width() * self.devicePixelRatio() win_h = event.size().height() * self.devicePixelRatio() self._updateViewportGeometry(win_w, win_h) QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection); def hideEvent(self, event): Application.getInstance().windowClosed() def _render(self): renderer = self._app.getRenderer() view = self._app.getController().getActiveView() renderer.beginRendering() view.beginRendering() renderer.renderQueuedNodes() view.endRendering() renderer.endRendering() def _onSceneChanged(self, object): self.update() @pyqtSlot() def _onWindowGeometryChanged(self): if self.windowState() == Qt.WindowNoState: self._preferences.setValue("general/window_width", self.width()) self._preferences.setValue("general/window_height", self.height()) self._preferences.setValue("general/window_left", self.x()) self._preferences.setValue("general/window_top", self.y()) self._preferences.setValue("general/window_state", Qt.WindowNoState) elif self.windowState() == Qt.WindowMaximized: self._preferences.setValue("general/window_state", Qt.WindowMaximized) def _updateViewportGeometry(self, width, height): view_w = width * self._viewport_rect.width() view_h = height * self._viewport_rect.height() for camera in self._app.getController().getScene().getAllCameras(): camera.setViewportSize(view_w, view_h) proj = Matrix() if camera.isPerspective(): proj.setPerspective(30, view_w / view_h, 1, 500) else: proj.setOrtho(-view_w / 2, view_w / 2, -view_h / 2, view_h / 2, -500, 500) camera.setProjectionMatrix(proj) self._app.getRenderer().setViewportSize(view_w, view_h) self._app.getRenderer().setWindowSize(width, height)
def table(self, rect: QRectF, lineData: list=None, columnCount: int=0, rowCount:int=0, w: dict=None, h: dict=None, size:int=18, border:bool=True) -> None: #lineData: [{x:1,y:2,text:"hallo"}...] # oder [{x:[1,3],y:[2,4],text."hallo"}..] for id in range(len(lineData)): if not isinstance(lineData[id], dict): lineData[id] = {"text": "{}".format(lineData[id])} if w is None: w = dict() if h is None: h = dict() #Berechne Reservierte Größen reserved = {"w": 0, "h": 0} for wr in w.values(): reserved["w"] += wr for hr in h.values(): reserved["h"] += hr #Berechne Zellengrößen marginX = 5 marginY = 5 remCol = columnCount - len(w) remRow = rowCount - len(h) regularWidth = (rect.width() - reserved["w"] - (columnCount + 1) * marginX) / remCol regularHeight = (rect.height() - reserved["h"] - (rowCount + 1) * marginY) / remRow def calcCellUsage() -> list: index = 0 pos = [] for column in range(columnCount): l = list() for row in range(rowCount): l.append(None) pos.append(l) curX = 0 curY = 0 for i in lineData: if "x" in i.keys(): x = i["x"] if isinstance(x, int): x = range(x, x + 1) elif isinstance(x, tuple): x = range(x[0], x[-1] + 1) else: x = range(curX, curX + 1) i["x"] = x if "y" in i.keys(): y = i["y"] if isinstance(y, int): y = range(y, y + 1) elif isinstance(y, tuple): x = range(y[0], y[-1] + 1) else: y = range(curY, curY + 1) i["y"] = y curX = x[-1] + 1 curY = y[-1] if curX >= columnCount: curX = 0 curY += 1 for xx in x: for yy in y: pos[xx][yy] = index index += 1 return pos def calcWidth(col:int) -> float: try: if col in w.keys(): return w[col] except IndexError: pass return regularWidth def calcHeight(row:int) -> float: try: if row in h.keys(): return h[row] except IndexError: pass return regularHeight def calcCellSize(col:int, row: int) -> tuple: if col >= columnCount: raise IndexError if row >= rowCount: raise IndexError if pos[col][row] is None: raise IndexError width = calcWidth(col) height = calcHeight(row) id = pos[col][row] try: if pos[col + 1][row] is id: width += calcCellSize(col + 1, row)[0] + marginX except IndexError: pass try: if pos[col][row + 1] is id: height += calcCellSize(col, row + 1)[1] + marginY except IndexError: pass return width, height def calcCumulativeWidth(col: int) -> float: if col is 0: return marginX x = calcWidth(col - 1) + marginX + calcCumulativeWidth(col - 1) return x def calcCumulativeHeight(row: int) -> float: if row is 0: return marginY return calcHeight(row - 1) + marginY + calcCumulativeHeight(row - 1) def calcCell(index: int) -> tuple: x = lineData[index]["x"] if not isinstance(x, int): x = x[0] y = lineData[index]["y"] if not isinstance(y, int): y = y[0] left = rect.left() + calcCumulativeWidth(x) top = rect.top() + calcCumulativeHeight(y) d = calcCellSize(x, y) return left, top, d[0], d[1] def getFontOptions(index: int) -> dict: data = { "size": size, "color": self._fg, "shaded": False, "bold": False } elem = lineData[index] if "size" in elem.keys(): data["size"] = elem["size"] if "color" in elem.keys(): data["color"] = elem["color"] if "shaded" in elem.keys(): data["shaded"] = elem["shaded"] if "bold" in elem.keys(): data["bold"] = elem["bold"] return data if border: self.rect(rect, False) pos = calcCellUsage() for elem in range(len(lineData)): r = QRectF(*calcCell(elem)) if border: self.rect(r, False) self.text(lineData[elem]["text"], r, **getFontOptions(elem))
def drawMapObject(self, painter, object, color): painter.save() bounds = object.bounds() rect = QRectF(bounds) painter.translate(rect.topLeft()) rect.moveTopLeft(QPointF(0, 0)) cell = object.cell() if (not cell.isEmpty()): CellRenderer(painter).render(cell, QPointF(), object.size(), CellRenderer.BottomLeft) if (self.testFlag(RenderFlag.ShowTileObjectOutlines)): tile = cell.tile imgSize = tile.size() tileOffset = tile.offset() rect = QRectF(QPointF(tileOffset.x(), tileOffset.y() - imgSize.height()), QSizeF(imgSize)) pen = QPen(Qt.SolidLine) pen.setCosmetic(True) painter.setPen(pen) painter.drawRect(rect) pen.setStyle(Qt.DotLine) pen.setColor(color) painter.setPen(pen) painter.drawRect(rect) else: lineWidth = self.objectLineWidth() scale = self.painterScale() if lineWidth == 0: x = 1 else: x = lineWidth shadowDist = x / scale shadowOffset = QPointF(shadowDist * 0.5, shadowDist * 0.5) linePen = QPen(color, lineWidth, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) #linePen.setCosmetic(True) shadowPen = QPen(linePen) shadowPen.setColor(Qt.black) brushColor = QColor(color) fillBrush = QBrush(brushColor) painter.setRenderHint(QPainter.Antialiasing) # Trying to draw an ellipse with 0-width is causing a hang in # CoreGraphics when drawing the path requested by the # QCoreGraphicsPaintEngine. Draw them as rectangle instead. shape = object.shape() if (shape == MapObject.Ellipse and ((rect.width() == 0.0) ^ (rect.height() == 0.0))): shape = MapObject.Rectangle x = shape if x==MapObject.Rectangle: if (rect.isNull()): rect = QRectF(QPointF(-10, -10), QSizeF(20, 20)) # Draw the shadow painter.setPen(shadowPen) painter.drawRect(rect.translated(shadowOffset)) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawRect(rect) elif x==MapObject.Polyline: screenPolygon = self.pixelToScreenCoords_(object.polygon()) thickShadowPen = QPen(shadowPen) thickLinePen = QPen(linePen) thickShadowPen.setWidthF(thickShadowPen.widthF() * 4) thickLinePen.setWidthF(thickLinePen.widthF() * 4) painter.setPen(shadowPen) painter.drawPolyline(screenPolygon.translated(shadowOffset)) painter.setPen(thickShadowPen) painter.drawPoint(screenPolygon.first() + shadowOffset) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawPolyline(screenPolygon) painter.setPen(thickLinePen) painter.drawPoint(screenPolygon.first()) elif x==MapObject.Polygon: screenPolygon = self.pixelToScreenCoords_(object.polygon()) thickShadowPen = QPen(shadowPen) thickLinePen = QPen(linePen) thickShadowPen.setWidthF(thickShadowPen.widthF() * 4) thickLinePen.setWidthF(thickLinePen.widthF() * 4) painter.setPen(shadowPen) painter.drawPolygon(screenPolygon.translated(shadowOffset)) painter.setPen(thickShadowPen) painter.drawPoint(screenPolygon.first() + shadowOffset) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawPolygon(screenPolygon) painter.setPen(thickLinePen) painter.drawPoint(screenPolygon.first()) elif x==MapObject.Ellipse: if (rect.isNull()): rect = QRectF(QPointF(-10, -10), QSizeF(20, 20)) # Draw the shadow painter.setPen(shadowPen) painter.drawEllipse(rect.translated(shadowOffset)) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawEllipse(rect) painter.restore()
def interactiveResize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QPointF """ scene = self.scene() snap = scene.mainwindow.snapToGrid size = scene.GridSize offset = self.handleSize + self.handleMove moved = self.label.moved R = QRectF(self.boundingRect()) D = QPointF(0, 0) minBoundW = self.minwidth + offset * 2 minBoundH = self.minheight + offset * 2 self.prepareGeometryChange() if self.mousePressHandle == self.handleTL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.selection.setLeft(R.left()) self.selection.setTop(R.top()) self.background[self.indexT] = QPointF(R.left() + R.width() / 2, R.top()) self.background[self.indexB] = QPointF( R.left() + R.width() / 2, self.background[self.indexB].y()) self.background[self.indexL] = QPointF(R.left(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF(R.left(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.top() + R.height() / 2) self.polygon[self.indexT] = QPointF(R.left() + R.width() / 2, R.top() + offset) self.polygon[self.indexB] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexB].y()) self.polygon[self.indexL] = QPointF(R.left() + offset, R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(R.left() + offset, R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.top() + R.height() / 2) elif self.mousePressHandle == self.handleTM: fromY = self.mousePressBound.top() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, -offset, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.selection.setTop(R.top()) self.background[self.indexT] = QPointF( self.background[self.indexT].x(), R.top()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.top() + R.height() / 2) self.polygon[self.indexT] = QPointF(self.polygon[self.indexT].x(), R.top() + offset) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.top() + R.height() / 2) elif self.mousePressHandle == self.handleTR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.selection.setRight(R.right()) self.selection.setTop(R.top()) self.background[self.indexT] = QPointF(R.right() - R.width() / 2, R.top()) self.background[self.indexB] = QPointF( R.right() - R.width() / 2, self.background[self.indexB].y()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF(R.right(), R.top() + R.height() / 2) self.polygon[self.indexT] = QPointF(R.right() - R.width() / 2, R.top() + offset) self.polygon[self.indexB] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexB].y()) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(R.right() - offset, R.top() + R.height() / 2) elif self.mousePressHandle == self.handleML: fromX = self.mousePressBound.left() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, -offset, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) self.selection.setLeft(R.left()) self.background[self.indexL] = QPointF( R.left(), self.mousePressBound.top() + self.mousePressBound.height() / 2) self.background[self.indexE] = QPointF( R.left(), self.mousePressBound.top() + self.mousePressBound.height() / 2) self.background[self.indexT] = QPointF( R.left() + R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF( R.left() + R.width() / 2, self.background[self.indexB].y()) self.polygon[self.indexL] = QPointF( R.left() + offset, self.mousePressBound.top() + self.mousePressBound.height() / 2) self.polygon[self.indexE] = QPointF( R.left() + offset, self.mousePressBound.top() + self.mousePressBound.height() / 2) self.polygon[self.indexT] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexB].y()) elif self.mousePressHandle == self.handleMR: fromX = self.mousePressBound.right() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, +offset, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) self.selection.setRight(R.right()) self.background[self.indexR] = QPointF( R.right(), self.mousePressBound.top() + self.mousePressBound.height() / 2) self.background[self.indexT] = QPointF( R.right() - R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF( R.right() - R.width() / 2, self.background[self.indexB].y()) self.polygon[self.indexR] = QPointF( R.right() - offset, self.mousePressBound.top() + self.mousePressBound.height() / 2) self.polygon[self.indexT] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexB].y()) elif self.mousePressHandle == self.handleBL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.selection.setLeft(R.left()) self.selection.setBottom(R.bottom()) self.background[self.indexT] = QPointF( R.left() + R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF(R.left() + R.width() / 2, R.bottom()) self.background[self.indexL] = QPointF(R.left(), R.bottom() - R.height() / 2) self.background[self.indexE] = QPointF(R.left(), R.bottom() - R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.bottom() - R.height() / 2) self.polygon[self.indexT] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.left() + R.width() / 2, R.bottom() - offset) self.polygon[self.indexL] = QPointF(R.left() + offset, R.bottom() - R.height() / 2) self.polygon[self.indexE] = QPointF(R.left() + offset, R.bottom() - R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.bottom() - R.height() / 2) elif self.mousePressHandle == self.handleBM: fromY = self.mousePressBound.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, +offset, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.selection.setBottom(R.bottom()) self.background[self.indexB] = QPointF( self.background[self.indexB].x(), R.bottom()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.top() + R.height() / 2) self.polygon[self.indexB] = QPointF(self.polygon[self.indexB].x(), R.bottom() - offset) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.top() + R.height() / 2) elif self.mousePressHandle == self.handleBR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.selection.setRight(R.right()) self.selection.setBottom(R.bottom()) self.background[self.indexT] = QPointF( R.right() - R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF(R.right() - R.width() / 2, R.bottom()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.bottom() - R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.bottom() - R.height() / 2) self.background[self.indexR] = QPointF(R.right(), R.bottom() - R.height() / 2) self.polygon[self.indexT] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.right() - R.width() / 2, R.bottom() - offset) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.bottom() - R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.bottom() - R.height() / 2) self.polygon[self.indexR] = QPointF(R.right() - offset, R.bottom() - R.height() / 2) self.updateHandles() self.updateTextPos(moved=moved) self.updateAnchors(self.mousePressData, D)
class NodeItemRenderer: """ Class is used to separate the drawing from the logic. This class renders a given node to a graphics scene """ __blobConnectorSizeWithPadding = Constants.nodeItemConnectorPaddding * 2 + Constants.connectorItemOuterSize def __init__(self, nodeItem): self.__nodeItem = nodeItem # create lists to store TextRects for the node item self.__headRects = list() self.__blobTopNameRects = list() self.__blobBottomNameRects = list() # create Qt rects for the head and body area self.__rectHead = QRectF(0, 0, 1, 1) self.__rectBlobArea = QRectF(0, 0, 1, 1) # create Qt rect for enclosing rects used as bounding rects self.__rectAll = QRectF(0, 0, 1, 1) self.__rectAllSelected = QRectF(0, 0, 1, 1) self.__hasCurrentPhase = True self.update() def boundingRect(self): # selected layers have a outer 'glow', so selected layers are bigger than not selected layers if self.__nodeItem.isSelected(): return self.__rectAllSelected else: return self.__rectAll def update(self): """ Recalculates positions of name, type, blobs, etc. """ # clear lists del self.__headRects[:] del self.__blobTopNameRects[:] del self.__blobBottomNameRects[:] # get size of the name nameWidth = Constants.nodeItemFontNameMetrics.width( self.__nodeItem.getName()) + Constants.nodeItemHeadPadding nameHeight = Constants.nodeItemFontTypeMetrics.height() self.__headRects.append( TextRect(self.__nodeItem.getName(), QRectF(0, 0, nameWidth, nameHeight), Constants.nodeItemFontName)) # get size of the type typeWidth = Constants.nodeItemFontTypeMetrics.width( self.__nodeItem.getTypeString()) + Constants.nodeItemHeadPadding typeHeight = Constants.nodeItemFontTypeMetrics.height() self.__headRects.append( TextRect(self.__nodeItem.getTypeString(), QRectF(0, 0, typeWidth, typeHeight), Constants.nodeItemFontType)) # get size of the phase (if the node has the paramter include.phase if len(self.__nodeItem.getPhase()) > 0: phaseString = "Phase: " + self.__nodeItem.getPhase() phaseWidth = Constants.nodeItemFontTypeMetrics.width( phaseString) + Constants.nodeItemHeadPadding phaseHeight = Constants.nodeItemFontTypeMetrics.height() self.__headRects.append( TextRect(phaseString, QRectF(0, 0, phaseWidth, phaseHeight), Constants.nodeItemFontType)) # calculate rect width and height for names of blobs topBlobsInfo = self.__buildBlobNameRectList( self.__nodeItem.getTopConnectors()) bottomBlobsInfo = self.__buildBlobNameRectList( self.__nodeItem.getBottomConnectors()) self.__blobTopNameRects = topBlobsInfo[0] self.__blobBottomNameRects = bottomBlobsInfo[0] blobAreaWidth = 2 * self.__blobConnectorSizeWithPadding + Constants.nodeItemConnectorPaddding + \ bottomBlobsInfo[1] + topBlobsInfo[1] blobAreaHeight = max(topBlobsInfo[2], bottomBlobsInfo[2], Constants.nodeItemMinBlobAreaHeight) # calculate the total width of the node rectWidth = blobAreaWidth for textRect in self.__headRects: rectWidth = max(rectWidth, textRect.rect.width()) # change the width of the head rects to be the full width of the node self.__updateHeadRects(rectWidth, self.__headRects) # calculate the total node height rectHeight = self.__rectHead.bottom() + blobAreaHeight # create Qt rects for the body and enclosing width the top left at (0, 0) self.__rectBlobArea = QRectF(0, self.__rectHead.bottom(), rectWidth, blobAreaHeight) self.__rectAll = QRectF(0, 0, rectWidth, rectHeight) # calculate a larger box to support selection (outer glow) selSize = Constants.nodeItemSelectionSize self.__rectAllSelected = QRectF(self.__rectAll.left() - selSize, self.__rectAll.top() - selSize, self.__rectAll.width() + 2 * selSize, self.__rectAll.height() + 2 * selSize) # after calculating the total width, adjust the name rects to align at the left/right self.__adjustBlobNameRectPositions(self.__blobTopNameRects, self.__rectAll.width(), self.__rectHead.bottom(), False) self.__adjustBlobNameRectPositions(self.__blobBottomNameRects, self.__rectAll.width(), self.__rectHead.bottom(), True) def __updateHeadRects(self, finalWidth, headRects): """ Resizes the size of the head text rect after the total needed width was calculated """ totalHeadHeight = 0 for x in range(0, len(headRects)): # special case for the first head text -> start at (0, 0) and has space at the top if x == 0: headRects[x].rect = QRectF( 0, 0, finalWidth, headRects[x].rect.height() + Constants.nodeItemTextMargin) # special case for the last head text -> has space at the bottom elif x == len(headRects) - 1: headRects[x].rect = QRectF( 0, headRects[x - 1].rect.bottom(), finalWidth, headRects[x].rect.height() + Constants.nodeItemTextMargin) else: headRects[x].rect = QRectF(0, headRects[x - 1].rect.bottom(), finalWidth, headRects[x].rect.height()) # add the rects height to the total head rect height totalHeadHeight += headRects[x].rect.height() # create Qt rect for the head part self.__rectHead = QRectF(0, 0, finalWidth, totalHeadHeight) def __buildBlobNameRectList(self, connectors): """ Creates rects for all top/bottom connectors (bounding rect for the text only) """ # check if the connector or the blob name text is higher blobNameHeightRect = max(Constants.nodeItemFontBlobMetrics.height(), self.__blobConnectorSizeWithPadding) blobNameRectList = list() maxWidth = 0 height = 0 # for each connector calculate the width of the name and create a Qt rect. # calculate the max width of all blob names and the total height for item in connectors: blobName = item.getBlobName() blobNameWidth = Constants.nodeItemFontBlobMetrics.width( blobName) + Constants.nodeItemTextMargin blobRect = QRectF(0, 0, blobNameWidth, blobNameHeightRect) blobNameRectList.append(TextRect(blobName, blobRect)) maxWidth = max(maxWidth, blobNameWidth) height += blobNameHeightRect return blobNameRectList, maxWidth, height def __adjustBlobNameRectPositions(self, blobRectNames, totalWidth, aboveHeight, atLeftBorder): """ Recalculates the blob name rects after the total width of the node was calculated """ i = 0 for item in blobRectNames: # position at the left side of the node x = self.__blobConnectorSizeWithPadding # or position at the right side of the node if not atLeftBorder: x = totalWidth - self.__blobConnectorSizeWithPadding - item.rect.width( ) # calculate starting position (head size + position in body) item.rect = QRectF(x, aboveHeight + i * item.rect.height(), item.rect.width(), item.rect.height()) i += 1 def updateConnectorPositions(self, connectors, atLeftBorder): """ Repositions the connector items after the new size of the node was calculated """ i = 0 for item in connectors: # position the connector item at the left border of the node,left of the blob name rect x = Constants.nodeItemConnectorPaddding + Constants.connectorItemOuterSize / 2 # or at the right border of the node, right of the blob name rect if not atLeftBorder: x = self.__rectAll.width() - Constants.nodeItemConnectorPaddding - Constants.connectorItemOuterSize + \ Constants.connectorItemOuterSize / 2 # set the y coordinate of the connector item to be at the center of the blob name rect y = self.__rectHead.bottom() + Constants.nodeItemConnectorPaddding + Constants.connectorItemOuterSize / 2 + \ i * self.__blobConnectorSizeWithPadding item.setPos(QPointF(x, y)) i += 1 def paint(self, painter): # use antialiasing for both text and box painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) # get the opacity of the node item, layers with the include.phase parameter have a different opacity opacity = Constants.itemOpacityInPhase if not self.__nodeItem.getNodeEditor().isCurrentPhase( self.__nodeItem.getPhase()): opacity = Constants.itemOpacityNotInPhase # draw a outer 'glow' around the node item if the node is selected if self.__nodeItem.isSelected(): selectedColor = Constants.selectedColor selectedColor.setAlpha(opacity) painter.setPen(QPen(selectedColor, Constants.nodeItemSelectionSize)) painter.drawRect(self.__rectAllSelected) painter.fillRect(self.__rectAllSelected, QBrush(selectedColor)) # get the type color for the header (name and type) typeColor = LayerColorDefinitions.getTypeColor( self.__nodeItem.getType()) typeColor.setRgb(typeColor.red(), typeColor.green(), typeColor.blue(), opacity) # get background color backgroundColor = Constants.itemBackgroundColorLight backgroundColor.setAlpha(opacity) # set a linear gradient for the header (type color -> background color) gradient = QLinearGradient(0, self.__rectHead.top(), 0, self.__rectHead.bottom()) gradient.setColorAt(0, typeColor) gradient.setColorAt(0.5, backgroundColor) # draw background and border for the header painter.fillRect(self.__rectHead, QBrush(gradient)) # draw background for the blob area painter.fillRect(self.__rectBlobArea, QBrush(backgroundColor)) borderColor = Constants.itemBorderColor if self.__nodeItem.getIsInPlace(): borderColor = Constants.itemInPlaceColor borderColor.setAlpha(opacity) painter.setPen(QPen(borderColor, Constants.nodeItemBorderSize)) # draw outer border around the node painter.drawRect(self.__rectAll) # draw a line to separate header and connectors borSize = Constants.nodeItemBorderSize painter.setPen(QPen(borderColor, borSize)) painter.drawLine(self.__rectHead.left() + borSize / 2, self.__rectHead.bottom() - borSize / 2, self.__rectHead.right() - borSize / 2, self.__rectHead.bottom() - borSize / 2) painter.setPen(QPen(QColor(0, 0, 0, opacity))) # draw text of header if len(self.__headRects) > 1: # align the first head text at the bottom to provide some space at the top painter.setFont(self.__headRects[0].font) painter.drawText(self.__headRects[0].rect, Qt.AlignHCenter | Qt.AlignBottom, self.__headRects[0].text) # align other head texts at the center for i in range(1, len(self.__headRects) - 1): painter.setFont(self.__headRects[i].font) painter.drawText(self.__headRects[i].rect, Qt.AlignCenter, self.__headRects[i].text) # align the last head text at the top to provide some space at the bottom painter.setFont(self.__headRects[-1].font) painter.drawText(self.__headRects[-1].rect, Qt.AlignHCenter | Qt.AlignTop, self.__headRects[-1].text) # there is only one head text, so align it at the center elif len(self.__headRects) == 1: painter.setFont(self.__headRects[0].font) painter.drawText(self.__headRects[0].rect, Qt.AlignHCenter | Qt.AlignCenter, self.__headRects[0].text) # draw blob names painter.setFont(Constants.nodeItemFontBlob) for item in self.__blobBottomNameRects: painter.drawText(item.rect, Qt.AlignVCenter | Qt.AlignLeft, item.text) for item in self.__blobTopNameRects: painter.drawText(item.rect, Qt.AlignVCenter | Qt.AlignRight, item.text)