Box erstellen via ToolbarButton click

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Basti123
User
Beiträge: 7
Registriert: Mittwoch 25. März 2020, 14:35

Hallo Leute,
ich bin neu hier und hoffe das Ihr mir hier ein bisschen helfen könnt :D .
Ich arbeite mit Python 3.7, pyqt 5.13.2 und windows 8 falls das das wichtig sein sollte.
Ich habe mich auch ein bisschen zu Toolbars eingelesen, allerdings hat mich das bei meinem Problem nicht wirklich geholfen.

Mein Problem ist das ich eine Box erstellen möchte via Toolbar click, welche dann in meiner graphische Scene erscheint. Die Toolbar zu erstellen war jetzt nicht so schwer, allerdings weiß ich nicht genau wie ich die Funktion schreiben soll mit der die neue Box erstellt wird.

Momentan lasse ich zum Programmstart drei Nodes mitladen, allerdings möchte ich später darauf übergehen das sie nur noch über die Toolbar hinzufügt werden können. Einfachshalber sollen sie einfach in der Mitte erstellt werden, also Position(0,0). Allerdings stehe ich gerade auf dem Schlauch und ich bekomme keine ordentliche Funktion zustande welche das macht. :(
Da ich ein Beginner bin habt bitte Gnade. :?
Hier ist mein Code den ich benutze... dieser ist eigentlich nochmal deutlich größer, allerdings habe ich ihn runter gestückelt, damit es nicht ganz so unübersichtlich wird.
Die Funktion um die es geht ist def addnewBox in editor_window.py , ich habe da erstmal ein Fragezeichen reingemacht, da ich komplett ratlos bin:(

Das Bild addBox.png welches ich für die Toolbox benutze in editor.py ist von der Website material.io mit dem Namen note_add.
Von hier: https://material.io/resources/icons/?ic ... e=baseline

Bleibt auch alle Gesund in diesen Zeiten!


editor_main.py

Code: Alles auswählen

import sys
 
from PyQt5.QtWidgets import *
from editor_window import Window
 
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
 
    wnd = Window()
    wnd.editor.addBoxes()
    wnd.show()
 
    sys.exit(app.exec_())

editor_window.py

Code: Alles auswählen

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
 
from editor_main_widget import MainWidget
from editor_scene import Scene
from editor_box import Box
 
class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
 
    def initUI(self):
 
        self.scene = Scene()
 
        self.createToolBars()
 
        # create node editor widget
        self.editor = MainWidget(self)
        self.setCentralWidget(self.editor)
 
        # set window properties
        self.setGeometry(200, 200, 800, 600)
        self.setWindowTitle("Editor")
        self.show()
 
    def createToolBars(self):
        # initialize toolbar
        toolbar = QToolBar("my main toolbar")
        toolbar.setIconSize(QSize(32, 32))
        self.addToolBar(toolbar)
        "buttons"
        button_action = QAction(QIcon("addBox.png"), "New box 1", self)
        button_action.setStatusTip("Add a new Box")
        button_action.triggered.connect(self.addnewBox())
        toolbar.addAction(button_action)
 
    def addnewBox(self): 
        ?
        

editor_scene.py

Code: Alles auswählen

from editor_graphics_scene import GraphicsScene
 
class Scene():
    def __init__(self):
        super().__init__()
 
        self.boxes = []
 
        self.scene_width = 64000
        self.scene_height = 64000
 
        self.initUI()
 
    def initUI(self):
        self.grScene = GraphicsScene(self)
        self.grScene.setGrScene(self.scene_width, self.scene_height)
 
    def addBox(self, box):
        self.boxes.append(box)
        
editor_graphics_scene.py

Code: Alles auswählen

import math
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
 
class GraphicsScene(QGraphicsScene):
    def __init__(self, scene, parent=None):
        super().__init__(parent)
        "Set the Setting for the Scene"
        self.scene = scene
        # settings
        self.gridSize = 20
        self.gridSquares = 5
 
        self._color_background = QColor("#393939")
        self._color_light = QColor("#2f2f2f")
        self._color_dark = QColor("#292929")
 
        self._pen_light = QPen(self._color_light)
        self._pen_light.setWidth(1)
        self._pen_dark = QPen(self._color_dark)
        self._pen_dark.setWidth(2)
 
        self.setBackgroundBrush(self._color_background)
 
    def setGrScene(self, width, height):
        self.setSceneRect(-width // 2, -height // 2, width, height)
 
    "Draw the Lines in the Scene"
    def drawBackground(self, painter, rect):
        super().drawBackground(painter, rect)
 
        # here we create our grid
        left = int(math.floor(rect.left()))
        right = int(math.ceil(rect.right()))
        top = int(math.floor(rect.top()))
        bottom = int(math.ceil(rect.bottom()))
 
        first_left = left - (left % self.gridSize)
        first_top = top - (top % self.gridSize)
 
        # compute all lines to be drawn
        lines_light, lines_dark = [], []
        for x in range(first_left, right, self.gridSize):
            if (x % (self.gridSize*self.gridSquares) != 0): lines_light.append(QLine(x, top, x, bottom))
            else: lines_dark.append(QLine(x, top, x, bottom))
 
        for y in range(first_top, bottom, self.gridSize):
            if (y % (self.gridSize*self.gridSquares) != 0): lines_light.append(QLine(left, y, right, y))
            else: lines_dark.append(QLine(left, y, right, y))
 
        # draw the lines
        painter.setPen(self._pen_light)
        painter.drawLines(*lines_light)
 
        painter.setPen(self._pen_dark)
        painter.drawLines(*lines_dark)
editor_box.py

Code: Alles auswählen

from editor_graphics_box import GraphicsBox
from editor_content_widget import ContentWidget
 
 
class Box():
    def __init__(self, scene, title="Undefined Box"):
        super().__init__()
        self._title = title
        self.scene = scene
 
        self.content = ContentWidget(self)
        self.grBox = GraphicsBox(self)
        self.title = title
 
        self.scene.addBox(self)
        self.scene.grScene.addItem(self.grBox)
 
    @property
    def pos(self):
        return self.grBox.pos()
    def setPos(self, x, y):
        self.grBox.setPos(x, y)
 
    @property
    def title(self):
        return self._title
 
    @title.setter
    def title(self, value):
        self._title = value
        self.grBox.title = self._title
editor_graphics_box.py

Code: Alles auswählen

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
 
class GraphicsBox(QGraphicsItem):
    def __init__(self, box, parent=None):
        super().__init__(parent)
 
        self.box = box
        self.content = self.box.content
 
        self._title_color = Qt.white
        self._title_font = QFont("Ubuntu", 10)
 
        self.width = 180
        self.height = 180
        self.edge_size = 10.0
        self.title_horizontal_padding = 2.0
        self.title_height = 24.0
        self._padding = 4.0
 
        self._pen_default = QPen(QColor("#7F000000"))
        self._pen_selected = QPen(QColor("#FFFFA637"))
 
        self._brush_title = QBrush(QColor("#FF313131"))
        self._brush_background = QBrush(QColor("#E3212121"))
 
        # init title
        self.initTitle()
        self.title = self.box.title
 
        # init content
        self.initContent()
 
        self.initUI()
 
    @property
    def title(self): return self._title
 
    @title.setter
    def title(self, value):
        self._title = value
        self.title_item.setPlainText(self._title)
        text_width = self.title_item.boundingRect().width() # set the width of the title
        offset = self.width // 2 - text_width // 2  # double // to get a number without comma number
        self.title_item.setPos( offset - self.title_horizontal_padding // 2, 0)
 
    def boundingRect(self):
        return QRectF(
            0,
            0,
            self.width,
            self.height
        ).normalized()
 
    def initUI(self):
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setFlag(QGraphicsItem.ItemIsMovable)
 
    def initTitle(self):
        self.title_item = QGraphicsTextItem(self)
        self.title_item.box = self.box
        self.title_item.setDefaultTextColor(self._title_color)
        self.title_item.setFont(self._title_font)
        self.title_item.setPos(self._padding, 0) # (x,y)
 
    def initContent(self):
        self.grContent = QGraphicsProxyWidget(self)
        self.content.setGeometry(self.edge_size, self.title_height + self.edge_size,
                                 self.width - 2 * self.edge_size, self.height - 2 * self.edge_size - self.title_height)
        self.grContent.setWidget(self.content)
 
    def paint(self, painter, QStyleOptionGraphicsItem, widget=None):
        # title
        path_title = QPainterPath()
        path_title.setFillRule(Qt.WindingFill)
        path_title.addRoundedRect(0,0, self.width, self.title_height, self.edge_size, self.edge_size)
        path_title.addRect(0, self.title_height - self.edge_size, self.edge_size, self.edge_size)
        path_title.addRect(self.width - self.edge_size, self.title_height - self.edge_size, self.edge_size, self.edge_size)
        painter.setPen(Qt.NoPen)
        painter.setBrush(self._brush_title)
        painter.drawPath(path_title.simplified())
 
        # content
        path_content = QPainterPath()
        path_content.setFillRule(Qt.WindingFill)
        path_content.addRoundedRect(0, self.title_height, self.width, self.height - self.title_height, self.edge_size, self.edge_size)
        path_content.addRect(0, self.title_height, self.edge_size, self.edge_size)
        path_content.addRect(self.width - self.edge_size, self.title_height, self.edge_size, self.edge_size)
        painter.setPen(Qt.NoPen)
        painter.setBrush(self._brush_background)
        painter.drawPath(path_content.simplified())
 
        # outline
        path_outline = QPainterPath()
        path_outline.addRoundedRect(0, 0, self.width, self.height, self.edge_size, self.edge_size)
        painter.setPen(self._pen_default if not self.isSelected() else self._pen_selected)
        painter.setBrush(Qt.NoBrush)
        painter.drawPath(path_outline.simplified())

editor_graphics_view.py

Code: Alles auswählen

from PyQt5.QtWidgets import QGraphicsView
from PyQt5.QtCore import *
from PyQt5.QtGui import *
 
class GraphicsView(QGraphicsView):
    def __init__(self, grScene, parent=None):
        super().__init__(parent)
        self.grScene = grScene
 
        self.initUI()
 
        self.setScene(self.grScene)
 
    def initUI(self):
        "make everthing looks smoother and more pixel"
        self.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing | QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform)
        "Update the Scene in case of movement and reset the lines behind the object that was moved"
        self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
        "hide the scrollbars"
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
 
    def mousePressEvent(self, event):
        if event.button() == Qt.MiddleButton:
            self.middleMouseButtonPress(event)
        elif event.button() == Qt.LeftButton:
            self.leftMouseButtonPress(event)
        elif event.button() == Qt.RightButton:
            self.rightMouseButtonPress(event)
        else:
            super().mousePressEvent(event)
 
    def mouseReleaseEvent(self, event):
        if event.button() == Qt.MiddleButton:
            self.middleMouseButtonRelease(event)
        elif event.button() == Qt.LeftButton:
            self.leftMouseButtonRelease(event)
        elif event.button() == Qt.RightButton:
            self.rightMouseButtonRelease(event)
        else:
            super().mouseReleaseEvent(event)
 
    "MIDDLE MOUSE BUTTON EVENT"
    def middleMouseButtonPress(self, event):
        super().mousePressEvent(event)
 
    def middleMouseButtonRelease(self, event):
        super().mousePressEvent(event)
 
    "LEFT MOUSE BUTTON EVENT"
    def leftMouseButtonPress(self, event):
        super().mousePressEvent(event)
 
    def leftMouseButtonRelease(self, event):
        super().mouseReleaseEvent(event)
 
    "RIGHT MOUSE BUTTON EVENT"
    def rightMouseButtonPress(self, event):
        super().mousePressEvent(event)
 
    def rightMouseButtonRelease(self, event):
        super().mouseReleaseEvent(event)

editor_content_widget.py

Code: Alles auswählen

from PyQt5.QtWidgets import *
 
class ContentWidget(QWidget):
    def __init__(self, box, parent=None):
        self.box = box
        super().__init__(parent)
 
        self.initUI()
 
    def initUI(self):
        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0,0,0,0)
        self.setLayout(self.layout)
 
        self.wdg_label = QLabel("Some Title")
        self.layout.addWidget(self.wdg_label)
        self.layout.addWidget(QTextEdit("test"))

editor_main_widget.py

Code: Alles auswählen

from PyQt5.QtWidgets import *
 
from editor_scene import Scene
from editor_box import Box
from editor_graphics_view import GraphicsView
 
class MainWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.initUI()
 
    def initUI(self):
 
        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.layout)
 
        # crate graphics scene
        self.scene = Scene()
 
        # create graphics view
        self.view = GraphicsView(self.scene.grScene, self)
        self.layout.addWidget(self.view)
 
    def addBoxes(self):
        box1 = Box(self.scene, "Box 1")
        box2 = Box(self.scene, "Box 2")
        box3 = Box(self.scene, "Box 3")
        box1.setPos(-350, -250)
        box2.setPos(-75, 0)
        box3.setPos(200, -150)
        
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du machst schonmal den klassischen Fehler - du schreibst

Code: Alles auswählen

        button_action.triggered.connect(self.addnewBox())
Womit addnewBox (maessig benannt. Entweder in pythonisch add_new_box, oder in Qt-isch addNewBox) schon gleich AUFGERUFEN wird, und der RUECKGABEWERT als slot an das Signal gebunden wird. Wohl kaum, was du willst.

Also die Klammern weglassen, und ein print-statement in die Method, damit du pruefen kannst, das es geklappt hat.

Danach ist mir nicht so genau klar, was dein eigentliches Problem ist. Das muesstest du dann nochmal besser beschreiben.
Basti123
User
Beiträge: 7
Registriert: Mittwoch 25. März 2020, 14:35

Ah ja danke das habe ich in der Tat übersehen!
Es sieht jetzt so aus:)

