Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Drag and drop widgets onto a MS word-like a4 page
Forum Updated to NodeBB v4.3 + New Features

Drag and drop widgets onto a MS word-like a4 page

Scheduled Pinned Locked Moved Solved Qt for Python
12 Posts 2 Posters 839 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M MightyDigits

    An update. So I have a graphicsview and graphicsscene.

    The peculiar part is that I cannot drop into the scene, only outside of it in the view. It does get added to the scene when dropping.

    How to resolve this issue, and basically inverse this behavior (not droppable outside of scene, droppable inside it).?

    Code below:

    import math
    
    from PyQt6.QtWidgets import QApplication, QHBoxLayout, QVBoxLayout, QWidget, QPushButton, QGridLayout, QScrollArea, QGraphicsGridLayout, QGraphicsView, QGraphicsScene
    from PyQt6 import QtGui, QtCore
    from PyQt6.QtCore import Qt, QMimeData
    from PyQt6.QtGui import QDrag, QPixmap
    
    
    class CustomGraphicsScene(QGraphicsScene):
        def __init__(self, parent=None):
            super(QGraphicsScene, self).__init__(parent)
    
        # def dragEnterEvent(self, event: QtGui.QDragEnterEvent) -> None:
        #     event.accept()
        #
        # def dropEvent(self, event: QtGui.QDropEvent) -> None:
        #     widget = event.source()    # Know which widget is triggering
        #     print(f"Widget:\t{widget}")
        #
        #     event.setDropAction(Qt.DropAction.MoveAction)
        #
        #     button = QPushButton("xyz")
        #
        #     # widget.setParent(self.graphics_view)
        #
        #     self.addWidget(button)
        #     event.accept()
    
    
    class DragButton(QPushButton):
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
        def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
            # Not sure if the conditional here is needed (yet)
            if event.buttons() == QtCore.Qt.MouseButton.LeftButton:    # Must use buttons(), not button()
                drag = QDrag(self)
    
                mime_data = QMimeData()
                drag.setMimeData(mime_data)
                drag.setHotSpot(event.pos())
    
                # Code to show what is being dragged
                pixmap = QPixmap(self.size())
                self.render(pixmap)
                drag.setPixmap(pixmap)
    
                drag.exec(Qt.DropAction.MoveAction)     # Event loop blocking the main loop until complete
    
    
    class Window(QWidget):
    
        def __init__(self):
            super().__init__()
            self.setAcceptDrops(True)
    
            self.main_layout = QVBoxLayout()
            self.button_layout = QHBoxLayout()
    
            self.graphics_scene = CustomGraphicsScene(parent=self)
    
            # Make a graphics view and add it to the main_layout as the 2nd widget
            self.graphics_view = QGraphicsView()
            self.graphics_view.setScene(self.graphics_scene)
            self.graphics_view.setAcceptDrops(True)
            page_size = 300
            self.graphics_view.setFixedSize(page_size, int(page_size*(math.sqrt(2))))
    
            # We want something like a ScrollArea??
            self.scrollArea = QScrollArea(self)
            self.scrollArea.setWidgetResizable(True)
    
            self.scrollArea.setWidget(self.graphics_view)
    
            for button in ["A", "B", "C", "D"]:
                _button = DragButton(button)
                self.button_layout.addWidget(_button)
    
            self.main_layout.addLayout(self.button_layout)
            self.main_layout.addWidget(self.scrollArea)
            self.setLayout(self.main_layout)
    
            # Test
            self.column = 3
            self.row = 3
    
        def dragEnterEvent(self, event: QtGui.QDragEnterEvent) -> None:
            event.accept()
    
        def dropEvent(self, event: QtGui.QDropEvent) -> None:
            widget = event.source()    # Know which widget is triggering
            print(f"Widget:\t{widget}")
            position = event.position()
    
            event.setDropAction(Qt.DropAction.MoveAction)
    
            button = QPushButton("xyz")
    
            self.graphics_scene.addWidget(button)
    
            event.accept()
    
    
    app = QApplication([])
    w = Window()
    w.show()
    
    app.exec()
    
    SGaistS Offline
    SGaistS Offline
    SGaist
    Lifetime Qt Champion
    wrote on last edited by SGaist
    #3

    Hi and welcome to devnet,

    You need to implement the dragMoveEvent as well.

    Interested in AI ? www.idiap.ch
    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

    M 1 Reply Last reply
    0
    • SGaistS SGaist

      Hi and welcome to devnet,

      You need to implement the dragMoveEvent as well.

      M Offline
      M Offline
      MightyDigits
      wrote on last edited by
      #4

      @SGaist To me it seems like it's because the scene cannot accept drops.

      How would the dragMoveEvent suddenly make me be able to drop into the scene, when without it I can drop anywhere but the scene, so long as I set setAcceptDrops to True?

      SGaistS 1 Reply Last reply
      0
      • M MightyDigits

        @SGaist To me it seems like it's because the scene cannot accept drops.

        How would the dragMoveEvent suddenly make me be able to drop into the scene, when without it I can drop anywhere but the scene, so long as I set setAcceptDrops to True?

        SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #5

        If you want custom drag and drop you must do it at the view level. The scene handles scene related drag and drop.

        dragEnterEvent enables the handling of DnD, dragMoveEvent, handles where it should happen and then dropEvent for the actual drop.

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        M 1 Reply Last reply
        2
        • SGaistS SGaist

          If you want custom drag and drop you must do it at the view level. The scene handles scene related drag and drop.

          dragEnterEvent enables the handling of DnD, dragMoveEvent, handles where it should happen and then dropEvent for the actual drop.

          M Offline
          M Offline
          MightyDigits
          wrote on last edited by
          #6

          @SGaist Got it to work. Thanks a lot!

          SGaistS 1 Reply Last reply
          0
          • M MightyDigits

            @SGaist Got it to work. Thanks a lot!

            SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #7

            @MightyDigits you're welcome !

            Since you have it working now, please mark the thread as solved using the "Topic Tools" button or the three doted menu beside the answer you deem correct so other forum members may know a solution has been found :-)

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            M 1 Reply Last reply
            0
            • M MightyDigits has marked this topic as solved on
            • SGaistS SGaist

              @MightyDigits you're welcome !

              Since you have it working now, please mark the thread as solved using the "Topic Tools" button or the three doted menu beside the answer you deem correct so other forum members may know a solution has been found :-)

              M Offline
              M Offline
              MightyDigits
              wrote on last edited by
              #8

              @SGaist Share the code as well?

              SGaistS 1 Reply Last reply
              0
              • M MightyDigits

                @SGaist Share the code as well?

                SGaistS Offline
                SGaistS Offline
                SGaist
                Lifetime Qt Champion
                wrote on last edited by
                #9

                @MightyDigits that would be nice as well ! It might help other people having the same issue :-)

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                M 1 Reply Last reply
                0
                • SGaistS SGaist

                  @MightyDigits that would be nice as well ! It might help other people having the same issue :-)

                  M Offline
                  M Offline
                  MightyDigits
                  wrote on last edited by
                  #10

                  @SGaist

                  Sure, below is the code.

                  Still having trouble with dropping the buttons at the right place. I have experience mapping ROI's from pyqtgraph into views, but PyQt is different. Tried all mapping options and followed some examples, but nothing is giving me what I want. Some of these little functionalities takes hours and hours without success...

                  The coordinates of the view/scene and buttons do not align, so how to achieve that?

                  import math
                  
                  from PyQt6.QtWidgets import QApplication, QHBoxLayout, QVBoxLayout, QWidget, QPushButton, QGridLayout, QScrollArea, QGraphicsGridLayout, QGraphicsView, QGraphicsScene
                  from PyQt6 import QtGui, QtCore
                  from PyQt6.QtCore import Qt, QMimeData
                  from PyQt6.QtGui import QDrag, QPixmap
                  
                  
                  class CustomGraphicsView(QGraphicsView):
                      def __init__(self, parent=None):
                          super(QGraphicsView, self).__init__(parent)
                          self.setAcceptDrops(True)
                  
                      def dragMoveEvent(self, event: QtGui.QDragMoveEvent) -> None:
                          if event.mimeData().hasImage():
                              event.acceptProposedAction()
                  
                      def dropEvent(self, event):
                          print("In dropEvent of VIEW")
                          if self.scene():
                              event.accept()
                  
                              button = DragButton("XYZ")
                              point = button.mapFromGlobal(event.position())
                  
                              try:
                                  button.move(point)
                                  self.scene().addWidget(button)
                                  print("Button moved")
                              except Exception as e:
                                  print(e)
                          event.acceptProposedAction()
                  
                  
                  class CustomGraphicsScene(QGraphicsScene):
                      def __init__(self, parent=None):
                          super(QGraphicsScene, self).__init__(parent)
                  
                  
                  class DragButton(QPushButton):
                  
                      def __init__(self, *args, **kwargs):
                          super().__init__(*args, **kwargs)
                          
                      def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
                          print("In mousemovevent of dragbutton")
                          drag = QDrag(self)
                          
                          mime_data = QMimeData()
                          mime_data.setText(f"{event.pos().x()}, {event.pos().y()}")
                  
                          drag.setMimeData(mime_data)
                          drag.setHotSpot(event.pos())
                  
                          pixmap = QPixmap(self.size())
                          self.render(pixmap)
                          drag.setPixmap(pixmap)
                  
                          drag.exec(Qt.DropAction.MoveAction)     # Event loop blocking the main loop until complete
                  
                      
                  class Window(QWidget):
                  
                      def __init__(self):
                          super().__init__()
                          self.setFixedSize(960, 540)
                  
                          self.main_layout = QVBoxLayout()
                          self.button_layout = QHBoxLayout()
                  
                          self.graphics_scene = CustomGraphicsScene(parent=self)
                  
                          self.graphics_view = CustomGraphicsView(self)
                          self.graphics_view.setScene(self.graphics_scene)
                          page_size = 300
                          self.graphics_view.setFixedSize(page_size, int(page_size*(math.sqrt(2))))
                  
                          self.scrollArea = QScrollArea(self)
                          self.scrollArea.setAcceptDrops(True)
                          self.scrollArea.setWidgetResizable(True)
                  
                          self.scrollArea.setWidget(self.graphics_view)
                  
                          for button in ["A", "B", "C", "D"]:
                              _button = DragButton(button)
                              self.button_layout.addWidget(_button)
                  
                          self.main_layout.addLayout(self.button_layout)
                          self.main_layout.addWidget(self.scrollArea)
                          self.setLayout(self.main_layout)
                  
                  
                  app = QApplication([])
                  w = Window()
                  w.show()
                  
                  app.exec()
                  
                  SGaistS 1 Reply Last reply
                  0
                  • M MightyDigits

                    @SGaist

                    Sure, below is the code.

                    Still having trouble with dropping the buttons at the right place. I have experience mapping ROI's from pyqtgraph into views, but PyQt is different. Tried all mapping options and followed some examples, but nothing is giving me what I want. Some of these little functionalities takes hours and hours without success...

                    The coordinates of the view/scene and buttons do not align, so how to achieve that?

                    import math
                    
                    from PyQt6.QtWidgets import QApplication, QHBoxLayout, QVBoxLayout, QWidget, QPushButton, QGridLayout, QScrollArea, QGraphicsGridLayout, QGraphicsView, QGraphicsScene
                    from PyQt6 import QtGui, QtCore
                    from PyQt6.QtCore import Qt, QMimeData
                    from PyQt6.QtGui import QDrag, QPixmap
                    
                    
                    class CustomGraphicsView(QGraphicsView):
                        def __init__(self, parent=None):
                            super(QGraphicsView, self).__init__(parent)
                            self.setAcceptDrops(True)
                    
                        def dragMoveEvent(self, event: QtGui.QDragMoveEvent) -> None:
                            if event.mimeData().hasImage():
                                event.acceptProposedAction()
                    
                        def dropEvent(self, event):
                            print("In dropEvent of VIEW")
                            if self.scene():
                                event.accept()
                    
                                button = DragButton("XYZ")
                                point = button.mapFromGlobal(event.position())
                    
                                try:
                                    button.move(point)
                                    self.scene().addWidget(button)
                                    print("Button moved")
                                except Exception as e:
                                    print(e)
                            event.acceptProposedAction()
                    
                    
                    class CustomGraphicsScene(QGraphicsScene):
                        def __init__(self, parent=None):
                            super(QGraphicsScene, self).__init__(parent)
                    
                    
                    class DragButton(QPushButton):
                    
                        def __init__(self, *args, **kwargs):
                            super().__init__(*args, **kwargs)
                            
                        def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
                            print("In mousemovevent of dragbutton")
                            drag = QDrag(self)
                            
                            mime_data = QMimeData()
                            mime_data.setText(f"{event.pos().x()}, {event.pos().y()}")
                    
                            drag.setMimeData(mime_data)
                            drag.setHotSpot(event.pos())
                    
                            pixmap = QPixmap(self.size())
                            self.render(pixmap)
                            drag.setPixmap(pixmap)
                    
                            drag.exec(Qt.DropAction.MoveAction)     # Event loop blocking the main loop until complete
                    
                        
                    class Window(QWidget):
                    
                        def __init__(self):
                            super().__init__()
                            self.setFixedSize(960, 540)
                    
                            self.main_layout = QVBoxLayout()
                            self.button_layout = QHBoxLayout()
                    
                            self.graphics_scene = CustomGraphicsScene(parent=self)
                    
                            self.graphics_view = CustomGraphicsView(self)
                            self.graphics_view.setScene(self.graphics_scene)
                            page_size = 300
                            self.graphics_view.setFixedSize(page_size, int(page_size*(math.sqrt(2))))
                    
                            self.scrollArea = QScrollArea(self)
                            self.scrollArea.setAcceptDrops(True)
                            self.scrollArea.setWidgetResizable(True)
                    
                            self.scrollArea.setWidget(self.graphics_view)
                    
                            for button in ["A", "B", "C", "D"]:
                                _button = DragButton(button)
                                self.button_layout.addWidget(_button)
                    
                            self.main_layout.addLayout(self.button_layout)
                            self.main_layout.addWidget(self.scrollArea)
                            self.setLayout(self.main_layout)
                    
                    
                    app = QApplication([])
                    w = Window()
                    w.show()
                    
                    app.exec()
                    
                    SGaistS Offline
                    SGaistS Offline
                    SGaist
                    Lifetime Qt Champion
                    wrote on last edited by
                    #11

                    From the looks of it, you did not mapToScene.

                    Interested in AI ? www.idiap.ch
                    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                    M 1 Reply Last reply
                    1
                    • SGaistS SGaist

                      From the looks of it, you did not mapToScene.

                      M Offline
                      M Offline
                      MightyDigits
                      wrote on last edited by
                      #12

                      @SGaist First thing I tried was mapToScene(), but that didn't work.

                      What worked, is defining the sceneRect for the scene. Without it, there apparently are no native coordinates and they get set on the fly with each button drop, with the first drop being dead center in the scene.

                      All I had to do is add the following line:

                      self.graphics_scene = CustomGraphicsScene(parent=self)
                      # ADDED THE FOLLOWING LINE:
                      self.graphics_scene.setSceneRect(0, 0, page_size, int(page_size*math.sqrt(2)))
                      
                      self.graphics_view = CustomGraphicsView(self)
                      

                      There's still some polishing like scrollarea showing the scrollbars, basically just not slightly showing the full rect. If I increase the size of the scrollarea, it doesn't solve it. But that stuff is for worries later on.

                      Thanks anyway, you're a great help!

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved