def uniqueItems(self): items = QGraphicsScene.items(self, order=Qt.SortOrder.DescendingOrder) itemList = [] for item in items: className = str(type(item)) if not className.find("PointItem") >= 0: itemList.append(item) return itemList
class UiVScreenOverview: def __init__(self, view, vscreen): """ :type view: PySide2.QtWidgets.QGraphicsView.QGraphicsView :param vscreen: hwmonitor.vscreen.vscreen.VScreen """ self.view = view self.vscreen = vscreen self.view_margin = 10 self.background_color = QColor(100, 100, 100) view.setInteractive(False) view.setStyleSheet('border: 0px;') view.setRenderHint(QPainter.Antialiasing) view.setCacheMode(QGraphicsView.CacheBackground) view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scene = QGraphicsScene(view) self.scene.setBackgroundBrush(self.background_color) for monitor in self.vscreen.monitors: item = MonitorRepresentation(index_from_device_name(monitor.device_name), monitor) item.update_position() self.scene.addItem(item) view.setScene(self.scene) def update_position(self): for item in self.scene.items(): item.update_position() def resize_scene(self): """Resize the scene to fit inside the available space of the viewport with an given margin. Update the scene rect, since it probably changes when changing the layout. """ rect = self.scene.itemsBoundingRect() self.view.setSceneRect(rect) view_rect = self.view.viewport().rect().adjusted(self.view_margin, self.view_margin, -self.view_margin, -self.view_margin) scene_rect = self.view.matrix().mapRect(rect) x_factor = view_rect.width() / scene_rect.width() y_factor = view_rect.height() / scene_rect.height() factor = min(x_factor, y_factor) self.view.scale(factor, factor) self.view.centerOn(rect.center())
class ItemRetrieve(UsesQApplication): '''Tests for QGraphicsScene item retrieval methods''' qapplication = True def setUp(self): #Acquire resources super(ItemRetrieve, self).setUp() self.scene = QGraphicsScene() self.topleft = QGraphicsRectItem(0, 0, 100, 100) self.topright = QGraphicsRectItem(100, 0, 100, 100) self.bottomleft = QGraphicsRectItem(0, 100, 100, 100) self.bottomright = QGraphicsRectItem(100, 100, 100, 100) self.items = [ self.topleft, self.topright, self.bottomleft, self.bottomright ] for item in self.items: self.scene.addItem(item) def tearDown(self): #Release resources del self.scene super(ItemRetrieve, self).tearDown() def testItems(self): #QGraphicsScene.items() items = self.scene.items() for i in items: self.assertTrue(i in self.items) def testItemAt(self): #QGraphicsScene.itemAt() self.assertEqual(self.scene.itemAt(50, 50, QTransform()), self.topleft) self.assertEqual(self.scene.itemAt(150, 50, QTransform()), self.topright) self.assertEqual(self.scene.itemAt(50, 150, QTransform()), self.bottomleft) self.assertEqual(self.scene.itemAt(150, 150, QTransform()), self.bottomright)
class ItemRetrieve(UsesQApplication): '''Tests for QGraphicsScene item retrieval methods''' qapplication = True def setUp(self): #Acquire resources super(ItemRetrieve, self).setUp() self.scene = QGraphicsScene() self.topleft = QGraphicsRectItem(0, 0, 100, 100) self.topright = QGraphicsRectItem(100, 0, 100, 100) self.bottomleft = QGraphicsRectItem(0, 100, 100, 100) self.bottomright = QGraphicsRectItem(100, 100, 100, 100) self.items = [self.topleft, self.topright, self.bottomleft, self.bottomright] for item in self.items: self.scene.addItem(item) def tearDown(self): #Release resources del self.scene super(ItemRetrieve, self).tearDown() def testItems(self): #QGraphicsScene.items() items = self.scene.items() for i in items: self.assertTrue(i in self.items) def testItemAt(self): #QGraphicsScene.itemAt() self.assertEqual(self.scene.itemAt(50, 50), self.topleft) self.assertEqual(self.scene.itemAt(150, 50), self.topright) self.assertEqual(self.scene.itemAt(50, 150), self.bottomleft) self.assertEqual(self.scene.itemAt(150, 150), self.bottomright)
class MyImageViewer(QMainWindow): def __init__(self, filename): super(MyImageViewer, self).__init__() self.filename = filename self.goTo = 0 self.frameIdx = 0 self.doZoom = False self.showOverlay = True self.imageFile = ImageFile(self.filename) self.initUI() self.updateUI() def prevFrame(self): if self.frameIdx > 0: self.frameIdx -= 1 self.updateUI() def nextFrame(self): if self.frameIdx < self.imageFile.frameMax: self.frameIdx += 1 self.updateUI() def firstFrame(self): self.frameIdx = 0 self.updateUI() def lastFrame(self): self.frameIdx = self.imageFile.frameMax self.updateUI() def toggleZoom(self): self.doZoom = not self.doZoom self.updateUI() def toggleOverlay(self): self.showOverlay = not self.showOverlay # don't recenter since we're not changing the frame self.updateUI(updateCenter=False) def keyPressEvent(self, e): # tab key toggles overlay if e.key() == 16777217: self.toggleOverlay() # enter key goes to number entered elif e.key() == 16777220: if self.goTo >= 0 and self.goTo <= self.imageFile.frameMax: self.frameIdx = self.goTo self.updateUI() self.goTo = 0 # ascii characters elif e.key() < 128: # number keys: store digits to build number of goto frame if chr(e.key()).isnumeric(): self.goTo = 10 * self.goTo + int(chr(e.key())) # z toggles zoom elif chr(e.key()) == 'Z': self.toggleZoom() else: #print("key:",e.key()) pass def updateUI(self, updateCenter=True): self.pixmap.setPixmap(self.getFramePixmap(self.frameIdx)) outlineBox = self.imageFile.box( self.imageFile.findCenterOfMass(self.frameIdx)) if self.boxRect: self.boxRect.setRect(*outlineBox) if self.doZoom: self.view.resetTransform() self.view.scale(2, 2) if updateCenter: self.view.centerOn( *self.imageFile.findCenterOfMass(self.frameIdx)) else: self.view.resetTransform() self.setWindowTitle( "%s... [%d/%d]" % (self.filename[:20], self.frameIdx, self.imageFile.frameMax)) def getFramePixmap(self, i): image = QtGui.QImage(self.imageFile.getFrame(i, self.showOverlay), self.imageFile.imgH, self.imageFile.imgW, QtGui.QImage.Format_ARGB32) return QtGui.QPixmap.fromImage(image) def initUI(self): self.scene = QGraphicsScene() self.pixmap = QGraphicsPixmapItem() self.scene.addItem(self.pixmap) self.boxRect = None # option to draw box around overlay spots if False: outlineBox = self.imageFile.box( self.imageFile.findCenterOfMass(self.frameIdx)) self.scene.addRect(QRect(*outlineBox), pen=QtGui.QPen(QtCore.Qt.blue, 1)) self.boxRect = self.scene.items()[ 0] # not the best way to get rect self.view = MyGraphicsView(self) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setScene(self.scene) self.setCentralWidget(self.view) controlDock = QDockWidget() controlDock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.addDockWidget(Qt.TopDockWidgetArea, controlDock) main_layout = QHBoxLayout() main_layout.setContentsMargins(0, 0, 0, 0) # first frame button firstButton = QPushButton('<<', self) firstButton.clicked.connect(self.firstFrame) main_layout.addWidget(firstButton) # previous frame button prevButton = QPushButton('<', self) prevButton.clicked.connect(self.prevFrame) main_layout.addWidget(prevButton) # next frame button nextButton = QPushButton('>', self) nextButton.clicked.connect(self.nextFrame) main_layout.addWidget(nextButton) # last frame button lastButton = QPushButton('>>', self) lastButton.clicked.connect(self.lastFrame) main_layout.addWidget(lastButton) # zoom button zoomButton = QPushButton('zoom', self) zoomButton.clicked.connect(self.toggleZoom) main_layout.addWidget(zoomButton) # zoom button overlayButton = QPushButton('color', self) overlayButton.clicked.connect(self.toggleOverlay) main_layout.addWidget(overlayButton) # add dock widget dockContainerWidget = QWidget(controlDock) dockContainerWidget.setLayout(main_layout) dockContainerWidget.setGeometry(0, 0, self.imageFile.imgW, 20) self.setFixedSize(self.imageFile.imgW, self.imageFile.imgH + dockContainerWidget.height()) self.show()
class DynamicView(QGraphicsView): viewChanged = Signal(QRect, float, int, int) def __init__(self, image, parent=None): super(DynamicView, self).__init__(parent) self.scene = QGraphicsScene() self.scene.setBackgroundBrush(Qt.darkGray) self.setScene(self.scene) self.set_image(image) self.setRenderHint(QPainter.SmoothPixmapTransform) self.setDragMode(QGraphicsView.ScrollHandDrag) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.ZOOM_STEP = 0.2 self.mouse_pressed = False self.next_fit = False self.fit_scale = 0 self.zoom_fit() def set_image(self, image): if type(image) is QPixmap: pixmap = image elif type(image) is QImage: pixmap = QPixmap.fromImage(image) elif type(image) is np.ndarray: pixmap = QPixmap.fromImage(mat2img(image)) else: raise TypeError( self.tr('DynamicView.set_image: Unsupported type: {}'.format( type(image)))) if not self.scene.items(): self.scene.addPixmap(pixmap) else: self.scene.items()[0].setPixmap(pixmap) self.scene.setSceneRect(QRectF(pixmap.rect())) def zoom_full(self): self.set_scaling(1) self.next_fit = True self.notify_change() def zoom_fit(self): self.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) self.fit_scale = self.matrix().m11() if self.fit_scale > 1: self.fit_scale = 1 self.zoom_full() else: self.next_fit = False self.notify_change() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.mouse_pressed = True QGraphicsView.mousePressEvent(self, event) def mouseMoveEvent(self, event): QGraphicsView.mouseMoveEvent(self, event) if self.mouse_pressed: self.notify_change() def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: self.mouse_pressed = False QGraphicsView.mouseReleaseEvent(self, event) def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton: if self.next_fit: self.zoom_fit() else: self.zoom_full() QGraphicsView.mouseDoubleClickEvent(self, event) def wheelEvent(self, event): if event.delta() > 0: self.change_zoom(+1) else: self.change_zoom(-1) def resizeEvent(self, event): # FIXME: Se la finestra viene massimizzata, il valore di fit_scale non si aggiorna if self.matrix().m11() <= self.fit_scale: self.zoom_fit() else: self.notify_change() QGraphicsView.resizeEvent(self, event) def change_zoom(self, direction): level = math.log2(self.matrix().m11()) if direction > 0: level += self.ZOOM_STEP else: level -= self.ZOOM_STEP scaling = 2**level if scaling < self.fit_scale: scaling = self.fit_scale self.next_fit = False elif scaling > 1: # scaling = 1 if scaling > 4: scaling = 4 self.next_fit = True self.set_scaling(scaling) self.notify_change() def set_scaling(self, scaling): matrix = QMatrix() matrix.scale(scaling, scaling) self.setMatrix(matrix) def change_view(self, _, new_scaling, new_horiz, new_vert): old_factor = self.matrix().m11() old_horiz = self.horizontalScrollBar().value() old_vert = self.verticalScrollBar().value() if new_scaling != old_factor or new_horiz != old_horiz or new_vert != old_vert: self.set_scaling(new_scaling) self.horizontalScrollBar().setValue(new_horiz) self.verticalScrollBar().setValue(new_vert) self.notify_change() def notify_change(self): scene_rect = self.get_rect() horiz_scroll = self.horizontalScrollBar().value() vert_scroll = self.verticalScrollBar().value() zoom_factor = self.matrix().m11() self.viewChanged.emit(scene_rect, zoom_factor, horiz_scroll, vert_scroll) def get_rect(self): top_left = self.mapToScene(0, 0).toPoint() if top_left.x() < 0: top_left.setX(0) if top_left.y() < 0: top_left.setY(0) view_size = self.viewport().size() bottom_right = self.mapToScene(view_size.width(), view_size.height()).toPoint() image_size = self.sceneRect().toRect() if bottom_right.x() >= image_size.width(): bottom_right.setX(image_size.width() - 1) if bottom_right.y() >= image_size.height(): bottom_right.setY(image_size.height() - 1) return QRect(top_left, bottom_right)
class CanvasGraphicsView(QGraphicsView): onSelection = Signal(PickNode) requestEditMode = Signal(bool) def __init__(self, parent=None): super(CanvasGraphicsView, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) # Scene properties self.setAcceptDrops(True) self.setMouseTracking(True) self.setRenderHint(QPainter.Antialiasing) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setBackgroundBrush(QBrush(QColor(51, 51, 51))) self.setFrameShape(QFrame.NoFrame) self.setDragMode(QGraphicsView.RubberBandDrag) self.ViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate) self.init() def init(self): self.piiPath = str() self._model = {'background': str()} self._isPanning = False self._isZooming = False self._mousePressed = False self._scene = QGraphicsScene() self._scene.selectionChanged.connect(self.update_node_settings) self._backgroundNode = QGraphicsPixmapItem() self._scene.addItem(self._backgroundNode) self._orderSelected = list() self._lastPos = QPoint(0, 0) self.editMode = False self._namespace = str() self._dragMulti = list() self._defaultColor = QColor(255, 255, 255) self._defaultTextColor = QColor(0, 0, 0) self._defaultTextSize = 20 self._defaultText = "New Node" self.workHight = 2160 self.workWidth = 4096 self.setScene(self._scene) self.setContextMenuPolicy(Qt.CustomContextMenu) self.setBackgroundImage(str()) def update_node_settings(self): if self._orderSelected: node = self._orderSelected[-1] self._defaultText = node.toPlainText() self._defaultColor = node.Background self._defaultTextColor = node.defaultTextColor() self._defaultTextSize = node.font().pointSize() def update_maya_selection(self): ''' Update Maya Scene base on active selection. ''' clearSelection() selection = list() for each in self._orderSelected: selection += each.Items if selection: selectObjects(selection) def setBackgroundImage(self, path=str()): ''' Set background image Parameters ---------- path: (str) Path to background image. ''' self._model['background'] = path self.setStatusTip(self._model['background']) pixmap = QPixmap(self._model['background']) self._backgroundNode.setPixmap(pixmap) def getBackgroundImage(self): ''' Get background image ''' return self._model['background'] BackgroundImage = property(getBackgroundImage, setBackgroundImage) def actionMenu(self, QPos): ''' Show action menu. Parameters ---------- QPos: (list) list of x and y location. ''' self.mainMenu = QMenu() add_action = self.mainMenu.addAction('Add A Button') add_action.setEnabled(self.editMode) add_action.triggered.connect(self.add_node) addMany_action = self.mainMenu.addAction('Add Many Buttons') addMany_action.setEnabled(self.editMode) addMany_action.triggered.connect(self.add_multiple_nodes) activeNode = self.mouse_on_node() if activeNode: update_action = self.mainMenu.addAction('Update Button') update_action.setEnabled(self.editMode) update_action.triggered.connect( lambda: self.update_node(activeNode)) delete_action = self.mainMenu.addAction('Delete Button') delete_action.setEnabled(self.editMode) delete_action.setShortcut('Backspace') delete_action.triggered.connect(self.removeSelected) self.mainMenu.addSeparator() # search for selected ButtonNode btnStatus = [ isinstance(n, ButtonNode) for n in self._scene.selectedItems() ] if True in btnStatus: # first ButtonNode activeNode = self._scene.selectedItems()[btnStatus.index(True)] command_action = self.mainMenu.addAction('Edit Command Button...') command_action.setEnabled(self.editMode) command_action.triggered.connect( lambda: self.update_ButtonNode(activeNode)) else: command_action = self.mainMenu.addAction('add Command Button...') command_action.setEnabled(self.editMode) command_action.triggered.connect(self.add_commands) self.mainMenu.addSeparator() reset_action = self.mainMenu.addAction('Reset View') reset_action.setShortcut('H') reset_action.triggered.connect(self.reset_view) frame_action = self.mainMenu.addAction('Frame View') frame_action.setShortcut('F') frame_action.triggered.connect(self.frame_view) self.mainMenu.addSeparator() alignGrp = QMenu('Align') self.mainMenu.addMenu(alignGrp) hac_action = alignGrp.addAction('Horizontal Align Center') hac_action.setIcon(QIconSVG('h_align-01')) hac_action.setEnabled(self.editMode) hac_action.triggered.connect(self.align_horizontal) vac_action = alignGrp.addAction('Vertical Align Center') vac_action.setIcon(QIconSVG('v_align-01')) vac_action.setEnabled(self.editMode) vac_action.triggered.connect(self.align_vertical) hd_action = alignGrp.addAction('Horizontal Distribute') hd_action.setIcon(QIconSVG('h_d_align-01')) hd_action.setEnabled(self.editMode) hd_action.triggered.connect(self.align_horizontal_distribute) vd_action = alignGrp.addAction('Vertical Distribute') vd_action.setIcon(QIconSVG('v_d_align-01')) vd_action.setEnabled(self.editMode) vd_action.triggered.connect(self.align_vertical_distribute) alignGrp.addSeparator() ins_action = alignGrp.addAction('Increase Size') ins_action.setShortcut('+') ins_action.setEnabled(self.editMode) ins_action.triggered.connect(self.increase_size) dis_action = alignGrp.addAction('Decrease Size') dis_action.setShortcut('-') dis_action.setEnabled(self.editMode) dis_action.triggered.connect(self.decrease_size) self.mainMenu.addSeparator() edit_mode = self.mainMenu.addAction('Edit Mode') edit_mode.setCheckable(True) edit_mode.setChecked(self.editMode) edit_mode.triggered.connect( lambda: self.request_edit(not self.editMode)) pos = self.mapToGlobal(QPoint(0, 0)) self.mainMenu.move(pos + QPos) self.mainMenu.show() def mouse_on_node(self): globPosition = self.mapFromGlobal(QCursor.pos()) scenePosition = self.mapToScene(globPosition) for node in self._scene.items(): if isinstance(node, PickNode): if node.mapRectToScene( node.boundingRect()).contains(scenePosition): return node return None def update_node(self, node=PickNode): ''' Update the Node selection base on selection in maya. ''' mayaScene = getActiveItems() # for each in self._scene.selectedItems(): node.Items = mayaScene def update_ButtonNode(self, node=ButtonNode): ''' Update the ButtonNode commands. Parameters ---------- node: (ButtonNode) ButtonNode Node. ''' self.newCommand = CommandDialog(text=node.toPlainText(), cmd=node.Command, cmdType=node.CommandsType) if self.newCommand.exec_() == QDialog.Accepted: data = self.newCommand.Raw node.setPlainText(data[PIIButton.TEXT]) node.Command = data[PIIButton.COMMAND] node.CommandsType = data[PIIButton.COMMANDTYPE] def add_commands(self): ''' Create a new ButtonNode with Commands. ''' globPosition = self.mapFromGlobal(QCursor.pos()) scenePosition = self.mapToScene(globPosition) self.newCommand = CommandDialog() if self.newCommand.exec_() == QDialog.Accepted: data = self.newCommand.Raw self.create_button(position=scenePosition, text=data[PIIButton.TEXT], size=self._defaultTextSize, textColor=self._defaultTextColor, bgColor=self._defaultColor, cmd=data[PIIButton.COMMAND], cmdType=data[PIIButton.COMMANDTYPE]) def align_horizontal(self): ''' Align the selection to center horizontally. ''' selected = self._scene.selectedItems() if len(selected) > 1: minValue = selected[0].y() maxValue = selected[0].y() whole = None for each in selected: y = each.y() value = y + each.boundingRect().height() # finding lowest value minValue = y if y < minValue else minValue minValue = value if value < minValue else minValue # finding highest value maxValue = y if y > maxValue else maxValue maxValue = value if value > maxValue else maxValue total = maxValue - minValue if total != 0: middle = (maxValue + minValue) / 2 for each in selected: center = each.shape().boundingRect().center() start_y = each.y() offset = start_y + center.y() - middle each.setY(each.y() - offset) def align_vertical(self): ''' Align the selection to center vertically. ''' selected = self._scene.selectedItems() if len(selected) > 1: # sort it based on x position + width selected = sorted(selected, key=lambda x: x.x() + x.boundingRect().width()) leftNode = selected[0] rightNode = selected[-1] # total length of x axis total = rightNode.boundingRect().width() + rightNode.x( ) - leftNode.x() if total != 0: middle = (total / 2) + leftNode.x() for each in selected: center = each.shape().boundingRect().center() start_x = each.x() offset = start_x + center.x() - middle each.setX(each.x() - offset) def align_horizontal_distribute(self): ''' Disturbute the selected nodes evenly between first node on the left and last node on the right horizontally. ''' selected = self._scene.selectedItems() if len(selected) > 2: # sort it based on x position + width selected = sorted(selected, key=lambda x: x.x() + x.boundingRect().width()) startItem = selected.pop(0) endItem = selected.pop(-1) # total length of items itemsLength = int() for each in selected: itemsLength += each.boundingRect().width() startPoint = startItem.x() + startItem.boundingRect().width() total = endItem.x() - startPoint section_num = len(selected) + 1 extraSpace = total - itemsLength # nicly divide if extraSpace > 0: gap = extraSpace / section_num nextPlace = startPoint for each in selected: newLoc = nextPlace + gap nextPlace += gap + each.boundingRect().width() each.setX(newLoc) else: total = endItem.x() - startPoint gap = total / section_num nextPlace = startPoint for each in selected: nextPlace += gap each.setX(nextPlace) else: errorMes("PUPPETMASTER-INFO: Select more than 2 nodes.") def align_vertical_distribute(self): ''' Disturbute the selected nodes evenly between first node on the top and last node on the bottom vertically. ''' selected = self._scene.selectedItems() if len(selected) > 2: # sort it based on y position + width selected = sorted( selected, key=lambda node: node.y() + node.boundingRect().height()) startItem = selected.pop(0) endItem = selected.pop(-1) # total length of items itemsLength = int() for each in selected: itemsLength += each.boundingRect().height() startPoint = startItem.y() + startItem.boundingRect().height() total = endItem.y() - startPoint section_num = len(selected) + 1 extraSpace = total - itemsLength # nicly divide if extraSpace > 0: gap = extraSpace / section_num nextPlace = startPoint for each in selected: newLoc = nextPlace + gap nextPlace += gap + each.boundingRect().height() each.setY(newLoc) else: total = endItem.y() - startPoint gap = total / section_num nextPlace = startPoint for each in selected: nextPlace += gap each.setY(nextPlace) else: errorMes("PUPPETMASTER-INFO: Select more than 2 nodes.") def reset_view(self): ''' Fit all the items to the view. ''' items = self._scene.items() if items: rects = [ item.mapToScene(item.boundingRect()).boundingRect() for item in items ] rect = self.min_bounding_rect(rects) self._scene.setSceneRect(rect) self.fitInView(rect, Qt.KeepAspectRatio) def frame_view(self): ''' Fit selected items to the view. ''' items = self._scene.selectedItems() if items: rects = [ item.mapToScene(item.boundingRect()).boundingRect() for item in items ] rect = self.min_bounding_rect(rects) self.fitInView(rect, Qt.KeepAspectRatio) def fit_contents(self): ''' Update the scene boundery. ''' items = self._scene.items() if items: rects = [ item.mapToScene(item.boundingRect()).boundingRect() for item in items ] rect = self.min_bounding_rect(rects) self._scene.setSceneRect(rect) def request_edit(self, value=bool): self.requestEditMode.emit(value) def min_bounding_rect(self, rectList=list()): ''' Get the minimum boundry based on objects in the scene. Parameters ---------- rectList: (list) List of QRectF (boundry of objects) Return ------ out: (QRectF) Get the minimum boundry ''' minX = rectList[0].left() minY = rectList[0].top() maxX = rectList[0].right() maxY = rectList[0].bottom() for k in range(1, len(rectList)): minX = min(minX, rectList[k].left()) minY = min(minY, rectList[k].top()) maxX = max(maxX, rectList[k].right()) maxY = max(maxY, rectList[k].bottom()) return QRectF(minX, minY, maxX - minX, maxY - minY) def increase_size(self): ''' Increase the size of selected items by 1 unit. ''' selected = self._scene.selectedItems() for each in selected: font = each.font() fontSize = font.pointSize() if fontSize < 99: fontSize += 1 font.setPointSize(fontSize) each.setFont(font) def decrease_size(self): ''' Decrease the size of selected items by 1 unit. ''' selected = self._scene.selectedItems() for each in selected: font = each.font() fontSize = font.pointSize() if fontSize > 1: fontSize -= 1 font.setPointSize(fontSize) each.setFont(font) def is_texture(self, path=str): ''' Check if the texture path is valid. Return ------ out: (bool) True if texture is valide, otherwise False. ''' if path.lower().endswith(IMAGE_FORMATS): return True return False def _QMimeDataToFile(self, data=QMimeData): ''' Get all the filepath from drag file. Parameters ---------- data: (QMimeData) QMimeData of dragged file. ''' files = list() if data.hasUrls: for each in data.urls(): files.append(each.toLocalFile()) return files def _is_dragValid(self, event): ''' Check for draged file validation ''' dragedItems = self._QMimeDataToFile(event.mimeData()) if dragedItems: first_path = dragedItems[0] if self.is_texture(first_path) and self.editMode: return True return False def dragEnterEvent(self, event): event.accept() if self._is_dragValid(event) else event.ignore() def dragMoveEvent(self, event): event.accept() if self._is_dragValid(event) else event.ignore() def dropEvent(self, event): dragedItems = self._QMimeDataToFile(event.mimeData()) if dragedItems: first_path = dragedItems[0] if self.is_texture(first_path): self.setBackgroundImage(path=first_path) event.accept() else: event.ignore() def mousePressEvent(self, event): self._lastPos = event.pos() self._lastScenePos = self.mapToScene(event.pos()) if self._dragMulti: for each in self._dragMulti: each.setSelected(True) self._dragMulti = list() if event.button() == Qt.MiddleButton: self._isPanning = True self.setCursor(QPixmap(iconSVG('nav-pan-02'))) self._dragPos = event.pos() event.accept() elif event.button() == Qt.RightButton: if event.modifiers() == Qt.AltModifier: self._isZooming = True self.setCursor(QPixmap(iconSVG('nav-zoom-02'))) self._dragPos = event.pos() self._dragPos2 = self.mapToScene(event.pos()) else: self.actionMenu(event.pos()) event.accept() else: super(CanvasGraphicsView, self).mousePressEvent(event) def mouseMoveEvent(self, event): if self._dragMulti and len(self._dragMulti) > 1: start = self._lastScenePos end = self.mapToScene(event.pos()) total = len(self._dragMulti) - 1 xLength = start.x() - end.x() yLength = start.y() - end.y() xStep = 0 if xLength == 0 else -(xLength / total) yStep = 0 if yLength == 0 else -(yLength / total) num = 0 for each in self._dragMulti: position = QPointF(start.x() + (num * xStep), start.y() + (num * yStep)) each.setPos(position) num += 1 if self._isPanning: 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() elif self._isZooming: newPos = event.pos() diff = newPos - self._dragPos self._dragPos = newPos factor = 1.000 if diff.x() < 0: factor = 0.98 else: factor = 1.02 self.scale(factor, factor) event.accept() else: if event.modifiers() == Qt.ShiftModifier: diff = event.pos() - self._lastPos x = event.x() if abs(diff.x()) > abs( diff.y()) else self._lastPos.x() y = event.y() if abs(diff.y()) > abs( diff.x()) else self._lastPos.y() event = QMouseEvent(QEvent.MouseMove, QPoint(x, y), self.mapToGlobal(QPoint(x, y)), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) super(CanvasGraphicsView, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): self._isPanning = False self._isZooming = False self.setCursor(Qt.ArrowCursor) super(CanvasGraphicsView, self).mouseReleaseEvent(event) self.fit_contents() self.update_maya_selection() def keyPressEvent(self, event): if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: if self.editMode: self.removeSelected() elif event.key() == Qt.Key_Plus: if self.editMode: self.increase_size() elif event.key() == Qt.Key_Minus: if self.editMode: self.decrease_size() elif event.key() == Qt.Key_H: self.reset_view() elif event.key() == Qt.Key_F: self.frame_view() else: super(CanvasGraphicsView, self).keyPressEvent(event) def removeSelected(self): ''' Remove selected Items. ''' for each in self._scene.selectedItems(): self._scene.removeItem(each) self.remove_stack(each) def wheelEvent(self, event): factor = 1.05 if event.delta() < 0: # factor = .2 / factor factor = 0.95 self.scale(factor, factor) self.update() def add_node(self): ''' Add a new PickNode to the scene. ''' # Cursor Position on Scene globPosition = self.mapFromGlobal(QCursor.pos()) scenePosition = self.mapToScene(globPosition) self.create_node(text=self._defaultText, size=self._defaultTextSize, textColor=self._defaultTextColor, bgColor=self._defaultColor, position=scenePosition, items=getActiveItems(), shape=PickShape.SQUARE) def add_multiple_nodes(self): ''' Add group of PickNode bellow each other to the scene. ''' # Cursor Position on Scene globPosition = self.mapFromGlobal(QCursor.pos()) scenePosition = self.mapToScene(globPosition) self._dragMulti = list() for each in getActiveItems(): node = self.create_node(text=self._defaultText, size=self._defaultTextSize, textColor=self._defaultTextColor, bgColor=self._defaultColor, position=scenePosition, items=[each], shape=PickShape.SQUARE) self._dragMulti.append(node) # scenePosition = QPointF(scenePosition.x(), node.y() + node.boundingRect().height() + 5) def create_node(self, position=list, text=str, size=int, textColor=QColor, bgColor=QColor, items=list, shape=PickShape.SQUARE): ''' Create a new PickNode. Parameters ---------- position: (list) List of x and y location. text: (str) Name of the text. size: (int) Size of the text. textColor: (QColor) Color of the text. bgColor: (QColor) Background Color of the node. items: (list) List of selected Maya object. Return ------ out: (PickNode) Reference of created Node. ''' textNode = PickNode() font = QFont("SansSerif", size) font.setStyleHint(QFont.Helvetica) textNode.setFont(font) textNode.setDefaultTextColor(textColor) textNode.setFlag(QGraphicsItem.ItemIsMovable, self.editMode) textNode.setFlag(QGraphicsItem.ItemIsSelectable) # textNode.setFlag(QGraphicsItem.ItemIsFocusable, self.editMode) textNode.Background = bgColor textNode.Items = items textNode.Shape = shape textNode.onSelected.connect(lambda: self.onSelection.emit(textNode)) textNode.onAddToStack.connect(lambda: self.add_stack(textNode)) textNode.onRemoveFromStack.connect(lambda: self.remove_stack(textNode)) textNode.setPos(position) textNode.setPlainText(text) self._scene.addItem(textNode) return textNode def create_button(self, position=list, text=str, size=int, textColor=QColor, bgColor=QColor, cmd=str, cmdType=str): ''' Create a new ButtonNode. Parameters ---------- position: (list) List of x and y location. text: (str) Name of the text. size: (int) Size of the text. textColor: (QColor) Color of the text. bgColor: (QColor) Background Color of the node. cmd: (str) Command to run when it's pressed. cmdType: (str) Type of command.("python"/"mel") ''' btnNode = ButtonNode() font = QFont("SansSerif", size) font.setStyleHint(QFont.Helvetica) btnNode.setFont(font) btnNode.setDefaultTextColor(textColor) btnNode.setFlag(QGraphicsItem.ItemIsMovable, self.editMode) btnNode.setFlag(QGraphicsItem.ItemIsSelectable) btnNode.Background = bgColor btnNode.CommandsType = cmdType btnNode.Command = cmd # btnNode.onSelected.connect(lambda: self.onSelection.emit(textNode)) btnNode.onSelected.connect(lambda: self.onSelection.emit(btnNode)) btnNode.onClicked.connect(self.scriptJob) btnNode.setPos(position) btnNode.setPlainText(text) self._scene.addItem(btnNode) def scriptJob(self, cmdType=str, cmd=str): ''' Run a command. Parameters ---------- cmd: (str) Command to run. cmdType: (str) Type of command.("python"/"mel") ''' if not self.editMode: if cmdType == CommandType.PYTHON: runPython(cmd) elif cmdType == CommandType.MEL: runMel(cmd) def add_stack(self, node=PickNode): ''' Add a node selection in right order into the stack. Parameters ---------- node: (PickNode) Selected node. ''' self._orderSelected.append(node) def remove_stack(self, node=PickNode): ''' Remove a node from the stack. Parameters ---------- node: (PickNode) Selected node. ''' if node in self._orderSelected: index = self._orderSelected.index(node) self._orderSelected.pop(index) def get_edit(self): return self.editMode def set_edit(self, value=bool): self.editMode = value for each in self._scene.items(): if type(each) == PickNode: each.setFlag(QGraphicsItem.ItemIsMovable, self.editMode) elif type(each) == ButtonNode: each.setFlag(QGraphicsItem.ItemIsMovable, self.editMode) Edit = property(get_edit, set_edit) def get_path(self): return self.piiPath def set_path(self, path=str): self.piiPath = path Path = property(get_path, set_path) def get_raw(self): ''' Get the scene information. (can be be save in .pii) Return ------ out: (dict) Dictionary of scene date to be save in .pii file. ''' image_data = str() pixmap = self._backgroundNode.pixmap() # Extract Image Data if not pixmap.isNull(): buffer = QBuffer() buffer.open(QIODevice.WriteOnly) pixmap.save(buffer, "PNG") # Image Data image_data = bytes(buffer.data().toBase64()).decode('ascii') nodeList = [] for each in self._scene.items(): if type(each) == PickNode: textColor = each.defaultTextColor() bgColor = each.Background item = { PIIPick.TYPE: PIINode.PICK, PIIPick.TEXT: each.toPlainText(), PIIPick.SIZE: each.font().pointSize(), PIIPick.POSITION: (each.pos().x(), each.pos().y()), PIIPick.COLOR: (textColor.red(), textColor.green(), textColor.blue()), PIIPick.BACKGROUND: (bgColor.red(), bgColor.green(), bgColor.blue()), PIIPick.SELECTION: each.Items, PIIPick.SHAPE: each.Shape } nodeList.append(item) elif type(each) == ButtonNode: textColor = each.defaultTextColor() bgColor = each.Background item = { PIIButton.TYPE: PIINode.BUTTON, PIIButton.TEXT: each.toPlainText(), PIIButton.SIZE: each.font().pointSize(), PIIButton.POSITION: (each.pos().x(), each.pos().y()), PIIButton.COLOR: (textColor.red(), textColor.green(), textColor.blue()), PIIButton.BACKGROUND: (bgColor.red(), bgColor.green(), bgColor.blue()), PIIButton.COMMAND: each.Command, PIIButton.COMMANDTYPE: each.CommandsType } nodeList.append(item) rawData = { PII.VERSION: "1.0.0", PII.BACKGROUND: image_data, PII.NODES: nodeList } return rawData def set_raw(self, data=dict): ''' set the scene information. (information from .pii) Parameters ---------- data: (dict) Dictionary of date from .pii file. ''' if data: if data[PII.VERSION] == "1.0.0": self.load_1_0_0(data) Raw = property(get_raw, set_raw) def get_namespace(self): ''' Get namespace of all PickNode. Return ------ out: (list) List of namespaces. ''' namespaceList = [] for each in self._scene.items(): if type(each) == PickNode: valueList = each.Items for sObj in valueList: if ":" in sObj: group = sObj.split(":")[:-1] for index in range(len(group)): namespaceList.append(":".join(group[:index + 1])) return list(set(namespaceList)) def set_namespace(self, data=dict): ''' Set namespace of all PickNode. Parameters ---------- data: (dict) Dictionary of namespace with value of new namespace. ''' for each in self._scene.items(): if type(each) == PickNode: valueList = each.Items newValue = list() for sObj in valueList: if ":" in sObj: # namesapce nameS = ":".join(sObj.split(":")[:-1]) # object name object_name = sObj.split(":")[-1] keys = data.keys() keys.sort(reverse=True) for key in keys: if key in nameS: nameS = nameS.replace(key, data[key], 1) # making sure doesn't start with ':' nameS = nameS[1:] if nameS.startswith(":") else nameS # add the object to namespace nameS = ":".join([nameS, object_name ]) if nameS else object_name newValue.append(nameS) else: newValue.append(sObj) each.Items = newValue Namespace = property(get_namespace, set_namespace) def get_NSHistory(self): return self._namespace def set_NSHistory(self, name=str): self._namespace = name NamespaceHistory = property(get_NSHistory, set_NSHistory) def get_highlight(self): return def set_highlight(self, data=list): if data: for each in self._scene.items(): # QApplication.processEvents() if type(each) == PickNode: for item in data: if item in each.Items: each.Highlight = True break else: each.Highlight = False else: for each in self._scene.items(): if type(each) == PickNode: each.Highlight = False Highlight = property(get_highlight, set_highlight) def clear_scene(self): ''' Clear the scene. ''' self._orderSelected = list() self._scene.clear() self._backgroundNode = QGraphicsPixmapItem() self._scene.addItem(self._backgroundNode) self.reset_view() def is_changed(self): ''' Check for the scene changes. ''' if self._backgroundNode.pixmap(): return True elif len(self._scene.items()) > 1: return True return False def load_1_0_0(self, data=dict): ''' Load v1.0.0 of .pii version file. Parameters ---------- data: (dict) Dictionary of date from .pii file. ''' if data[PII.BACKGROUND]: # Import Image Data newPix = QPixmap() newPix.loadFromData( QByteArray.fromBase64(data[PII.BACKGROUND].encode('ascii')), "PNG") self._backgroundNode.setPixmap(newPix) for each in data[PII.NODES]: if each["type"] == PIINode.PICK: self.create_node(text=each[PIIPick.TEXT], size=each[PIIPick.SIZE], textColor=QColor(*each[PIIPick.COLOR]), bgColor=QColor(*each[PIIPick.BACKGROUND]), position=QPointF(*each[PIIPick.POSITION]), items=each[PIIPick.SELECTION], shape=each[PIIPick.SHAPE]) elif each["type"] == PIINode.BUTTON: self.create_button(position=QPointF(*each[PIIButton.POSITION]), text=each[PIIButton.TEXT], size=each[PIIButton.SIZE], textColor=QColor(*each[PIIButton.COLOR]), bgColor=QColor(*each[PIIButton.BACKGROUND]), cmd=each[PIIButton.COMMAND], cmdType=each[PIIButton.COMMANDTYPE]) def set_nodes_bg_color(self, color=QColor): ''' Set background color of selected nodes. Parameters ---------- color: (QColor) QColor value. ''' self._defaultColor = color for each in self._scene.selectedItems(): each.Background = color self.update() def set_nodes_font_color(self, color=QColor): ''' Set font color of selected nodes. Parameters ---------- color: (QColor) QColor value. ''' self._defaultTextColor = color for each in self._scene.selectedItems(): each.setDefaultTextColor(color) def set_nodes_font_size(self, size=int): ''' Set font size of selected nodes. Parameters ---------- size: (int) font size. ''' self._defaultTextSize = size for each in self._scene.selectedItems(): font = each.font() font.setPointSize(size) each.setFont(font) def set_nodes_text(self, text=str): ''' Set text for selected nodes. Parameters ---------- text: (str) text for the node. ''' self._defaultText = text for each in self._scene.selectedItems(): each.setPlainText(text) def set_nodes_shape(self, shape=str): ''' Set shape for selected nodes. Parameters ---------- shape: (str) name for the shape. ''' for each in self._scene.selectedItems(): if isinstance(each, PickNode): each.Shape = shape
def allGraphicsSceneItems( scene: QtWidgets.QGraphicsScene) -> T.List[QtWidgets.QGraphicsItem]: """return all items in scene""" return scene.items()
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("My App") # self.label = QLabel() # image = QPixmap("test_images/002.png") # label = QPixmap("test_masks/002.png") # canvas = QPixmap(512,512) # # image = QImage("test_images/002.png") # label = QImage("test_masks/002.png") # # painter = QPainter() # painter.begin(canvas) # painter.drawImage(QPoint(0,0), label) # painter.drawImage(QPoint(0,0), image) # painter.end() # # self.label.setPixmap(canvas) # self.setCentralWidget(self.label) # self.scene = QGraphicsScene(0, 0, 400, 200) pixmap = QPixmap("test_images/002.png") pixmap_item = self.scene.addPixmap(pixmap) pixmap_item.setPos(0, 0) view = View(self.scene) view.setRenderHint(QPainter.RenderHint.Antialiasing) self.view = view # self.view.setAlignment(Qt.AlignLeft) # self.view.setAlignment(Qt.AlignTop) self.view.setMinimumWidth(512) self.view.setMinimumHeight(512) self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.view.ensureVisible(pixmap_item, 0, 0) btn = QPushButton("button") btn.clicked.connect(self.list_items()) hbox = QHBoxLayout(self) hbox.addWidget(view) hbox.addWidget(btn) widget = QWidget() widget.setLayout(hbox) self.setCentralWidget(widget) def list_items(self): print(self.scene.items()) item = self.scene.items()[0] pos = item.pos() x, y = pos.x(), pos.y() item.setPos(x + 10, x + 10)