Code: Alles auswählen

    def createToolBars(self):
        # initialize toolbar
        toolbar = QToolBar("my main toolbar")
        toolbar.setIconSize(QSize(32, 32))
        self.addToolBar(toolbar)
        "buttons"
        button_action = QAction(QIcon("addBox.png"), "New box 1", self)
        button_action.setStatusTip("Add a new Box")
        button_action.triggered.connect(self.addNewBox)
        toolbar.addAction(button_action)

    def addNewBox(self):
        print("addnewbox called")
Mein Output ist dann das was in print steht.

Für die Erklärung nehme ich mal ein Bild vom Editor zur Unterstützung
Also momentan sieht der Editor so aus:
Bild
sollte das Bild nicht laden dann hier der Link:
https://flickr.com/photos/147301383@N02/49700033888/

Auf dem Bild sieht man die 3 Boxen die gleich von Anfang an mit dabei sind und mein Plan war es, dass wenn man oben auf dem Toolbar Button klickt noch eine weitere Box erstellt wird. Und es sollen beliebig viele Boxen erstellt werden können durch den Button click.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

In addBoxes machst du doch box1 = Box(self.scene, "Box 1"), was hindert dich daran, das gleiche in adNewBox zu machen?
Basti123
User
Beiträge: 7
Registriert: Mittwoch 25. März 2020, 14:35

