Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Drag tabs between QTabWidgets
QtWS25 Last Chance

Drag tabs between QTabWidgets

Scheduled Pinned Locked Moved Solved General and Desktop
qtabwidgetqtabbartabdragdrag and drop
6 Posts 3 Posters 9.3k Views
  • 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.
  • Joel BodenmannJ Offline
    Joel BodenmannJ Offline
    Joel Bodenmann
    wrote on last edited by
    #1

    I have two QTabWidgets in a QSplitter. I'd like to be able to drag a tab from one QTabWidget to the other QTabWidget. Can anybody tell me how I can archive this?

    I saw this post: https://forum.qt.io/topic/38631/dragging-a-tab-from-qtabwidget-into-a-separate-window/5
    However, my problem is that the dragMoveEvent() from my QTabWidget subclass never gets called. I moved one step further and subclassed QTabBar but I have the same problem there: dragMoveEvent() is never called.

    Any ideas? As far as I can tell there's no widget property that must be enabled to enable drags, that seems to only exist for drops.
    If there's a better way to archive what I want to archive I'm all ears. After all eventually I'd like to have a visible "animation" when the tab is being dragged around.

    Industrial process automation software: https://simulton.com
    Embedded Graphics & GUI library: https://ugfx.io

    1 Reply Last reply
    0
    • Chris KawaC Online
      Chris KawaC Online
      Chris Kawa
      Lifetime Qt Champion
      wrote on last edited by
      #2

      The post you linked to is in error. The drag move event takes place on the target widget, not when you want to start a drag from the source. So in that example code it should be mouseMoveEvent, not dragMoveEvent.

      @Joel-Bodenmann said:

      As far as I can tell there's no widget property that must be enabled to enable drags

      There's no property but you must implement dragging yourself. See the docs for an example how to do it. That's for the source widget of the drag.

      The target has to have dropping enabled. When something is dragged over it it will receive drag enter event. If that event is accepted the drag move events will follow when you move above the target and finally a drop event when you release the mouse button.

      Joel BodenmannJ 1 Reply Last reply
      1
      • Chris KawaC Chris Kawa

        The post you linked to is in error. The drag move event takes place on the target widget, not when you want to start a drag from the source. So in that example code it should be mouseMoveEvent, not dragMoveEvent.

        @Joel-Bodenmann said:

        As far as I can tell there's no widget property that must be enabled to enable drags

        There's no property but you must implement dragging yourself. See the docs for an example how to do it. That's for the source widget of the drag.

        The target has to have dropping enabled. When something is dragged over it it will receive drag enter event. If that event is accepted the drag move events will follow when you move above the target and finally a drop event when you release the mouse button.

        Joel BodenmannJ Offline
        Joel BodenmannJ Offline
        Joel Bodenmann
        wrote on last edited by Joel Bodenmann
        #3

        @Chris-Kawa
        Thank you very much for clearing things up! I thought that there was something fishy...
        So if I am not mistaken I have to subclass QTabBar for this, and not QTabWidget. Is that correct?

        Can you tell me what I'd have to do to "animate" the moving tab? I'd like to have the tab (just the tab, thing with the string in it) under the mouse cursor while it is being dragged around.

        Industrial process automation software: https://simulton.com
        Embedded Graphics & GUI library: https://ugfx.io

        1 Reply Last reply
        0
        • Chris KawaC Online
          Chris KawaC Online
          Chris Kawa
          Lifetime Qt Champion
          wrote on last edited by
          #4

          So if I am not mistaken I have to subclass QTabBar for this, and not QTabWidget. Is that correct?

          Yes. You can still use QTabWidget for convenience and just set your customized tab bar on it.

          Can you tell me what I'd have to do to "animate" the moving tab?

          It's a bit complicated and I haven't actually tried it, so take this with a grain of salt, but I'd try something like this:

          First - you need to "remove" the original tab from the source. For this you can simply hide the tab for the exec() part of the drag and either show it back or permanently delete, depending on whether the drag was aborted or successful (you can get that from the exec return value).

          Next part is showing the tab under cursor when it is being dragged. For that you need to set a pixmap on the drag object that will represent the tab. You can probably get it easily with render() of the tab widget. Just calculate the right region based on the geometry of the tab you're dragging.

          Last part (and the hardest I think) is placing a tab in the target widget and moving it around. For that I'd create a "dummy" tab in the target in the drop enter event. If you get the drop leave event remove the dummy. If you get a drop event the dummy becomes the new tab. The tricky part is moving the tab around while the drag operation is still going on. I'd try to send a fake mouse press event to the tab widget in the drag enter, then fake mouse move events in the drop move event and finally a fake mouse release in the drop and leave drag events. This way the dummy tab should move along with the cursor when you move it.

          Again, I haven't tried it, but I think it should be doable this way.

          Joel BodenmannJ 1 Reply Last reply
          1
          • Chris KawaC Chris Kawa

            So if I am not mistaken I have to subclass QTabBar for this, and not QTabWidget. Is that correct?

            Yes. You can still use QTabWidget for convenience and just set your customized tab bar on it.

            Can you tell me what I'd have to do to "animate" the moving tab?

            It's a bit complicated and I haven't actually tried it, so take this with a grain of salt, but I'd try something like this:

            First - you need to "remove" the original tab from the source. For this you can simply hide the tab for the exec() part of the drag and either show it back or permanently delete, depending on whether the drag was aborted or successful (you can get that from the exec return value).

            Next part is showing the tab under cursor when it is being dragged. For that you need to set a pixmap on the drag object that will represent the tab. You can probably get it easily with render() of the tab widget. Just calculate the right region based on the geometry of the tab you're dragging.

            Last part (and the hardest I think) is placing a tab in the target widget and moving it around. For that I'd create a "dummy" tab in the target in the drop enter event. If you get the drop leave event remove the dummy. If you get a drop event the dummy becomes the new tab. The tricky part is moving the tab around while the drag operation is still going on. I'd try to send a fake mouse press event to the tab widget in the drag enter, then fake mouse move events in the drop move event and finally a fake mouse release in the drop and leave drag events. This way the dummy tab should move along with the cursor when you move it.

            Again, I haven't tried it, but I think it should be doable this way.

            Joel BodenmannJ Offline
            Joel BodenmannJ Offline
            Joel Bodenmann
            wrote on last edited by
            #5

            Thank you very much, this helped a lot!

            I got it working as per your recommendation. I will publish the resulting widget in the following days for people that stumble across the same issue in the future.

            Industrial process automation software: https://simulton.com
            Embedded Graphics & GUI library: https://ugfx.io

            1 Reply Last reply
            0
            • A Offline
              A Offline
              ARussell
              wrote on last edited by ARussell
              #6

              Had a similar Issue. Was able to find a solution. Below is a generic PyQt5 example that solves the problem using right click.

              import sys
              
              from PyQt5.QtGui import *
              from PyQt5.QtWidgets import *
              from PyQt5.QtCore import *
              
              class Tabs(QTabWidget):
                  def __init__(self, parent):
                      super().__init__(parent)
                      self.parent = parent
                      self.setAcceptDrops(True)
                      self.tabBar = self.tabBar()
                      self.tabBar.setMouseTracking(True)
                      self.indexTab = None
                      self.setMovable(True)
              
                      self.addTab(QWidget(self), 'Tab One')
                      self.addTab(QWidget(self), 'Tab Two')
              
                  def mouseMoveEvent(self, e):
                      if e.buttons() != Qt.RightButton:
                          return
              
                      globalPos = self.mapToGlobal(e.pos())
                      tabBar = self.tabBar
                      posInTab = tabBar.mapFromGlobal(globalPos)
                      self.indexTab = tabBar.tabAt(e.pos())
                      tabRect = tabBar.tabRect(self.indexTab)
              
                      pixmap = QPixmap(tabRect.size())
                      tabBar.render(pixmap,QPoint(),QRegion(tabRect))
                      mimeData = QMimeData()
                      drag = QDrag(tabBar)
                      drag.setMimeData(mimeData)
                      drag.setPixmap(pixmap)
                      cursor = QCursor(Qt.OpenHandCursor)
                      drag.setHotSpot(e.pos() - posInTab)
                      drag.setDragCursor(cursor.pixmap(),Qt.MoveAction)
                      dropAction = drag.exec_(Qt.MoveAction)
              
              
                  def dragEnterEvent(self, e):
                      e.accept()
                      if e.source().parentWidget() != self:
                          return
              
                      print(self.indexOf(self.widget(self.indexTab)))
                      self.parent.TABINDEX = self.indexOf(self.widget(self.indexTab))
              
              
                  def dragLeaveEvent(self,e):
                      e.accept()
              
              
                  def dropEvent(self, e):
                      print(self.parent.TABINDEX)
                      if e.source().parentWidget() == self:
                          return
              
                      e.setDropAction(Qt.MoveAction)
                      e.accept()
                      counter = self.count()
              
                      if counter == 0:
                          self.addTab(e.source().parentWidget().widget(self.parent.TABINDEX),e.source().tabText(self.parent.TABINDEX))
                      else:
                          self.insertTab(counter + 1 ,e.source().parentWidget().widget(self.parent.TABINDEX),e.source().tabText(self.parent.TABINDEX))
              
              
              class Window(QWidget):
                  def __init__(self):
              
                      super().__init__()
              
                      self.TABINDEX = 0
                      tabWidgetOne = Tabs(self)
                      tabWidgetTwo = Tabs(self)
              
                      layout = QHBoxLayout()
              
                      self.moveWidget = None
              
                      layout.addWidget(tabWidgetOne)
                      layout.addWidget(tabWidgetTwo)
              
                      self.setLayout(layout)
              
              if __name__ == '__main__':
              
                  app = QApplication(sys.argv)
                  window = Window()
                  window.show()
                  sys.exit(app.exec_())
              
              1 Reply Last reply
              0
              • NarutoblazeN Narutoblaze referenced this topic on
              • NarutoblazeN Narutoblaze referenced this topic on

              • Login

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