def markToMoveSignal(self, part=None): if not isinstance(self.assistHandle, LicPlacementAssistant): self.releaseAssist() if self.assistHandle is None: self.assistHandle = LicPlacementAssistant(self.views()[0]) self.assistHandle.setItemtoMove(part)
class LicGraphicsScene(QGraphicsScene): PageViewContinuous = -1 PageViewContinuousFacing = -2 hasMargins = False _staticGuides = [] _crossGuides = [] _selected = [] _assist = None _catchTheMouse = False def __init__(self, parent): QGraphicsScene.__init__(self, parent) self.setBackgroundBrush(Qt.gray) self.reset() def __getCatchTheMouse(self): return self._catchTheMouse def __setCatchTheMouse(self, state): self._catchTheMouse = state if state: self.saveSelection() else: self.restoreSelection() catchTheMouse = property(__getCatchTheMouse,__setCatchTheMouse) def reset(self): self.scaleFactor = 1.0 self.pagesToDisplay = 1 self.currentPage = None self.pages = [] self.selectedSubmodels = [] self.guides = [] self.xSnapLine = self.createSnapLine() self.ySnapLine = self.createSnapLine() self.snapToGuides = True self.snapToItems = True self.renderMode = 'full' # Or "background" or "foreground" self.guide1v = None self.guide2v = None self.guide1h = None self.guide2h = None def markToMove(self, part=None): if self._assist is None: self._assist = LicPlacementAssistant( self.views()[0] ) self._assist.setItemtoMove(part) def createSnapLine(self): snapLine = QGraphicsLineItem() pen = QPen(Qt.darkCyan) pen.setWidth(2) snapLine.setPen(pen) snapLine.setZValue(10000) # Put on top of everything else snapLine.hide() self.addItem(snapLine) return snapLine def saveSelection(self): self._selected = list(self.selectedItems()) def restoreSelection(self): if [] != self._selected: for item in self._selected: item.setSelected(True) def clearSelection(self): self.clearSelectedParts() self.selectedSubmodels = [] QGraphicsScene.clearSelection(self) def clearSelectedParts(self): partList = [] for item in self.selectedItems(): if isinstance(item, Part): partList.append(item) if partList: for part in partList[:-1]: part.setSelected(False, False) partList[-1].setSelected(False, True) def clear(self): QGraphicsScene.clear(self) self.reset() if self.views().__len__() > 0: self.views()[0].resetTransform() self.update() def drawForeground(self, painter, rect): """ In general, you should, and in fact mostly cannot paint outside paint events (if you really really need this, you have to enable painting outside of the paintEvent by setting the flag Qt::WA_PaintOutsidePaintEvent. This is highly discouraged, though!). If you want to paint in the QGraphiscView, You should reimplement QGraphicsView::paintEvent(). However, the correct solution is to use one of the two dedicated functions in QGraphicsView that allow you to paint behind all items in the background, or as overlay on top of all items: virtual void drawBackground ( QPainter * painter, const QRectF & rect ) virtual void drawForeground ( QPainter * painter, const QRectF & rect ) Please look in the Qt Assistant documentation in QGraphicsView for further details. """ if self.items().__len__() > 2: return QGraphicsScene.drawForeground(self, painter, rect) else: painter.fillRect(rect,Qt.gray) pt = rect.bottomRight() painter.drawPixmap(pt[0]-512,pt[1]-64,QPixmap(":/emmet")) def drawOneItem(self, painter, item, option, widget): painter.save() painter.setMatrix(item.sceneMatrix(), True) item.paint(painter, option, widget) painter.restore() def drawItems(self, painter, items, options, widget): LicGLHelpers.clear([0.62, 0.62, 0.65, 1.0]) # First draw all items that are not annotations if self.renderMode == 'full' or self.renderMode == 'background': for i, item in enumerate(items): if item.isVisible() and (not hasattr(item, 'isAnnotation') or not item.isAnnotation): self.drawOneItem(painter, item, options[i], widget) if widget and self.renderMode == 'full': # Build list of pages to be drawn (if any) rect = QRectF(self.views()[0].mapToScene(QPoint()), QSizeF(widget.size()) / self.scaleFactor) pagesToDraw = [] for page in self.pages: if page.isVisible() and rect.intersects(page.rect().translated(page.pos())): pagesToDraw.append(page) if pagesToDraw: # Setup the GL items to be drawn & the necessary context painter.beginNativePainting() LicGLHelpers.initFreshContext(False) # Draw all GL items for page in pagesToDraw: page.drawGLItems(rect) LicGLHelpers.setupForQtPainter() # Reset all GL lighting, so that subsequent drawing is not affected painter.endNativePainting() # Draw all annotation if self.renderMode == 'full' or self.renderMode == 'foreground': for i, item in enumerate(items): if item.isVisible() and (hasattr(item, 'isAnnotation') and item.isAnnotation): self.drawOneItem(painter, item, options[i], widget) def pageUp(self): self.clearSelection() if self.pages and self.currentPage: self.selectPageFullUpdate(max(self.currentPage._number - 1, self.pages[0]._number)) def pageDown(self): self.clearSelection() if self.pages and self.currentPage: self.selectPageFullUpdate(min(self.pages[-1]._number, self.currentPage._number + 1)) def pageCount(self): """ Return only the number of pages with Construction Step Image [CSI]. Exclude template page, title page and part list pages. """ count = 0 for page in self.pages: if page.data(Qt.WhatsThisRole) == "Page": count += 1 return count def selectFirstPage(self): if self.pages: self.selectPageFullUpdate(1) def selectLastPage(self): if self.pages: self.selectPageFullUpdate(self.pages[-1]._number) def selectCurrentPage(self): if self.currentPage: self.selectPageFullUpdate(self.currentPage._number) def selectNextPart(self): """ Focus on next|first sibling part """ choosen = csi = None for item in self.selectedItems(): if isinstance(item, (Step,CSI,Part)): choosen = item if isinstance(choosen, Step): csi = choosen.csi choosen = csi.getPartList()[0] if isinstance(choosen, CSI): csi = choosen choosen = csi.getPartList()[0] if isinstance(choosen, Part): csi = choosen.getCSI() lst = csi.getPartList() try: idx = lst.index(choosen) except ValueError: idx = 0 else: idx += 1 if idx > lst.__len__() -1: idx = 0 choosen = lst[idx] if csi and choosen: csi.selectPart(choosen) return def selectPageFullUpdate(self, pageNumber): self.selectPage(pageNumber) self.currentPage.setSelected(True) self.emit(SIGNAL("sceneClick")) def refreshView(self): self.setPagesToDisplay(self.pagesToDisplay) def selectPage(self, pageNumber): # Don't call currentPage.setSelected() from here! Must be done later for page in self.pages: if self.pagesToDisplay == 1 and page._number == pageNumber: page.setPos(0, 0) page.show() self.currentPage = page elif self.pagesToDisplay == 2: if pageNumber % 2: # draw odd pages on right if page._number == pageNumber: page.setPos(Page.PageSize.width() + 20, 0) page.show() self.currentPage = page elif page._number == pageNumber - 1: page.show() page.setPos(10, 0) else: page.hide() page.setPos(0, 0) else: # draw even pages on left if page._number == pageNumber: page.setPos(10, 0) page.show() self.currentPage = page elif page._number == pageNumber + 1: page.setPos(Page.PageSize.width() + 20, 0) page.show() else: page.hide() page.setPos(0, 0) elif self.pagesToDisplay == self.PageViewContinuous or self.pagesToDisplay == self.PageViewContinuousFacing: if page._number == pageNumber: self.currentPage = page else: page.hide() page.setPos(0, 0) self.scrollToPage(self.currentPage) def selectionChangedHandler(self): selList = self.selectedItems() if self.pagesToDisplay == 1 or not selList or isinstance(selList[-1], Guide): return self.scrollToPage(selList[-1].getPage()) def fullItemSelectionUpdate(self, *itemList): self.clearSelection() for item in itemList: item.setSelected(True) self.emit(SIGNAL("sceneClick")) def scrollToPage(self, page): if page is None: return view = self.views()[0] view.setInteractive(False) view.centerOn(page) view.setInteractive(True) self.currentPage = page def showOnePage(self): self.pagesToDisplay = 1 self.setSceneRect(0, 0, Page.PageSize.width(), Page.PageSize.height()) self.maximizeGuides(Page.PageSize.width(), Page.PageSize.height()) for page in self.pages: page.hide() page.setPos(0.0, 0.0) self.selectCurrentPage() def showTwoPages(self): if len(self.pages) < 2: return self.showOnePage() self.pagesToDisplay = 2 self.setSceneRect(0, 0, (Page.PageSize.width() * 2) + 30, Page.PageSize.height() + 20) self.maximizeGuides(Page.PageSize.width() * 2, Page.PageSize.height()) for page in self.pages: page.hide() page.setPos(0, 0) index = self.pages.index(self.currentPage) if self.currentPage == self.pages[-1]: p1 = self.pages[index - 1] p2 = self.currentPage else: p1 = self.currentPage p2 = self.pages[index + 1] p1.setPos(10, 0) p1.show() p2.setPos(Page.PageSize.width() + 20, 0) p2.show() self.selectCurrentPage() def continuous(self): self.pagesToDisplay = self.PageViewContinuous pc = max(len(self.pages), 1) ph = Page.PageSize.height() height = (10 * (pc + 1)) + (ph * pc) self.setSceneRect(0, 0, Page.PageSize.width() + 20, height) self.maximizeGuides(0, height) for i, page in enumerate(self.pages): page.setPos(10, (10 * (i + 1)) + (ph * i)) page.show() self.selectCurrentPage() def continuousFacing(self): if len(self.pages) < 3: return self.continuous() self.pagesToDisplay = self.PageViewContinuousFacing pw = Page.PageSize.width() ph = Page.PageSize.height() rows = sum(divmod(len(self.pages) - 1, 2)) + 1 width = pw + pw + 30 height = (10 * (rows + 1)) + (ph * rows) self.setSceneRect(0, 0, width, height) self.maximizeGuides(width, height) self.pages[0].setPos(10, 10) # Template page first self.pages[0].show() for i, page in enumerate(self.pages[1:]): i += 2 x = 10 + ((pw + 10) * (i % 2)) y = (10 * ((i // 2) + 1)) + (ph * (i // 2)) page.setPos(x, y) page.show() self.selectCurrentPage() def setPagesToDisplay(self, pagesToDisplay): if pagesToDisplay == self.PageViewContinuous: return self.continuous() if pagesToDisplay == self.PageViewContinuousFacing: return self.continuousFacing() if pagesToDisplay == 2: return self.showTwoPages() return self.showOnePage() def addItem(self, item): QGraphicsScene.addItem(self, item) if isinstance(item, Page): self.pages.append(item) self.pages.sort(key = lambda x: x._number) self.setPagesToDisplay(self.pagesToDisplay) def sortPages(self): self.pages.sort(key = lambda x: x._number) def removeItem(self, item): self.emit(SIGNAL("itemDeleted"), item) QGraphicsScene.removeItem(self, item) if not isinstance(item, Page): return if isinstance(item, Page) and item in self.pages: self.pages.remove(item) if self.pagesToDisplay == self.PageViewContinuous: self.continuous() elif self.pagesToDisplay == self.PageViewContinuousFacing: self.continuousFacing() def removeBlankPages(self): stack = self.undoStack stack.beginMacro("Remove blank pages") for page in self.pages: if page.isEmpty(): stack.push(AddRemovePageCommand(page.scene() ,page ,False)) stack.endMacro() def removeAllGuides(self): self.undoStack.beginMacro("Remove all guides") for guide in list(self.guides): self.undoStack.push(LicUndoActions.AddRemoveGuideCommand(self, guide, False)) self.undoStack.endMacro() def addGuide(self, orientation, pos): guide = Guide(orientation, self) guide.setPos(pos) self.guides.append(guide) self.addItem(guide) def addNewGuide(self, orientation): self.undoStack.push(LicUndoActions.AddRemoveGuideCommand(self, Guide(orientation, self), True)) def showHideMargins(self): if not isinstance(self.guide1v, FixedGuide): self.guide1v = FixedGuide(LicLayout.Vertical, self) if not isinstance(self.guide2v, FixedGuide): self.guide2v = FixedGuide(LicLayout.Vertical, self) if not isinstance(self.guide1h, FixedGuide): self.guide1h = FixedGuide(LicLayout.Horizontal, self) if not isinstance(self.guide2h, FixedGuide): self.guide2h = FixedGuide(LicLayout.Horizontal, self) self._staticGuides = [self.guide1v,self.guide2h,self.guide2v,self.guide1h] if self.hasMargins: for g in self._staticGuides: g.hide() if not self.hasMargins: self.guide1v.setPos(QPointF(LicLayout.PageDefaultMargin , LicLayout.PageDefaultMargin)) self.guide2v.setPos(QPointF(self.width() -LicLayout.PageDefaultMargin , LicLayout.PageDefaultMargin)) self.guide1h.setPos(QPointF(LicLayout.PageDefaultMargin , LicLayout.PageDefaultMargin)) self.guide2h.setPos(QPointF(LicLayout.PageDefaultMargin , self.height() -LicLayout.PageDefaultMargin)) for g in self._staticGuides: g.show() self.hasMargins = not self.hasMargins def maximizeGuides(self, width, height): for guide in self.guides: if guide.orientation == LicLayout.Vertical and height > 0: guide.setLength(height) elif guide.orientation == LicLayout.Horizontal and width > 0: guide.setLength(width) def snap(self, item): if not self.snapToGuides and not self.snapToItems: return # User disabled snap snapDistance = 20 margin = 20 # Hide any existing snap guide lines self.xSnapLine.hide() self.ySnapLine.hide() # Build dict of all guides and page items and their [left, right, top, bottom] points itemDict = {} if self.snapToGuides: for guide in self.guides: guidePt = guide.mapToScene(guide.line().p1()) itemDict[guide] = [guidePt.x(), guidePt.y()] if self.snapToItems: for pageItem in item.getPage().getAllChildItems(): if isinstance(pageItem, Step): continue if item.isAncestorOf(pageItem): continue if pageItem is item: continue itemDict[pageItem] = pageItem.getSceneCornerList() if isinstance(pageItem, Page): # Bump page points inwards so we snap to margin, not outside edge itemDict[pageItem][0] += margin itemDict[pageItem][1] += margin itemDict[pageItem][2] -= margin itemDict[pageItem][3] -= margin if not itemDict: return # Nothing to snap to # Get top-left & bottom-right corners of target item tl, br = item.getSceneCorners() # Placeholders for current nearest corner & item nearestX = dx = x = nearestY = dy = y = 100 newXItem = newYItem = None def snapEdge(targetEdge, itemEdge, nearest, dt, t, currentItem, newItem): i = targetEdge - itemEdge if abs(i) < nearest: return abs(i), i, targetEdge, newItem return nearest, dt, t, currentItem def snapX(targetEdge, itemEdge): return snapEdge(targetEdge, itemEdge, nearestX, dx, x, newXItem, pageItem) def snapY(targetEdge, itemEdge): return snapEdge(targetEdge, itemEdge, nearestY, dy, y, newYItem, pageItem) for pageItem, pts in itemDict.items(): if isinstance(pageItem, Guide): left, top = pts right, bottom = pts else: left, top, right, bottom = pts nearestX, dx, x, newXItem = snapX(left, tl.x()) # Compare left edges nearestX, dx, x, newXItem = snapX(right, br.x()) # Compare right edges nearestY, dy, y, newYItem = snapY(top, tl.y()) # Compare top edges nearestY, dy, y, newYItem = snapY(bottom, br.y()) # Compare bottom edges if not isinstance(pageItem, Page): # Check if two items line up horizontally / vertically. Snap with margin on opposite sides if so if (top < tl.y() and bottom > br.y()) or (top > tl.y() and bottom < br.y()): nearestX, dx, x, newXItem = snapX(right + margin, tl.x()) # Snap item's left edge to right w. margin nearestX, dx, x, newXItem = snapX(left - margin, br.x()) # Snap item's right edge to left if (left < tl.x() and right > br.x()) or (left > tl.x() and right < br.x()): nearestY, dy, y, newYItem = snapY(bottom + margin, tl.y()) # Snap item's bottom edge to top w. margin nearestY, dy, y, newYItem = snapY(top - margin, br.y()) # Snap item's top edge to bottom # Snap item into position if nearestX < snapDistance: item.moveBy(dx, 0) if nearestY < snapDistance: item.moveBy(0, dy) tl, br = item.getSceneCorners() # Get top-left & bottom-right corners of newly positioned item # Position a little snap guide line between item & snapped-to item if nearestX < snapDistance: if isinstance(newXItem, Guide): top, bottom = tl.y() + 10, br.y() - 10 else: left, top, right, bottom = itemDict[newXItem] # Look up item points to snap to self.xSnapLine.setLine(x, min(top, tl.y()), x, max((bottom, br.y()))) # Position snap guide line self.xSnapLine.show() if nearestY < snapDistance: if isinstance(newYItem, Guide): left, right = tl.x() + 10, br.x() - 10 else: left, top, right, bottom = itemDict[newYItem] # Look up item points to snap to self.ySnapLine.setLine(min(left, tl.x()), y, max((right, br.x())), y) # Position snap guide line self.ySnapLine.show() def mouseReleaseEvent(self, event): if self.catchTheMouse: self.emit(SIGNAL("sceneClick"), event) return # Need to compare the selection list before and after selection, to deselect any selected parts parts = [] for item in self.selectedItems(): if isinstance(item, Part): parts.append(item) QGraphicsScene.mouseReleaseEvent(self, event) selItems = self.selectedItems() for part in parts: if not part in selItems: part.setSelected(False) self.emit(SIGNAL("sceneClick")) def mouseMoveEvent(self, event): eventPos = event.scenePos() if self.catchTheMouse: if [] == self._crossGuides: hor = FixedGuide(LicLayout.Horizontal , self) ver = FixedGuide(LicLayout.Vertical , self) hor_2 = FixedGuide(LicLayout.Horizontal , self) ver_2 = FixedGuide(LicLayout.Vertical , self) self._crossGuides.append(hor) self._crossGuides.append(ver) self._crossGuides.append(hor_2) self._crossGuides.append(ver_2) self._crossGuides[0].setPos(eventPos) self._crossGuides[1].setPos(eventPos) self._crossGuides[2].setPos(LicLayout.PageDefaultMargin , eventPos.y()) self._crossGuides[3].setPos(eventPos.x(), LicLayout.PageDefaultMargin) return elif [] != self._crossGuides: for guide in self._crossGuides: self.removeItem(guide) self._crossGuides = [] return QGraphicsScene.mouseMoveEvent(self, event) def mousePressEvent(self, event): if self.catchTheMouse: # Need to correctly handling sceneClick signal on release event return # Need to compare the selection list before and after selection, to deselect any selected parts parts = [] for item in self.selectedItems(): if isinstance(item, Part): parts.append(item) QGraphicsScene.mousePressEvent(self, event) selItems = self.selectedItems() for part in parts: if not part in selItems: part.setSelected(False) def contextMenuEvent(self, event): # We can't use the default handler at all because it calls the menu of the # item that was *right-clicked on*, not the menu of the selected items. # So check if clicked item is selected. clickedItem = self.itemAt(event.scenePos()) if clickedItem and clickedItem.isSelected(): return clickedItem.contextMenuEvent(event) selList = self.selectedItems() if selList: return selList[-1].contextMenuEvent(event) event.ignore() def keyPressEvent(self, event): pass # Need this to properly ignore built-in press events def keyReleaseEvent(self, event): if not self.pages: return # No pages = nothing to do here for item in self.selectedItems(): # On Template Page, igNOre event so all page elements can't move if isinstance(item, (QGraphicsSimpleTextItem,TemplateLineItem,TemplatePLIItem,TemplateCSI)): if item.flags().__int__() == NoMoveFlags.__int__ (): return # Part class haVE own event if isinstance(item, Part): item.keyReleaseEvent(event) return key = event.key() if key == Qt.Key_PageUp: return self.pageUp() if key == Qt.Key_PageDown: return self.pageDown() if key == Qt.Key_Home: return self.selectFirstPage() if key == Qt.Key_End: return self.selectLastPage() x = y = 0 offset = 20 if event.modifiers() & Qt.ShiftModifier else 1 if event.modifiers() & Qt.ControlModifier: offset = 5 if key == Qt.Key_Left: x = -offset elif key == Qt.Key_Right: x = offset elif key == Qt.Key_Up: y = -offset elif key == Qt.Key_Down: y = offset else: event.ignore() # We do not handle this key stroke here - pass it on and return return movedItems = [] for item in self.selectedItems(): if isinstance(item, Page): continue # Pages cannot be moved item.oldPos = item.pos() item.moveBy(x, y) if not isinstance(item, CalloutArrowEndItem): movedItems.append(item) if movedItems: self.emit(SIGNAL("itemsMoved"), movedItems) event.accept()
def markToMove(self, part=None): if self._assist is None: self._assist = LicPlacementAssistant( self.views()[0] ) self._assist.setItemtoMove(part)