Wenn ich einfach nur eingebe:

Code: Alles auswählen

    
    def addNewBox(self):
        print("addnewbox called")
        box = Box(self.scene, "Box")
        box.setPos(0, 0)
        
Dann wird nur das Print statement ausgegeben aber sonst passiert auch nichts...
was ja auch klar ist, weil ich definiere ja nur meine box und setze die Position.
Aber ich weiß leider nicht wie ich die Fkt am besten schreiben soll damit diese auch erscheint an den gewünschten Koordinaten:/.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was sind denn die gewuenschten Koordinaten? Und warum passiert hier nichts, aber an der anderen Stelle, wo du drei davon machst, passiert wohl etwas?
Basti123
User
Beiträge: 7
Registriert: Mittwoch 25. März 2020, 14:35

Also die gewünschten Koordinaten sind 0, 0.
Die Drei Boxen welche in editor_main_widget.py gespeichert sind.

Code: Alles auswählen

    def addBoxes(self):
        box1 = Box(self.scene, "Box 1")
        box2 = Box(self.scene, "Box 2")
        box3 = Box(self.scene, "Box3 ")
        box1.setPos(-350, -250)
        box2.setPos(-75, 0)
        box3.setPos(200, -150)
werden in der Main Funktion mit der Zeile wnd.editor.addBoxes() geladen.

