在拆分的 Widget PyQt Qt 中,将 Tab 从 QtabBar 拖放到其他 QtabBar

Posted

技术标签:

【中文标题】在拆分的 Widget PyQt Qt 中,将 Tab 从 QtabBar 拖放到其他 QtabBar【英文标题】:drag and drop a Tab from a QtabBar to other QtabBar in a splitted Widget PyQt Qt 【发布时间】:2017-09-24 16:48:27 【问题描述】:

我该如何存档: - 我需要将选项卡从其 tabBar 拖放到拆分小部件中的其他 tabBar? 我已经对 QtabBar 进行了子类化并实现了拖放事件,我已经可以使用正确的像素图等将其拖放到同一个 tabBar 中,但不能放在另一个 tabBar 中。 在输出中收到此错误,告诉我我没有提供正确的参数,这是我为制作和示例而简化的代码,以及窗口的 .JPG。

class EsceneTest(qg.QMainWindow):
    def __init__(self,parent=getMayaWindow()):
        super(EsceneTest,self).__init__(parent)
        #---------------------------------------------------------#        
        #check for open Window first
        winName = windowTitle
        if cmds.window(winName, exists =1):
            cmds.deleteUI(winName, wnd=True)

        self.setAttribute(qc.Qt.WA_DeleteOnClose) 
        self._initUI()      

    def _initUI(self):                     
        self.setObjectName(windowObject)
        self.setWindowTitle(windowTitle)
        self.setMinimumWidth(450)
        self.setMinimumHeight(500)
        self.resize(1080, 800) # re-size the window
        centralWidget = qg.QWidget()
        centralWidget.setObjectName('centralWidget')
        self.setCentralWidget(centralWidget)
        central_layout = qg.QVBoxLayout(centralWidget)

        ######################
        # tab container
        #
        self.tabWidget = qg.QTabWidget()
        self.tabWidget.setAcceptDrops(True)
        self.tab_layout = qg.QVBoxLayout(self.tabWidget)
        central_layout.addWidget(self.tabWidget)

        #######################
        # TabBar
        #                 
        custom_tabbar = ColtabBar()
        self.tabWidget.setTabBar(custom_tabbar)        

        #######################
        # ViewportTab
        #         
        tabCentral_wdg = qg.QWidget()
        self.top_lyt = qg.QVBoxLayout(tabCentral_wdg)
        self.tab_layout.addLayout(self.top_lyt)       

        fixedHBox_lyt = qg.QHBoxLayout()
        self.top_lyt.addLayout(fixedHBox_lyt)   
        self.tabWidget.addTab(tabCentral_wdg,'- Viewport')

        #######################
        # Example ExtraTab
        #        
        tabTwo_wdg = qg.QWidget()
        tabTwo_wdg_lyt = qg.QHBoxLayout(tabTwo_wdg)
        self.tab_layout.addLayout(tabTwo_wdg_lyt)        
        label = qg.QLabel(' -- This is an example -- ')
        label.setStyleSheet("""
                            background : qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(53, 57, 60), stop:1 rgb(33, 34, 36));
                            border-style : none;
                            font-size: 40px;
                            font-family: Calibri;
                            color : rgb(200,200,100);
                        """)
        label.setAlignment(qc.Qt.AlignVCenter | qc.Qt.AlignHCenter )
        tabTwo_wdg_lyt.addWidget(label)       
        tab_panel_lyt = qg.QVBoxLayout(label)    
        self.tabWidget.addTab(tabTwo_wdg,'- ExtraExample') 

        ############################
        #   Q Splitter Widget to insert the dragged Tabs
        #
        split = qg.QSplitter(qc.Qt.Orientation.Vertical, self)
        central_layout.addWidget(split)
        tab_splitted = qg.QTabWidget()
        split.setLayout(qg.QVBoxLayout())
        split.insertWidget(0,tab_splitted)
        tabBar_2 = ColtabBar()
        tab_splitted.setTabBar(tabBar_2)
        tabBar_2.addTab('- Insert-Here')
#---------------------------------------------------------------------------------------------#

