Python Forum

Full Version: Drag items across tabs
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
When a dragged item hovers on a tab, I want the view to switch on this tab. The problem is two fold;

1. How to trigger new events once the mouse entered the tagBar
2. How to know which tab is being hovered

I tried subclassing the QTabBar, and catching dragMoveEvent, but the event is never triggered. Then I tried to reset or ignore the dragEnterEvent, so it might be trigered again without leaving the tabBar. What I tried are the commented lines below;

#!/usr/bin/python3
import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class TabBar(QtWidgets.QTabBar):
    def __init__(self, parent):
        super().__init__()
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        print("enter", event)
        #event.ignore()
        #QtCore.QCoreApplication.postEvent(self, QtGui.QDragLeaveEvent())
        #QtCore.QCoreApplication.sendEvent(self, QtGui.QDragLeaveEvent())
        #QtCore.QObject.event(self, QtGui.QDragLeaveEvent())
        #self.dragLeaveEvent(QtGui.QDragLeaveEvent())

    # This event is never called
    def dragMoveEvent(self, event):
        print("move", event)


class TabWidget(QtWidgets.QTabWidget):
    def __init__(self, parent):
        super().__init__()
        tab1 = QtWidgets.QTreeWidget()
        self.setTabBar(TabBar(self))
        self.addTab(tab1, "1st tab")
        self.addTab(QtWidgets.QTreeWidget(), "2nd tab")
        self.addTab(QtWidgets.QTreeWidget(), "3rd tab")

        item = QtWidgets.QTreeWidgetItem()
        item.setText(0, "drag me")
        tab1.addTopLevelItem(item)
        tab1.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)


class Main(QtWidgets.QMainWindow):
    def __init__(self, parent):
        super().__init__()
        self.ui = QtWidgets.QWidget(self)
        self.ui.tab = TabWidget(self)
        self.ui.layout = QtWidgets.QVBoxLayout()
        self.ui.layout.addWidget(self.ui.tab)
        self.ui.setLayout(self.ui.layout)
        self.setCentralWidget(self.ui)
        self.show()


if __name__== '__main__':
    app = QtWidgets.QApplication(sys.argv)
    gui = Main(app)
    sys.exit(app.exec_())
I wonder if this is possible to do, as the tabBar is seen as a single object containing all the tabs, and I don't know how to access them directly. Any ideas?
look at this example on github

It shows how to drag/drop/move between TreeWidgets
Thanks for your reply. Yes I saw this already, but it is not what I am looking for. Accepting a drop in the TreeWidget is easy, but I want the tab to change once it is hovered, just like when something is hovered over tabs in Chrome or Firefox. The problem is that only a single even occur once the drag enter the tagbar, but I couldn't find how todo it for separate tabs.
to get dragMoveEvent printing add to dragEnterEvent:

event.accept()

I tried it a bit, I can change the tab index with an eventFilter, but it works only after mouse button release.

#!/usr/bin/python3
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
 
 
class Main(QtWidgets.QMainWindow):
    def __init__(self, parent):
        super().__init__()
        self.tab1 = QtWidgets.QTreeWidget()
        self.tab2 = QtWidgets.QTreeWidget()
        self.tab3 = QtWidgets.QTreeWidget()
        self.tabIndex = 0
        item = QtWidgets.QTreeWidgetItem()
        item.setText(0, "drag me")
        self.tab1.addTopLevelItem(item)
        self.tab1.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)
        self.TabBar = QtWidgets.QTabBar()
        self.ui = QtWidgets.QWidget(self)
        self.ui.tab = QtWidgets.QTabWidget(self)
        self.ui.tab.setTabBar(self.TabBar)
        self.ui.tab.addTab(self.tab1, "1st tab")
        self.ui.tab.addTab(self.tab2, "2nd tab")
        self.ui.tab.addTab(self.tab3, "3rd tab")
        self.tab2.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
        self.tab3.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
        self.ui.layout = QtWidgets.QVBoxLayout()
        self.ui.layout.addWidget(self.ui.tab)
        self.ui.setLayout(self.ui.layout)
        self.setCentralWidget(self.ui)

        self.TabBar.setCurrentIndex(self.tabIndex)
        self.mouseHoverOnTabBar()

        self.setAcceptDrops(True)
        self.show()

    def mouseHoverOnTabBar(self):
        self.ui.tab=self.ui.tab.tabBar()
        self.ui.tab.setMouseTracking(True)
        self.ui.tab.installEventFilter(self)

    def eventFilter(self, obj, event):
        if obj == self.ui.tab:
            if event.type() == QtCore.QEvent.Enter:
                pos = event.pos()
                self.tabIndex=self.TabBar.tabAt(pos)
                print("tab index:", self.tabIndex)