Code: Alles auswählen

import sys
from PyQt5.QtWidgets import *
from editor_window import Window

if __name__ == '__main__':
    app = QApplication(sys.argv)

    wnd = Window()
    wnd.editor.addBoxes()
    wnd.show()

    sys.exit(app.exec_())
der ... .editor. ... ist in editor_window.py definiert als:

Code: Alles auswählen

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        self.scene = Scene()

        self.createToolBars()

        # create node editor widget
        self.editor = MainWidget(self)
        self.setCentralWidget(self.editor)
        ...
        
und in der Class MainWidget befindet sich die oben genannte Funktion def addBoxes.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und warum kannst du dann diesen Aufruf von setPos danach nicht taetigen? Mal abgesehen davon, dass das eigentlich in den Konstruktor gehoert, wenn so eine Box ohne Koordinaten keinen Sinn macht. Generell ist mein Punkt: ich kann dir nicht sagen, warum das nicht geht. Ich habe den Code nicht. Ich kann dir nur sagen, wenn du das gleiche tust, dann *sollte* das gleiche passieren. Wenn nicht, dann fehlt da eben was. So wie hier die Position zu setzen.
Basti123
User
Beiträge: 7
Registriert: Mittwoch 25. März 2020, 14:35

hmm ich habe ja die Position mit box.setPos gesetzt eig. Kann ich dir den Code irgendwie geben?
Ich weiß daher weiß ich auch nicht wo der Fehler liegt:(
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ah, da ist es, hab's nicht gesehen. Dann muss es ja irgendwas danach sein. Und du kannst den Code auf github stellen. Ich habe aber auch nur so viel Zeit, ich kann dir nicht versprechen, dass ich da jetzt Stunden mit debugging verbringen kann. Insbesondere, wenn das nicht in einem Zustand ist, der sich leicht reproduzieren laesst. Also kompliziertes Setup etc.
Basti123
User
Beiträge: 7
Registriert: Mittwoch 25. März 2020, 14:35

Hey danke dir :) vlt findest du ja trotzdem was! würde mich natürlich sehr freuen.
Was wäre für dich ein leichters Setup?
es befindet sich hier der Code an dem ich gerade rum experimentiere:
https://github.com/sebbastiannn/TUProje ... esttoolbar
Antworten