class ColtabBar(qg.QTabBar):
    def __init__(self):
        super(ColtabBar, self).__init__()
        self.indexTab = None        
        self.setAcceptDrops(True)   

    ##################################
    # Events
    def mouseMoveEvent(self, e):
        if e.buttons() != qc.Qt.MiddleButton:
            return

        globalPos = self.mapToGlobal(e.pos())
        posInTab = self.mapFromGlobal(globalPos)
        self.indexTab = self.tabAt(e.pos())
        tabRect = self.tabRect(self.indexTab)


        pixmap = qg.QPixmap(tabRect.size())
        self.render(pixmap,qc.QPoint(),qg.QRegion(tabRect))
        mimeData = qc.QMimeData()
        drag = qg.QDrag(self)
        drag.setMimeData(mimeData)
        drag.setPixmap(pixmap)

        cursor = qg.QCursor(qc.Qt.OpenHandCursor)    
        drag.setHotSpot(e.pos() - posInTab)
        drag.setDragCursor(cursor.pixmap(),qc.Qt.MoveAction)       
        dropAction = drag.exec_(qc.Qt.MoveAction)


    def mousePressEvent(self, e):
        #super(qg.QWidget).mousePressEvent(e)
        if e.button() == qc.Qt.RightButton:
            print('press')

        if e.button() == qc.Qt.LeftButton:            
            globalPos = self.mapToGlobal(e.pos())
            posInTab = self.mapFromGlobal(globalPos)
            self.indexTab = self.tabAt(e.pos())
            self.setCurrentIndex(self.indexTab)

    def dragEnterEvent(self, e):
        e.accept()


    def dropEvent(self, e):
        e.setDropAction(qc.Qt.MoveAction)
        e.accept()        
        self.insertTab(self.indexTab, self.tabText(self.indexTab)) 
        self.removeTab(self.indexTab)

ColtabBar 是我执行拖放事件的子类。

图片->

【问题讨论】:

你考虑过使用 QDockWidgets 吗?用户可以通过标签和拖动它们 是的,我以这种方式尝试过,但我做到了,但不是我想要的方式..我喜欢做我当时脑子里想的事情,好吧,终于做到了,结束12 小时 ufff 现在将发布结果 :) 非常感谢您的建议。 @SteakOverflow 【参考方案1】:

经过几个小时,今天在网上吃了很多很多 Qt 页面,我以自己的方式做到了,现在我可以将选项卡从一个选项卡拖放到另一个选项卡,反之亦然,而不仅仅是从选择当前标签,我可以在标签栏中选择我想要的每个标签,并在拖动时向我显示小标签的像素图......

代码如下:

** 已编辑 **

我让它更加防弹,当我使用超过 2 个带有索引的选项卡时出现错误,现在工作得更好,当我将它放在同一个小部件中时,它返回事件而不执行代码,加上悬停选项卡也可以用鼠标右键选择..我希望这对将来的任何人都有帮助。

TABINDEX = int()

def getTabIndex(index):
    global TABINDEX
    if index == -1 or index == TABINDEX:
        return
    TABINDEX = index
    print (TABINDEX)
    return TABINDEX


class ColtTab(qg.QTabWidget):
    def __init__(self):
        super(ColtTab,self).__init__()
        self.setAcceptDrops(True)
        self.tabBar = self.tabBar()
        self.tabBar.setMouseTracking(True)
        self.setDocumentMode(True)
        self.indexTab = int()
        self.setMovable(True)
        self.setStyleSheet(style_sheet_file)


    # test for hovering and selecting tabs automatic while mouser over then - not working for now...
    def eventFilter(self, obj, event):
        if obj == self.tabBar:
            if event.type() == qc.QEvent.MouseMove:
                index=self.tabBar.tabAt(event.pos())
                self.tabBar.setCurrentIndex (index)
                return True
            else:
                return
        else:
            return

    ##################################
    # Events
    #
    def mouseMoveEvent(self, e):
        if e.buttons() != qc.Qt.MiddleButton:
            return

        globalPos = self.mapToGlobal(e.pos())
        #print(globalPos)
        tabBar = self.tabBar
        #print(tabBar)
        posInTab = tabBar.mapFromGlobal(globalPos)
        #print(posInTab)
        self.indexTab = tabBar.tabAt(e.pos())
        #print(self.indexTab)
        tabRect = tabBar.tabRect(self.indexTab)
        #print(tabRect)
        #print(tabRect.size())

        pixmap = qg.QPixmap(tabRect.size())
        tabBar.render(pixmap,qc.QPoint(),qg.QRegion(tabRect))
        mimeData = qc.QMimeData()
        drag = qg.QDrag(tabBar)
        drag.setMimeData(mimeData)
        drag.setPixmap(pixmap)
        cursor = qg.QCursor(qc.Qt.OpenHandCursor)
        drag.setHotSpot(e.pos() - posInTab)
        drag.setDragCursor(cursor.pixmap(),qc.Qt.MoveAction)
        dropAction = drag.exec_(qc.Qt.MoveAction)


    def mousePressEvent(self, e):
        if e.button() == qc.Qt.RightButton:
            self.tabBar.installEventFilter(self)
            print('Right button pressed')

        super(ColtTab, self).mousePressEvent(e)


    def dragEnterEvent(self, e):
        e.accept()
        if e.source().parentWidget() != self:
            return

        # Helper function for retrieving the Tab index into a global Var
        getTabIndex(self.indexOf(self.widget(self.indexTab)))


    def dragLeaveEvent(self,e):
        e.accept()


    def dropEvent(self, e):
        if e.source().parentWidget() == self:
            return

        e.setDropAction(qc.Qt.MoveAction)
        e.accept()
        counter = self.count()

        if counter == 0:
            self.addTab(e.source().parentWidget().widget(TABINDEX),e.source().tabText(TABINDEX))
        else:
            self.insertTab(counter + 1 ,e.source().parentWidget().widget(TABINDEX),e.source().tabText(TABINDEX))

        print ('Tab dropped')

    def mouseReleaseEvent(self, e):
        if e.button() == qc.Qt.RightButton:
            print('Right button released')
            self.tabBar.removeEventFilter(self)

        super(ColtTab, self).mouseReleaseEvent(e)


    #---------------------------------------------------------------------------------#

