diff options
Diffstat (limited to 'examples/widgets/graphicsview/diagramscene/diagramscene.py')
-rwxr-xr-x | examples/widgets/graphicsview/diagramscene/diagramscene.py | 825 |
1 files changed, 825 insertions, 0 deletions
diff --git a/examples/widgets/graphicsview/diagramscene/diagramscene.py b/examples/widgets/graphicsview/diagramscene/diagramscene.py new file mode 100755 index 000000000..ea0af84f9 --- /dev/null +++ b/examples/widgets/graphicsview/diagramscene/diagramscene.py @@ -0,0 +1,825 @@ +#!/usr/bin/env python + +############################################################################# +## +## Copyright (C) 2013 Riverbank Computing Limited. +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the PySide examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import math + +from PySide2 import QtCore, QtGui, QtWidgets + +import diagramscene_rc + + +class Arrow(QtWidgets.QGraphicsLineItem): + def __init__(self, startItem, endItem, parent=None, scene=None): + super(Arrow, self).__init__(parent, scene) + + self.arrowHead = QtGui.QPolygonF() + + self.myStartItem = startItem + self.myEndItem = endItem + self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True) + self.myColor = QtCore.Qt.black + self.setPen(QtGui.QPen(self.myColor, 2, QtCore.Qt.SolidLine, + QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) + + def setColor(self, color): + self.myColor = color + + def startItem(self): + return self.myStartItem + + def endItem(self): + return self.myEndItem + + def boundingRect(self): + extra = (self.pen().width() + 20) / 2.0 + p1 = self.line().p1() + p2 = self.line().p2() + return QtCore.QRectF(p1, QtCore.QSizeF(p2.x() - p1.x(), p2.y() - p1.y())).normalized().adjusted(-extra, -extra, extra, extra) + + def shape(self): + path = super(Arrow, self).shape() + path.addPolygon(self.arrowHead) + return path + + def updatePosition(self): + line = QtCore.QLineF(self.mapFromItem(self.myStartItem, 0, 0), self.mapFromItem(self.myEndItem, 0, 0)) + self.setLine(line) + + def paint(self, painter, option, widget=None): + if (self.myStartItem.collidesWithItem(self.myEndItem)): + return + + myStartItem = self.myStartItem + myEndItem = self.myEndItem + myColor = self.myColor + myPen = self.pen() + myPen.setColor(self.myColor) + arrowSize = 20.0 + painter.setPen(myPen) + painter.setBrush(self.myColor) + + centerLine = QtCore.QLineF(myStartItem.pos(), myEndItem.pos()) + endPolygon = myEndItem.polygon() + p1 = endPolygon.at(0) + myEndItem.pos() + + intersectPoint = QtCore.QPointF() + for i in endPolygon: + p2 = i + myEndItem.pos() + polyLine = QtCore.QLineF(p1, p2) + intersectType, intersectPoint = polyLine.intersect(centerLine) + if intersectType == QtCore.QLineF.BoundedIntersection: + break + p1 = p2 + + self.setLine(QtCore.QLineF(intersectPoint, myStartItem.pos())) + line = self.line() + + angle = math.acos(line.dx() / line.length()) + if line.dy() >= 0: + angle = (math.pi * 2.0) - angle + + arrowP1 = line.p1() + QtCore.QPointF(math.sin(angle + math.pi / 3.0) * arrowSize, + math.cos(angle + math.pi / 3) * arrowSize) + arrowP2 = line.p1() + QtCore.QPointF(math.sin(angle + math.pi - math.pi / 3.0) * arrowSize, + math.cos(angle + math.pi - math.pi / 3.0) * arrowSize) + + self.arrowHead.clear() + for point in [line.p1(), arrowP1, arrowP2]: + self.arrowHead.append(point) + + painter.drawLine(line) + painter.drawPolygon(self.arrowHead) + if self.isSelected(): + painter.setPen(QtGui.QPen(myColor, 1, QtCore.Qt.DashLine)) + myLine = QtCore.QLineF(line) + myLine.translate(0, 4.0) + painter.drawLine(myLine) + myLine.translate(0,-8.0) + painter.drawLine(myLine) + + +class DiagramTextItem(QtWidgets.QGraphicsTextItem): + lostFocus = QtCore.Signal(QtWidgets.QGraphicsTextItem) + + selectedChange = QtCore.Signal(QtWidgets.QGraphicsItem) + + def __init__(self, parent=None, scene=None): + super(DiagramTextItem, self).__init__(parent, scene) + + self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable) + self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable) + + def itemChange(self, change, value): + if change == QtWidgets.QGraphicsItem.ItemSelectedChange: + self.selectedChange.emit(self) + return value + + def focusOutEvent(self, event): + self.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) + self.lostFocus.emit(self) + super(DiagramTextItem, self).focusOutEvent(event) + + def mouseDoubleClickEvent(self, event): + if self.textInteractionFlags() == QtCore.Qt.NoTextInteraction: + self.setTextInteractionFlags(QtCore.Qt.TextEditorInteraction) + super(DiagramTextItem, self).mouseDoubleClickEvent(event) + + +class DiagramItem(QtWidgets.QGraphicsPolygonItem): + Step, Conditional, StartEnd, Io = range(4) + + def __init__(self, diagramType, contextMenu, parent=None, scene=None): + super(DiagramItem, self).__init__(parent, scene) + + self.arrows = [] + + self.diagramType = diagramType + self.myContextMenu = contextMenu + + path = QtGui.QPainterPath() + if self.diagramType == self.StartEnd: + path.moveTo(200, 50) + path.arcTo(150, 0, 50, 50, 0, 90) + path.arcTo(50, 0, 50, 50, 90, 90) + path.arcTo(50, 50, 50, 50, 180, 90) + path.arcTo(150, 50, 50, 50, 270, 90) + path.lineTo(200, 25) + self.myPolygon = path.toFillPolygon() + elif self.diagramType == self.Conditional: + self.myPolygon = QtGui.QPolygonF([ + QtCore.QPointF(-100, 0), QtCore.QPointF(0, 100), + QtCore.QPointF(100, 0), QtCore.QPointF(0, -100), + QtCore.QPointF(-100, 0)]) + elif self.diagramType == self.Step: + self.myPolygon = QtGui.QPolygonF([ + QtCore.QPointF(-100, -100), QtCore.QPointF(100, -100), + QtCore.QPointF(100, 100), QtCore.QPointF(-100, 100), + QtCore.QPointF(-100, -100)]) + else: + self.myPolygon = QtGui.QPolygonF([ + QtCore.QPointF(-120, -80), QtCore.QPointF(-70, 80), + QtCore.QPointF(120, 80), QtCore.QPointF(70, -80), + QtCore.QPointF(-120, -80)]) + + self.setPolygon(self.myPolygon) + self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True) + self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True) + + def removeArrow(self, arrow): + try: + self.arrows.remove(arrow) + except ValueError: + pass + + def removeArrows(self): + for arrow in self.arrows[:]: + arrow.startItem().removeArrow(arrow) + arrow.endItem().removeArrow(arrow) + self.scene().removeItem(arrow) + + def addArrow(self, arrow): + self.arrows.append(arrow) + + def image(self): + pixmap = QtGui.QPixmap(250, 250) + pixmap.fill(QtCore.Qt.transparent) + painter = QtGui.QPainter(pixmap) + painter.setPen(QtGui.QPen(QtCore.Qt.black, 8)) + painter.translate(125, 125) + painter.drawPolyline(self.myPolygon) + return pixmap + + def contextMenuEvent(self, event): + self.scene().clearSelection() + self.setSelected(True) + self.myContextMenu.exec_(event.screenPos()) + + def itemChange(self, change, value): + if change == QtWidgets.QGraphicsItem.ItemPositionChange: + for arrow in self.arrows: + arrow.updatePosition() + + return value + + +class DiagramScene(QtWidgets.QGraphicsScene): + InsertItem, InsertLine, InsertText, MoveItem = range(4) + + itemInserted = QtCore.Signal(DiagramItem) + + textInserted = QtCore.Signal(QtWidgets.QGraphicsTextItem) + + itemSelected = QtCore.Signal(QtWidgets.QGraphicsItem) + + def __init__(self, itemMenu, parent=None): + super(DiagramScene, self).__init__(parent) + + self.myItemMenu = itemMenu + self.myMode = self.MoveItem + self.myItemType = DiagramItem.Step + self.line = None + self.textItem = None + self.myItemColor = QtCore.Qt.white + self.myTextColor = QtCore.Qt.black + self.myLineColor = QtCore.Qt.black + self.myFont = QtGui.QFont() + + def setLineColor(self, color): + self.myLineColor = color + if self.isItemChange(Arrow): + item = self.selectedItems()[0] + item.setColor(self.myLineColor) + self.update() + + def setTextColor(self, color): + self.myTextColor = color + if self.isItemChange(DiagramTextItem): + item = self.selectedItems()[0] + item.setDefaultTextColor(self.myTextColor) + + def setItemColor(self, color): + self.myItemColor = color + if self.isItemChange(DiagramItem): + item = self.selectedItems()[0] + item.setBrush(self.myItemColor) + + def setFont(self, font): + self.myFont = font + if self.isItemChange(DiagramTextItem): + item = self.selectedItems()[0] + item.setFont(self.myFont) + + def setMode(self, mode): + self.myMode = mode + + def setItemType(self, type): + self.myItemType = type + + def editorLostFocus(self, item): + cursor = item.textCursor() + cursor.clearSelection() + item.setTextCursor(cursor) + + if not item.toPlainText(): + self.removeItem(item) + item.deleteLater() + + def mousePressEvent(self, mouseEvent): + if (mouseEvent.button() != QtCore.Qt.LeftButton): + return + + if self.myMode == self.InsertItem: + item = DiagramItem(self.myItemType, self.myItemMenu) + item.setBrush(self.myItemColor) + self.addItem(item) + item.setPos(mouseEvent.scenePos()) + self.itemInserted.emit(item) + elif self.myMode == self.InsertLine: + self.line = QtWidgets.QGraphicsLineItem(QtCore.QLineF(mouseEvent.scenePos(), + mouseEvent.scenePos())) + self.line.setPen(QtGui.QPen(self.myLineColor, 2)) + self.addItem(self.line) + elif self.myMode == self.InsertText: + textItem = DiagramTextItem() + textItem.setFont(self.myFont) + textItem.setTextInteractionFlags(QtCore.Qt.TextEditorInteraction) + textItem.setZValue(1000.0) + textItem.lostFocus.connect(self.editorLostFocus) + textItem.selectedChange.connect(self.itemSelected) + self.addItem(textItem) + textItem.setDefaultTextColor(self.myTextColor) + textItem.setPos(mouseEvent.scenePos()) + self.textInserted.emit(textItem) + + super(DiagramScene, self).mousePressEvent(mouseEvent) + + def mouseMoveEvent(self, mouseEvent): + if self.myMode == self.InsertLine and self.line: + newLine = QtCore.QLineF(self.line.line().p1(), mouseEvent.scenePos()) + self.line.setLine(newLine) + elif self.myMode == self.MoveItem: + super(DiagramScene, self).mouseMoveEvent(mouseEvent) + + def mouseReleaseEvent(self, mouseEvent): + if self.line and self.myMode == self.InsertLine: + startItems = self.items(self.line.line().p1()) + if len(startItems) and startItems[0] == self.line: + startItems.pop(0) + endItems = self.items(self.line.line().p2()) + if len(endItems) and endItems[0] == self.line: + endItems.pop(0) + + self.removeItem(self.line) + self.line = None + + if len(startItems) and len(endItems) and \ + isinstance(startItems[0], DiagramItem) and \ + isinstance(endItems[0], DiagramItem) and \ + startItems[0] != endItems[0]: + startItem = startItems[0] + endItem = endItems[0] + arrow = Arrow(startItem, endItem) + arrow.setColor(self.myLineColor) + startItem.addArrow(arrow) + endItem.addArrow(arrow) + arrow.setZValue(-1000.0) + self.addItem(arrow) + arrow.updatePosition() + + self.line = None + super(DiagramScene, self).mouseReleaseEvent(mouseEvent) + + def isItemChange(self, type): + for item in self.selectedItems(): + if isinstance(item, type): + return True + return False + + +class MainWindow(QtWidgets.QMainWindow): + InsertTextButton = 10 + + def __init__(self): + super(MainWindow, self).__init__() + + self.createActions() + self.createMenus() + self.createToolBox() + + self.scene = DiagramScene(self.itemMenu) + self.scene.setSceneRect(QtCore.QRectF(0, 0, 5000, 5000)) + self.scene.itemInserted.connect(self.itemInserted) + self.scene.textInserted.connect(self.textInserted) + self.scene.itemSelected.connect(self.itemSelected) + + self.createToolbars() + + layout = QtWidgets.QHBoxLayout() + layout.addWidget(self.toolBox) + self.view = QtWidgets.QGraphicsView(self.scene) + layout.addWidget(self.view) + + self.widget = QtWidgets.QWidget() + self.widget.setLayout(layout) + + self.setCentralWidget(self.widget) + self.setWindowTitle("Diagramscene") + + def backgroundButtonGroupClicked(self, button): + buttons = self.backgroundButtonGroup.buttons() + for myButton in buttons: + if myButton != button: + button.setChecked(False) + + text = button.text() + if text == "Blue Grid": + self.scene.setBackgroundBrush(QtGui.QBrush(QtGui.QPixmap(':/images/background1.png'))) + elif text == "White Grid": + self.scene.setBackgroundBrush(QtGui.QBrush(QtGui.QPixmap(':/images/background2.png'))) + elif text == "Gray Grid": + self.scene.setBackgroundBrush(QtGui.QBrush(QtGui.QPixmap(':/images/background3.png'))) + else: + self.scene.setBackgroundBrush(QtGui.QBrush(QtGui.QPixmap(':/images/background4.png'))) + + self.scene.update() + self.view.update() + + def buttonGroupClicked(self, id): + buttons = self.buttonGroup.buttons() + for button in buttons: + if self.buttonGroup.button(id) != button: + button.setChecked(False) + + if id == self.InsertTextButton: + self.scene.setMode(DiagramScene.InsertText) + else: + self.scene.setItemType(id) + self.scene.setMode(DiagramScene.InsertItem) + + def deleteItem(self): + for item in self.scene.selectedItems(): + if isinstance(item, DiagramItem): + item.removeArrows() + self.scene.removeItem(item) + + def pointerGroupClicked(self, i): + self.scene.setMode(self.pointerTypeGroup.checkedId()) + + def bringToFront(self): + if not self.scene.selectedItems(): + return + + selectedItem = self.scene.selectedItems()[0] + overlapItems = selectedItem.collidingItems() + + zValue = 0 + for item in overlapItems: + if (item.zValue() >= zValue and isinstance(item, DiagramItem)): + zValue = item.zValue() + 0.1 + selectedItem.setZValue(zValue) + + def sendToBack(self): + if not self.scene.selectedItems(): + return + + selectedItem = self.scene.selectedItems()[0] + overlapItems = selectedItem.collidingItems() + + zValue = 0 + for item in overlapItems: + if (item.zValue() <= zValue and isinstance(item, DiagramItem)): + zValue = item.zValue() - 0.1 + selectedItem.setZValue(zValue) + + def itemInserted(self, item): + self.pointerTypeGroup.button(DiagramScene.MoveItem).setChecked(True) + self.scene.setMode(self.pointerTypeGroup.checkedId()) + self.buttonGroup.button(item.diagramType).setChecked(False) + + def textInserted(self, item): + self.buttonGroup.button(self.InsertTextButton).setChecked(False) + self.scene.setMode(self.pointerTypeGroup.checkedId()) + + def currentFontChanged(self, font): + self.handleFontChange() + + def fontSizeChanged(self, font): + self.handleFontChange() + + def sceneScaleChanged(self, scale): + newScale = int(scale[:-1]) / 100.0 + oldMatrix = self.view.matrix() + self.view.resetMatrix() + self.view.translate(oldMatrix.dx(), oldMatrix.dy()) + self.view.scale(newScale, newScale) + + def textColorChanged(self): + self.textAction = self.sender() + self.fontColorToolButton.setIcon(self.createColorToolButtonIcon( + ':/images/textpointer.png', + QtGui.QColor(self.textAction.data()))) + self.textButtonTriggered() + + def itemColorChanged(self): + self.fillAction = self.sender() + self.fillColorToolButton.setIcon(self.createColorToolButtonIcon( + ':/images/floodfill.png', + QtGui.QColor(self.fillAction.data()))) + self.fillButtonTriggered() + + def lineColorChanged(self): + self.lineAction = self.sender() + self.lineColorToolButton.setIcon(self.createColorToolButtonIcon( + ':/images/linecolor.png', + QtGui.QColor(self.lineAction.data()))) + self.lineButtonTriggered() + + def textButtonTriggered(self): + self.scene.setTextColor(QtGui.QColor(self.textAction.data())) + + def fillButtonTriggered(self): + self.scene.setItemColor(QtGui.QColor(self.fillAction.data())) + + def lineButtonTriggered(self): + self.scene.setLineColor(QtGui.QColor(self.lineAction.data())) + + def handleFontChange(self): + font = self.fontCombo.currentFont() + font.setPointSize(int(self.fontSizeCombo.currentText())) + if self.boldAction.isChecked(): + font.setWeight(QtGui.QFont.Bold) + else: + font.setWeight(QtGui.QFont.Normal) + font.setItalic(self.italicAction.isChecked()) + font.setUnderline(self.underlineAction.isChecked()) + + self.scene.setFont(font) + + def itemSelected(self, item): + font = item.font() + color = item.defaultTextColor() + self.fontCombo.setCurrentFont(font) + self.fontSizeCombo.setEditText(str(font.pointSize())) + self.boldAction.setChecked(font.weight() == QtGui.QFont.Bold) + self.italicAction.setChecked(font.italic()) + self.underlineAction.setChecked(font.underline()) + + def about(self): + QtWidgets.QMessageBox.about(self, "About Diagram Scene", + "The <b>Diagram Scene</b> example shows use of the graphics framework.") + + def createToolBox(self): + self.buttonGroup = QtWidgets.QButtonGroup() + self.buttonGroup.setExclusive(False) + self.buttonGroup.buttonClicked[int].connect(self.buttonGroupClicked) + + layout = QtWidgets.QGridLayout() + layout.addWidget(self.createCellWidget("Conditional", DiagramItem.Conditional), + 0, 0) + layout.addWidget(self.createCellWidget("Process", DiagramItem.Step), 0, + 1) + layout.addWidget(self.createCellWidget("Input/Output", DiagramItem.Io), + 1, 0) + + textButton = QtWidgets.QToolButton() + textButton.setCheckable(True) + self.buttonGroup.addButton(textButton, self.InsertTextButton) + textButton.setIcon(QtGui.QIcon(QtGui.QPixmap(':/images/textpointer.png') + .scaled(30, 30))) + textButton.setIconSize(QtCore.QSize(50, 50)) + + textLayout = QtWidgets.QGridLayout() + textLayout.addWidget(textButton, 0, 0, QtCore.Qt.AlignHCenter) + textLayout.addWidget(QtWidgets.QLabel("Text"), 1, 0, + QtCore.Qt.AlignCenter) + textWidget = QtWidgets.QWidget() + textWidget.setLayout(textLayout) + layout.addWidget(textWidget, 1, 1) + + layout.setRowStretch(3, 10) + layout.setColumnStretch(2, 10) + + itemWidget = QtWidgets.QWidget() + itemWidget.setLayout(layout) + + self.backgroundButtonGroup = QtWidgets.QButtonGroup() + self.backgroundButtonGroup.buttonClicked.connect(self.backgroundButtonGroupClicked) + + backgroundLayout = QtWidgets.QGridLayout() + backgroundLayout.addWidget(self.createBackgroundCellWidget("Blue Grid", + ':/images/background1.png'), 0, 0) + backgroundLayout.addWidget(self.createBackgroundCellWidget("White Grid", + ':/images/background2.png'), 0, 1) + backgroundLayout.addWidget(self.createBackgroundCellWidget("Gray Grid", + ':/images/background3.png'), 1, 0) + backgroundLayout.addWidget(self.createBackgroundCellWidget("No Grid", + ':/images/background4.png'), 1, 1) + + backgroundLayout.setRowStretch(2, 10) + backgroundLayout.setColumnStretch(2, 10) + + backgroundWidget = QtWidgets.QWidget() + backgroundWidget.setLayout(backgroundLayout) + + self.toolBox = QtWidgets.QToolBox() + self.toolBox.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Ignored)) + self.toolBox.setMinimumWidth(itemWidget.sizeHint().width()) + self.toolBox.addItem(itemWidget, "Basic Flowchart Shapes") + self.toolBox.addItem(backgroundWidget, "Backgrounds") + + def createActions(self): + self.toFrontAction = QtWidgets.QAction( + QtGui.QIcon(':/images/bringtofront.png'), "Bring to &Front", + self, shortcut="Ctrl+F", statusTip="Bring item to front", + triggered=self.bringToFront) + + self.sendBackAction = QtWidgets.QAction( + QtGui.QIcon(':/images/sendtoback.png'), "Send to &Back", self, + shortcut="Ctrl+B", statusTip="Send item to back", + triggered=self.sendToBack) + + self.deleteAction = QtWidgets.QAction(QtGui.QIcon(':/images/delete.png'), + "&Delete", self, shortcut="Delete", + statusTip="Delete item from diagram", + triggered=self.deleteItem) + + self.exitAction = QtWidgets.QAction("E&xit", self, shortcut="Ctrl+X", + statusTip="Quit Scenediagram example", triggered=self.close) + + self.boldAction = QtWidgets.QAction(QtGui.QIcon(':/images/bold.png'), + "Bold", self, checkable=True, shortcut="Ctrl+B", + triggered=self.handleFontChange) + + self.italicAction = QtWidgets.QAction(QtGui.QIcon(':/images/italic.png'), + "Italic", self, checkable=True, shortcut="Ctrl+I", + triggered=self.handleFontChange) + + self.underlineAction = QtWidgets.QAction( + QtGui.QIcon(':/images/underline.png'), "Underline", self, + checkable=True, shortcut="Ctrl+U", + triggered=self.handleFontChange) + + self.aboutAction = QtWidgets.QAction("A&bout", self, shortcut="Ctrl+B", + triggered=self.about) + + def createMenus(self): + self.fileMenu = self.menuBar().addMenu("&File") + self.fileMenu.addAction(self.exitAction) + + self.itemMenu = self.menuBar().addMenu("&Item") + self.itemMenu.addAction(self.deleteAction) + self.itemMenu.addSeparator() + self.itemMenu.addAction(self.toFrontAction) + self.itemMenu.addAction(self.sendBackAction) + + self.aboutMenu = self.menuBar().addMenu("&Help") + self.aboutMenu.addAction(self.aboutAction) + + def createToolbars(self): + self.editToolBar = self.addToolBar("Edit") + self.editToolBar.addAction(self.deleteAction) + self.editToolBar.addAction(self.toFrontAction) + self.editToolBar.addAction(self.sendBackAction) + + self.fontCombo = QtWidgets.QFontComboBox() + self.fontCombo.currentFontChanged.connect(self.currentFontChanged) + + self.fontSizeCombo = QtWidgets.QComboBox() + self.fontSizeCombo.setEditable(True) + for i in range(8, 30, 2): + self.fontSizeCombo.addItem(str(i)) + validator = QtGui.QIntValidator(2, 64, self) + self.fontSizeCombo.setValidator(validator) + self.fontSizeCombo.currentIndexChanged.connect(self.fontSizeChanged) + + self.fontColorToolButton = QtWidgets.QToolButton() + self.fontColorToolButton.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup) + self.fontColorToolButton.setMenu( + self.createColorMenu(self.textColorChanged, QtCore.Qt.black)) + self.textAction = self.fontColorToolButton.menu().defaultAction() + self.fontColorToolButton.setIcon( + self.createColorToolButtonIcon(':/images/textpointer.png', + QtCore.Qt.black)) + self.fontColorToolButton.setAutoFillBackground(True) + self.fontColorToolButton.clicked.connect(self.textButtonTriggered) + + self.fillColorToolButton = QtWidgets.QToolButton() + self.fillColorToolButton.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup) + self.fillColorToolButton.setMenu( + self.createColorMenu(self.itemColorChanged, QtCore.Qt.white)) + self.fillAction = self.fillColorToolButton.menu().defaultAction() + self.fillColorToolButton.setIcon( + self.createColorToolButtonIcon(':/images/floodfill.png', + QtCore.Qt.white)) + self.fillColorToolButton.clicked.connect(self.fillButtonTriggered) + + self.lineColorToolButton = QtWidgets.QToolButton() + self.lineColorToolButton.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup) + self.lineColorToolButton.setMenu( + self.createColorMenu(self.lineColorChanged, QtCore.Qt.black)) + self.lineAction = self.lineColorToolButton.menu().defaultAction() + self.lineColorToolButton.setIcon( + self.createColorToolButtonIcon(':/images/linecolor.png', + QtCore.Qt.black)) + self.lineColorToolButton.clicked.connect(self.lineButtonTriggered) + + self.textToolBar = self.addToolBar("Font") + self.textToolBar.addWidget(self.fontCombo) + self.textToolBar.addWidget(self.fontSizeCombo) + self.textToolBar.addAction(self.boldAction) + self.textToolBar.addAction(self.italicAction) + self.textToolBar.addAction(self.underlineAction) + + self.colorToolBar = self.addToolBar("Color") + self.colorToolBar.addWidget(self.fontColorToolButton) + self.colorToolBar.addWidget(self.fillColorToolButton) + self.colorToolBar.addWidget(self.lineColorToolButton) + + pointerButton = QtWidgets.QToolButton() + pointerButton.setCheckable(True) + pointerButton.setChecked(True) + pointerButton.setIcon(QtGui.QIcon(':/images/pointer.png')) + linePointerButton = QtWidgets.QToolButton() + linePointerButton.setCheckable(True) + linePointerButton.setIcon(QtGui.QIcon(':/images/linepointer.png')) + + self.pointerTypeGroup = QtWidgets.QButtonGroup() + self.pointerTypeGroup.addButton(pointerButton, DiagramScene.MoveItem) + self.pointerTypeGroup.addButton(linePointerButton, + DiagramScene.InsertLine) + self.pointerTypeGroup.buttonClicked[int].connect(self.pointerGroupClicked) + + self.sceneScaleCombo = QtWidgets.QComboBox() + self.sceneScaleCombo.addItems(["50%", "75%", "100%", "125%", "150%"]) + self.sceneScaleCombo.setCurrentIndex(2) + self.sceneScaleCombo.currentIndexChanged[str].connect(self.sceneScaleChanged) + + self.pointerToolbar = self.addToolBar("Pointer type") + self.pointerToolbar.addWidget(pointerButton) + self.pointerToolbar.addWidget(linePointerButton) + self.pointerToolbar.addWidget(self.sceneScaleCombo) + + def createBackgroundCellWidget(self, text, image): + button = QtWidgets.QToolButton() + button.setText(text) + button.setIcon(QtGui.QIcon(image)) + button.setIconSize(QtCore.QSize(50, 50)) + button.setCheckable(True) + self.backgroundButtonGroup.addButton(button) + + layout = QtWidgets.QGridLayout() + layout.addWidget(button, 0, 0, QtCore.Qt.AlignHCenter) + layout.addWidget(QtWidgets.QLabel(text), 1, 0, QtCore.Qt.AlignCenter) + + widget = QtWidgets.QWidget() + widget.setLayout(layout) + + return widget + + def createCellWidget(self, text, diagramType): + item = DiagramItem(diagramType, self.itemMenu) + icon = QtGui.QIcon(item.image()) + + button = QtWidgets.QToolButton() + button.setIcon(icon) + button.setIconSize(QtCore.QSize(50, 50)) + button.setCheckable(True) + self.buttonGroup.addButton(button, diagramType) + + layout = QtWidgets.QGridLayout() + layout.addWidget(button, 0, 0, QtCore.Qt.AlignHCenter) + layout.addWidget(QtWidgets.QLabel(text), 1, 0, QtCore.Qt.AlignCenter) + + widget = QtWidgets.QWidget() + widget.setLayout(layout) + + return widget + + def createColorMenu(self, slot, defaultColor): + colors = [QtCore.Qt.black, QtCore.Qt.white, QtCore.Qt.red, QtCore.Qt.blue, QtCore.Qt.yellow] + names = ["black", "white", "red", "blue", "yellow"] + + colorMenu = QtWidgets.QMenu(self) + for color, name in zip(colors, names): + action = QtWidgets.QAction(self.createColorIcon(color), name, self, + triggered=slot) + action.setData(QtGui.QColor(color)) + colorMenu.addAction(action) + if color == defaultColor: + colorMenu.setDefaultAction(action) + return colorMenu + + def createColorToolButtonIcon(self, imageFile, color): + pixmap = QtGui.QPixmap(50, 80) + pixmap.fill(QtCore.Qt.transparent) + painter = QtGui.QPainter(pixmap) + image = QtGui.QPixmap(imageFile) + target = QtCore.QRect(0, 0, 50, 60) + source = QtCore.QRect(0, 0, 42, 42) + painter.fillRect(QtCore.QRect(0, 60, 50, 80), color) + painter.drawPixmap(target, image, source) + painter.end() + + return QtGui.QIcon(pixmap) + + def createColorIcon(self, color): + pixmap = QtGui.QPixmap(20, 20) + painter = QtGui.QPainter(pixmap) + painter.setPen(QtCore.Qt.NoPen) + painter.fillRect(QtCore.QRect(0, 0, 20, 20), color) + painter.end() + + return QtGui.QIcon(pixmap) + + +if __name__ == '__main__': + + import sys + + app = QtWidgets.QApplication(sys.argv) + + mainWindow = MainWindow() + mainWindow.setGeometry(100, 100, 800, 500) + mainWindow.show() + + sys.exit(app.exec_()) |