#                self.TabBar.setCurrentIndex (self.tabIndex)
#                self.TabBar.setTabEnabled(self.tabIndex, True)
                self.dragEnterEvent(event)
                event.accept()
                return True
        return super().eventFilter(obj, event)

    def dragEnterEvent(self, event):
        print("drag enter")
#        pos = QtGui.QCursor.pos()
#        self.tabIndex=self.TabBar.tabAt(pos)
#        print("tab index:", self.tabIndex, "\npos:", pos)
        self.TabBar.setCurrentIndex (self.tabIndex)
        event.accept()

    def dragMoveEvent(self, event):
        print("drag move enter")
        event.accept()

    def hoverEnter(self, event):
        print ("On Hover")
        event.accept()
 
if __name__== '__main__':
    app = QtWidgets.QApplication(sys.argv)
    gui = Main(app)
    sys.exit(app.exec_())
forget the last post. It's so easy

setChangeCurrentOnDrag

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
 
 
class Main(QtWidgets.QMainWindow):
    def __init__(self, parent):
        super().__init__()

        self.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
        self.tab1 = QtWidgets.QTreeWidget()
        self.tab2 = QtWidgets.QTreeWidget()
        self.tab3 = QtWidgets.QTreeWidget()
        self.tabIndex = 0
        item = QtWidgets.QTreeWidgetItem()
        item.setText(0, "drag me")
        self.tab1.addTopLevelItem(item)
        self.tab1.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)
        self.TabBar = QtWidgets.QTabBar()

        self.ui = QtWidgets.QWidget(self)
        self.ui.tab = QtWidgets.QTabWidget(self)
        self.ui.tab.setTabBar(self.TabBar)
        self.ui.tab.addTab(self.tab1, "1st tab")
        self.ui.tab.addTab(self.tab2, "2nd tab")
        self.ui.tab.addTab(self.tab3, "3rd tab")
        self.tab2.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
        self.tab3.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
        self.ui.layout = QtWidgets.QVBoxLayout()
        self.ui.layout.addWidget(self.ui.tab)
        self.ui.setLayout(self.ui.layout)
        self.setCentralWidget(self.ui)

        self.TabBar.setCurrentIndex(self.tabIndex)
        self.TabBar.setChangeCurrentOnDrag(True)
        self.TabBar.setAcceptDrops(True)

        self.setAcceptDrops(True)
        self.show()

 
if __name__== '__main__':
    app = QtWidgets.QApplication(sys.argv)
    gui = Main(app)
    sys.exit(app.exec_())
dragMoveEvent required that event.acceptProposedAction() is called in dragEnterEvent. So by using tabAt(pos) as you suggested, this work:

#!/usr/bin/python3
import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class TabBar(QtWidgets.QTabBar):
    def __init__(self, parent):
        super().__init__()
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        event.acceptProposedAction()
        QtWidgets.QTabBar.dragEnterEvent(self, event)

    def dragMoveEvent(self, event):
        pos = event.pos()
        index = self.tabAt(pos)
        self.setCurrentIndex(index)


class TabWidget(QtWidgets.QTabWidget):
    def __init__(self, parent):
        super().__init__()
        tab1 = QtWidgets.QTreeWidget()
        self.setTabBar(TabBar(self))
        self.addTab(tab1, "1st tab")
        self.addTab(QtWidgets.QTreeWidget(), "2nd tab")
        self.addTab(QtWidgets.QTreeWidget(), "3rd tab")

        item = QtWidgets.QTreeWidgetItem()
        item.setText(0, "drag me")
        tab1.addTopLevelItem(item)
        tab1.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)


class Main(QtWidgets.QMainWindow):
    def __init__(self, parent):
        super().__init__()
        self.ui = QtWidgets.QWidget(self)
        self.ui.tab = TabWidget(self)
        self.ui.layout = QtWidgets.QVBoxLayout()
        self.ui.layout.addWidget(self.ui.tab)
        self.ui.setLayout(self.ui.layout)
        self.setCentralWidget(self.ui)
        self.show()


if __name__== '__main__':
    app = QtWidgets.QApplication(sys.argv)
    gui = Main(app)
    sys.exit(app.exec_())

(Sep-01-2019, 06:08 PM)Axel_Erfurt Wrote: [ -> ]forget the last post. It's so easy setChangeCurrentOnDrag

Thanks for the tip, I did not see this in the docs.. Sometimes the solutions are simpler than expected Doh