图片->

【讨论】:

【参考方案2】:

发现这个帖子很有用。使用您的解决方案在 PyQt5 中创建了一个自包含的通用示例。将来可能会帮助某人。

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_())

【讨论】:

【参考方案3】:

这是根据别人的代码修改的,可能是上面的示例之一。

无论如何,这是标签到标签或标签到窗口拖放标签内容的最少代码。

from PyQt5.QtWidgets import QTabWidget
from PyQt5.QtCore import Qt, QPoint, QMimeData
from PyQt5.QtGui import QPixmap, QRegion, QDrag, QCursor

class TabWidget(QTabWidget):
   def __init__(self, parent=None, new=None):
      super().__init__(parent)
      self.setAcceptDrops(True)
      self.tabBar().setMouseTracking(True)
      self.setMovable(True)
      if new:
         TabWidget.setup(self)

   def __setstate__(self, data):
      self.__init__(new=False)
      self.setParent(data['parent'])
      for widget, tabname in data['tabs']:
         self.addTab(widget, tabname)
      TabWidget.setup(self)

   def __getstate__(self):
      data = 
         'parent' : self.parent(),
         'tabs' : [],
      
      tab_list = data['tabs']
      for k in range(self.count()):
         tab_name = self.tabText(k)
         widget = self.widget(k)
         tab_list.append((widget, tab_name))
      return data

   def setup(self):
      pass

   def mouseMoveEvent(self, e):
      if e.buttons() != Qt.RightButton:
         return

      globalPos = self.mapToGlobal(e.pos())
      tabBar = self.tabBar()
      posInTab = tabBar.mapFromGlobal(globalPos)
      index = tabBar.tabAt(e.pos())
      tabBar.dragged_content = self.widget(index)
      tabBar.dragged_tabname = self.tabText(index)
      tabRect = tabBar.tabRect(index)

      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)
      drag.exec_(Qt.MoveAction)

   def dragEnterEvent(self, e):
      e.accept()
      #self.parent().dragged_index = self.indexOf(self.widget(self.dragged_index))

   def dragLeaveEvent(self,e):
      e.accept()

   def dropEvent(self, e):
      if e.source().parentWidget() == self:
         return

      e.setDropAction(Qt.MoveAction)
      e.accept()
      tabBar = e.source()
      self.addTab(tabBar.dragged_content, tabBar.dragged_tabname)


if __name__ == '__main__':
   from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout
   import sys

   class Window(QWidget):
      def __init__(self):

         super().__init__()

         self.dragged_index = None
         tabWidgetOne = TabWidget(self)
         tabWidgetTwo = TabWidget(self)
         tabWidgetOne.addTab(QWidget(), "tab1")
         tabWidgetTwo.addTab(QWidget(), "tab2")

         layout = QHBoxLayout()

         self.moveWidget = None

         layout.addWidget(tabWidgetOne)
         layout.addWidget(tabWidgetTwo)

         self.setLayout(layout)

   app = QApplication(sys.argv)
   window = Window()
   window1 = Window()
   window.show()
   window1.show()
   sys.exit(app.exec_())

【讨论】:

以上是关于在拆分的 Widget PyQt Qt 中,将 Tab 从 QtabBar 拖放到其他 QtabBar的主要内容,如果未能解决你的问题,请参考以下文章

Qt/PyQt:QGraphicsItem vs. QGraphicsWidget 几何、位置、鼠标交互

将 Matplotlib 图形嵌入到小部件中 - PyQt5

PyQt / Qt Designer - 没有插件的提升小部件的额外参数

如何配置 Qt Designer 以在 OSX 中加载 PyQt 小部件?

qt qt向table widget插入列数据

在 pyqt5 小部件中更新 matplotlib