Bottom Page

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
 [PyQt] Drag items across tabs
#1
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?
Quote
#2
look at this example on github

It shows how to drag/drop/move between TreeWidgets
Quote
#3
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.
Quote
#4
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_())
Alfalfa likes this post
Quote
#5
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_())
Alfalfa likes this post
Quote
#6
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
Quote

Top Page

Possibly Related Threads...
Thread Author Replies Views Last Post
  [Tkinter] Adding space between Notebook tabs Columbo 4 460 Jul-10-2019, 10:46 PM
Last Post: Columbo
  [Tkinter] How to get a tabs works exactly same as google chrome sarthak260 0 467 Mar-07-2019, 10:45 AM
Last Post: sarthak260
  [Tkinter] How to create multilple tabs in tkinter from different classes Rishav 5 8,866 Jul-11-2018, 11:59 AM
Last Post: CCChris91
  [PyQt] Drag and drop converter Raures 0 1,508 Oct-01-2017, 07:44 PM
Last Post: Raures
  How to create mutiple tabs in tkinter using oops Rishav 2 2,652 Jul-12-2017, 04:43 PM
Last Post: Rishav
  Drag and Drop GUI crystallized_neuron 3 10,246 Apr-26-2017, 08:41 PM
Last Post: joe_anonimist
  Drag and drop image object vijaysagi 1 7,062 Apr-06-2017, 09:36 AM
Last Post: wuf

Forum Jump:


Users browsing this thread: 1 Guest(s)