代码之家  ›  专栏  ›  技术社区  ›  ymmx

是否可以拖动QTabWidget并打开一个新窗口,其中包含pyqt5中此选项卡中的内容?

  •  0
  • ymmx  · 技术社区  · 8 年前

    我想知道是否可以通过单击并拖动一个选项卡来打开一个包含该选项卡中内容的新窗口。如果可能的话,我还想做相反的事情:将新窗口拖到选项卡内(最初的位置)。

    我不知道该怎么开始。我在一些论坛上读到,所有这些都必须进行编码,但我不知道Qt是否允许一些设施这样做?

    以下代码作为起点:

    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
    import sys
    
    
    class SurfViewer(QMainWindow):
        def __init__(self, parent=None):
            super(SurfViewer, self).__init__()
            self.parent = parent 
            self.centralTabs= QTabWidget()
            self.setCentralWidget(self.centralTabs)
            self.setFixedWidth(200)
            self.setFixedHeight(200)
    
            #tab 1 
            self.tab_1 = QWidget()
            self.centralTabs.addTab(self.tab_1,"Label") 
            vbox = QVBoxLayout()
            Label = QLabel('Tab1')
            Label.setFixedWidth(180)
            LineEdit = QLineEdit('Tab1')
            LineEdit.setFixedWidth(180)
            vbox.addWidget(Label)
            vbox.addWidget(LineEdit)
            vbox.setAlignment(Qt.AlignTop)
            self.tab_1.setLayout(vbox)
    
            #tab 2 
            self.tab_2 = QWidget()
            self.centralTabs.addTab(self.tab_2,"Label")
            vbox = QVBoxLayout()
            Label = QLabel('Tab2')
            Label.setFixedWidth(180)
            LineEdit = QLineEdit('Tab2')
            LineEdit.setFixedWidth(180)
            vbox.addWidget(Label)
            vbox.addWidget(LineEdit)
            vbox.setAlignment(Qt.AlignTop)
            self.tab_2.setLayout(vbox)
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        ex = SurfViewer(app)
        ex.setWindowTitle('window') 
        ex.show()
        sys.exit(app.exec_( ))
    

    这与我的Qt水平相差甚远,所以我需要一些帮助。 如果我完全理解,我需要重新实现 mousePressEvent() 以及 dragMoveEvent() QTabWidget ? 他们的主题是: In PyQt4, is it possible to detach tabs from a QTabWidget? 但它是用PYQT4的,我用的是PYQT5。

    使现代化

    所以,根据 在PyQt4中,是否可以从QTabWidget分离选项卡? 转换为pyqt5后

    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
    import sys
    
    class DetachableTabWidget(QTabWidget):
        def __init__(self, parent=None):
            QTabWidget.__init__(self, parent)
    
            self.tabBar = self.TabBar(self)
            self.tabBar.onDetachTabSignal.connect(self.detachTab)
            self.tabBar.onMoveTabSignal.connect(self.moveTab)
    
            self.setTabBar(self.tabBar)
    
    
        ##
        #  The default movable functionality of QTabWidget must remain disabled
        #  so as not to conflict with the added features
        def setMovable(self, movable):
            pass
    
        ##
        #  Move a tab from one position (index) to another
        #
        #  @param    fromIndex    the original index location of the tab
        #  @param    toIndex      the new index location of the tab
        @pyqtSlot(int, int)
        def moveTab(self, fromIndex, toIndex):
            widget = self.widget(fromIndex)
            icon = self.tabIcon(fromIndex)
            text = self.tabText(fromIndex)
    
            self.removeTab(fromIndex)
            self.insertTab(toIndex, widget, icon, text)
            self.setCurrentIndex(toIndex)
    
    
        ##
        #  Detach the tab by removing it's contents and placing them in
        #  a DetachedTab dialog
        #
        #  @param    index    the index location of the tab to be detached
        #  @param    point    the screen position for creating the new DetachedTab dialog
        @pyqtSlot(int, QPoint)
        def detachTab(self, index, point):
    
            # Get the tab content
            name = self.tabText(index)
            icon = self.tabIcon(index)        
            if icon.isNull():
                icon = self.window().windowIcon()              
            contentWidget = self.widget(index)
            contentWidgetRect = contentWidget.frameGeometry()
    
            # Create a new detached tab window
            detachedTab = self.DetachedTab(contentWidget, self.parentWidget())
            detachedTab.setWindowModality(Qt.NonModal)
            detachedTab.setWindowTitle(name)
            detachedTab.setWindowIcon(icon)
            detachedTab.setObjectName(name)
            detachedTab.setGeometry(contentWidgetRect)
            detachedTab.onCloseSignal.connect(self.attachTab)
            detachedTab.move(point)
            detachedTab.show()
    
    
        ##
        #  Re-attach the tab by removing the content from the DetachedTab dialog,
        #  closing it, and placing the content back into the DetachableTabWidget
        #
        #  @param    contentWidget    the content widget from the DetachedTab dialog
        #  @param    name             the name of the detached tab
        #  @param    icon             the window icon for the detached tab
        @pyqtSlot(QWidget, type(''), QIcon)
        def attachTab(self, contentWidget, name, icon):
    
            # Make the content widget a child of this widget
            contentWidget.setParent(self)
    
    
            # Create an image from the given icon
            if not icon.isNull():
                tabIconPixmap = icon.pixmap(icon.availableSizes()[0])
                tabIconImage = tabIconPixmap.toImage()
            else:
                tabIconImage = None
    
    
            # Create an image of the main window icon
            if not icon.isNull():
                windowIconPixmap = self.window().windowIcon().pixmap(icon.availableSizes()[0])
                windowIconImage = windowIconPixmap.toImage()
            else:
                windowIconImage = None
    
    
            # Determine if the given image and the main window icon are the same.
            # If they are, then do not add the icon to the tab
            if tabIconImage == windowIconImage:
                index = self.addTab(contentWidget, name)
            else:
                index = self.addTab(contentWidget, icon, name)
    
    
            # Make this tab the current tab
            if index > -1:
                self.setCurrentIndex(index)
    
    
        ##
        #  When a tab is detached, the contents are placed into this QDialog.  The tab
        #  can be re-attached by closing the dialog or by double clicking on its
        #  window frame.
        class DetachedTab(QDialog):
            onCloseSignal = pyqtSignal(QWidget,type(''), QIcon)
    
            def __init__(self, contentWidget, parent=None):
                QDialog.__init__(self, parent)
    
                layout = QVBoxLayout(self)            
                self.contentWidget = contentWidget            
                layout.addWidget(self.contentWidget)
                self.contentWidget.show()
                self.setWindowFlags(Qt.Window)
    
    
            ##
            #  Capture a double click event on the dialog's window frame
            #
            #  @param    event    an event
            #
            #  @return            true if the event was recognized
            def event(self, event):
    
                # If the event type is QEvent.NonClientAreaMouseButtonDblClick then
                # close the dialog
                if event.type() == 176:
                    event.accept()
                    self.close()
    
                return QDialog.event(self, event)
    
    
            ##
            #  If the dialog is closed, emit the onCloseSignal and give the
            #  content widget back to the DetachableTabWidget
            #
            #  @param    event    a close event
            def closeEvent(self, event):
                self.onCloseSignal.emit(self.contentWidget, self.objectName(), self.windowIcon())
    
    
        ##
        #  The TabBar class re-implements some of the functionality of the QTabBar widget
        class TabBar(QTabBar):
            onDetachTabSignal = pyqtSignal(int, QPoint)
            onMoveTabSignal = pyqtSignal(int, int)
    
            def __init__(self, parent=None):
                QTabBar.__init__(self, parent)
    
                self.setAcceptDrops(True)
                self.setElideMode(Qt.ElideRight)
                self.setSelectionBehaviorOnRemove(QTabBar.SelectLeftTab)
    
                self.dragStartPos = QPoint()
                self.dragDropedPos = QPoint()
                self.mouseCursor = QCursor()
                self.dragInitiated = False
    
    
            ##
            #  Send the onDetachTabSignal when a tab is double clicked
            #
            #  @param    event    a mouse double click event
            def mouseDoubleClickEvent(self, event):
                event.accept()
                self.onDetachTabSignal.emit(self.tabAt(event.pos()), self.mouseCursor.pos())
    
    
            ##
            #  Set the starting position for a drag event when the mouse button is pressed
            #
            #  @param    event    a mouse press event
            def mousePressEvent(self, event):
                if event.button() == Qt.LeftButton:
                    self.dragStartPos = event.pos()
    
                self.dragDropedPos.setX(0)
                self.dragDropedPos.setY(0)
    
                self.dragInitiated = False
    
                QTabBar.mousePressEvent(self, event)
    
    
            ##
            #  Determine if the current movement is a drag.  If it is, convert it into a QDrag.  If the
            #  drag ends inside the tab bar, emit an onMoveTabSignal.  If the drag ends outside the tab
            #  bar, emit an onDetachTabSignal.
            #
            #  @param    event    a mouse move event
            def mouseMoveEvent(self, event):
    
                # Determine if the current movement is detected as a drag
                if not self.dragStartPos.isNull() and ((event.pos() - self.dragStartPos).manhattanLength() < QApplication.startDragDistance()):
                    self.dragInitiated = True
    
                # If the current movement is a drag initiated by the left button
                if (((event.buttons() & Qt.LeftButton)) and self.dragInitiated):
    
                    # Stop the move event
                    finishMoveEvent = QMouseEvent(QEvent.MouseMove, event.pos(), Qt.NoButton, Qt.NoButton, Qt.NoModifier)
                    QTabBar.mouseMoveEvent(self, finishMoveEvent)
    
                    # Convert the move event into a drag
                    drag = QDrag(self)
                    mimeData = QMimeData()
                    mimeData.setData('action', b'application/tab-detach')
                    drag.setMimeData(mimeData)
    
                    #Create the appearance of dragging the tab content
                    pixmap = self.parentWidget().grab()
                    targetPixmap = QPixmap(pixmap.size())
                    targetPixmap.fill(Qt.transparent)
                    painter = QPainter(targetPixmap)
                    painter.setOpacity(0.85)
                    painter.drawPixmap(0, 0, pixmap)
                    painter.end()
                    drag.setPixmap(targetPixmap)
    
                    # Initiate the drag
                    dropAction = drag.exec_(Qt.MoveAction | Qt.CopyAction)
    
                    # If the drag completed outside of the tab bar, detach the tab and move
                    # the content to the current cursor position
                    if dropAction == Qt.IgnoreAction:
                        event.accept()
                        self.onDetachTabSignal.emit(self.tabAt(self.dragStartPos), self.mouseCursor.pos())
    
                    # Else if the drag completed inside the tab bar, move the selected tab to the new position
                    elif dropAction == Qt.MoveAction:
                        if not self.dragDropedPos.isNull():
                            event.accept()
                            self.onMoveTabSignal.emit(self.tabAt(self.dragStartPos), self.tabAt(self.dragDropedPos))
                else:
                    QTabBar.mouseMoveEvent(self, event)
    
    
            ##
            #  Determine if the drag has entered a tab position from another tab position
            #
            #  @param    event    a drag enter event
            def dragEnterEvent(self, event):
                mimeData = event.mimeData()
                formats = mimeData.formats()
    
                if 'action' in formats and mimeData.data('action') == 'application/tab-detach':
                    event.acceptProposedAction()
    
                QTabBar.dragMoveEvent(self, event)
    
    
            ##
            #  Get the position of the end of the drag
            #
            #  @param    event    a drop event
            def dropEvent(self, event):
                self.dragDropedPos = event.pos()
                QTabBar.dropEvent(self, event)
    
    class SurfViewer(QMainWindow):
        def __init__(self, parent=None):
            super(SurfViewer, self).__init__()
            self.parent = parent
            self.centralTabs= DetachableTabWidget()
            self.setCentralWidget(self.centralTabs)
            self.setFixedWidth(200)
            self.setFixedHeight(200)
    
            #tab 1
            self.tab_1 = QWidget()
            self.centralTabs.addTab(self.tab_1,"Label")
            vbox = QVBoxLayout()
            Label = QLabel('Tab1')
            Label.setFixedWidth(180)
            LineEdit = QLineEdit('Tab1')
            LineEdit.setFixedWidth(180)
            vbox.addWidget(Label)
            vbox.addWidget(LineEdit)
            vbox.setAlignment(Qt.AlignTop)
            self.tab_1.setLayout(vbox)
    
            #tab 2
            self.tab_2 = QWidget()
            self.centralTabs.addTab(self.tab_2,"Label")
            vbox = QVBoxLayout()
            Label = QLabel('Tab2')
            Label.setFixedWidth(180)
            LineEdit = QLineEdit('Tab2')
            LineEdit.setFixedWidth(180)
            vbox.addWidget(Label)
            vbox.addWidget(LineEdit)
            vbox.setAlignment(Qt.AlignTop)
            self.tab_2.setLayout(vbox)
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        ex = SurfViewer(app)
        ex.setWindowTitle('window')
        ex.show()
        sys.exit(app.exec_( ))
    

    这个代码还有一个问题。当几个选项卡被取消跟踪并且我以错误的顺序关闭它们时,这些选项卡在主窗口中的顺序就错误了。我想把它们按原来的顺序排列

    更新2

    所以我重写了 attachTab 功能,以便将卡舌放回分离前的位置:

    def attachTab(self, contentWidget, name, icon):
    
            # Make the content widget a child of this widget
            contentWidget.setParent(self)
    
    
            # Create an image from the given icon
            if not icon.isNull():
                tabIconPixmap = icon.pixmap(icon.availableSizes()[0])
                tabIconImage = tabIconPixmap.toImage()
            else:
                tabIconImage = None
    
    
            # Create an image of the main window icon
            if not icon.isNull():
                windowIconPixmap = self.window().windowIcon().pixmap(icon.availableSizes()[0])
                windowIconImage = windowIconPixmap.toImage()
            else:
                windowIconImage = None
    
    
            # Determine if the given image and the main window icon are the same.
            # If they are, then do not add the icon to the tab
            if name  == 'Model Selection':
                index = 0
            elif name  == "Model' Parameters":
                index = 1
            elif name  == 'Stim settings':
                index = 2
            elif name  == 'Parameter evolution settings':
                index = 3
            elif name  == 'LambdaE':
                index = 4
            elif name  == 'Simulation settings':
                index = 5
            elif name  == 'LFP + PPS + Pulse Results':
                index = 6
            if tabIconImage == windowIconImage:
                index = self.insertTab(index,contentWidget, name)
                # index = self.addTab(contentWidget, name)
            else:
                # index = self.addTab(contentWidget, icon, name)
                index = self.insertTab(index,contentWidget, icon, name)
    
    
    
            # Make this tab the current tab
            if index > -1:
                self.setCurrentIndex(index)
    

    因此,每个选项卡都是根据起始位置插入的,但所有操作都是手动完成的。也许有一种自动的方式可以做到这一点。

    我还增加了阻力最小距离,因为它看起来太短了 ni公司 mouseMoveEvent 功能:

    if not self.dragStartPos.isNull() and ((event.pos() - self.dragStartPos).manhattanLength() > QApplication.startDragDistance()*2):
    

    我还要修改(<至>仅当距离大于 startDragDistance()

    1 回复  |  直到 8 年前
        1
  •  2
  •   Hitesh Patel    8 年前

    这是在PyQt4上为PyQt中的可拆卸选项卡小部件开发的代码。希望这对你有帮助。

    from PyQt4 import QtGui, QtCore
    from PyQt4.QtCore import pyqtSignal, pyqtSlot
    
    ##
    # The DetachableTabWidget adds additional functionality to Qt's QTabWidget that allows it
    # to detach and re-attach tabs.
    #
    # Additional Features:
    #   Detach tabs by
    #     dragging the tabs away from the tab bar
    #     double clicking the tab
    #   Re-attach tabs by
    #     closing the detached tab's window
    #     double clicking the detached tab's window frame
    #
    # Modified Features:
    #   Re-ordering (moving) tabs by dragging was re-implemented  
    #   
    class DetachableTabWidget(QtGui.QTabWidget):
        def __init__(self, parent=None):
            QtGui.QTabWidget.__init__(self, parent)
    
            self.tabBar = self.TabBar(self)
            self.tabBar.onDetachTabSignal.connect(self.detachTab)
            self.tabBar.onMoveTabSignal.connect(self.moveTab)
    
            self.setTabBar(self.tabBar)
    
    
        ##
        #  The default movable functionality of QTabWidget must remain disabled
        #  so as not to conflict with the added features
        def setMovable(self, movable):
            pass
    
        ##
        #  Move a tab from one position (index) to another
        #
        #  @param    fromIndex    the original index location of the tab
        #  @param    toIndex      the new index location of the tab
        @pyqtSlot(int, int)
        def moveTab(self, fromIndex, toIndex):
            widget = self.widget(fromIndex)
            icon = self.tabIcon(fromIndex)
            text = self.tabText(fromIndex)
    
            self.removeTab(fromIndex)
            self.insertTab(toIndex, widget, icon, text)
            self.setCurrentIndex(toIndex)
    
    
        ##
        #  Detach the tab by removing it's contents and placing them in
        #  a DetachedTab dialog
        #
        #  @param    index    the index location of the tab to be detached
        #  @param    point    the screen position for creating the new DetachedTab dialog
        @pyqtSlot(int, QtCore.QPoint)
        def detachTab(self, index, point):
    
            # Get the tab content
            name = self.tabText(index)
            icon = self.tabIcon(index)        
            if icon.isNull():
                icon = self.window().windowIcon()              
            contentWidget = self.widget(index)
            contentWidgetRect = contentWidget.frameGeometry()
    
            # Create a new detached tab window
            detachedTab = self.DetachedTab(contentWidget, self.parentWidget())
            detachedTab.setWindowModality(QtCore.Qt.NonModal)
            detachedTab.setWindowTitle(name)
            detachedTab.setWindowIcon(icon)
            detachedTab.setObjectName(name)
            detachedTab.setGeometry(contentWidgetRect)
            detachedTab.onCloseSignal.connect(self.attachTab)
            detachedTab.move(point)
            detachedTab.show()
    
    
        ##
        #  Re-attach the tab by removing the content from the DetachedTab dialog,
        #  closing it, and placing the content back into the DetachableTabWidget
        #
        #  @param    contentWidget    the content widget from the DetachedTab dialog
        #  @param    name             the name of the detached tab
        #  @param    icon             the window icon for the detached tab
        @pyqtSlot(QtGui.QWidget, QtCore.QString, QtGui.QIcon)
        def attachTab(self, contentWidget, name, icon):
    
            # Make the content widget a child of this widget
            contentWidget.setParent(self)
    
    
            # Create an image from the given icon
            if not icon.isNull():
                tabIconPixmap = icon.pixmap(icon.availableSizes()[0])
                tabIconImage = tabIconPixmap.toImage()
            else:
                tabIconImage = None
    
    
            # Create an image of the main window icon
            if not icon.isNull():
                windowIconPixmap = self.window().windowIcon().pixmap(icon.availableSizes()[0])
                windowIconImage = windowIconPixmap.toImage()
            else:
                windowIconImage = None
    
    
            # Determine if the given image and the main window icon are the same.
            # If they are, then do not add the icon to the tab
            if tabIconImage == windowIconImage:
                index = self.addTab(contentWidget, name)
            else:
                index = self.addTab(contentWidget, icon, name)
    
    
            # Make this tab the current tab
            if index > -1:
                self.setCurrentIndex(index)
    
    
        ##
        #  When a tab is detached, the contents are placed into this QDialog.  The tab
        #  can be re-attached by closing the dialog or by double clicking on its
        #  window frame.
        class DetachedTab(QtGui.QDialog):
            onCloseSignal = pyqtSignal(QtGui.QWidget, QtCore.QString, QtGui.QIcon)
    
            def __init__(self, contentWidget, parent=None):
                QtGui.QDialog.__init__(self, parent)
    
                layout = QtGui.QVBoxLayout(self)            
                self.contentWidget = contentWidget            
                layout.addWidget(self.contentWidget)
                self.contentWidget.show()
    
    
            ##
            #  Capture a double click event on the dialog's window frame
            #
            #  @param    event    an event
            #
            #  @return            true if the event was recognized
            def event(self, event):
    
                # If the event type is QEvent.NonClientAreaMouseButtonDblClick then
                # close the dialog
                if event.type() == 176:
                    event.accept()
                    self.close()
    
                return QtGui.QDialog.event(self, event)
    
    
            ##
            #  If the dialog is closed, emit the onCloseSignal and give the
            #  content widget back to the DetachableTabWidget
            #
            #  @param    event    a close event
            def closeEvent(self, event):
                self.onCloseSignal.emit(self.contentWidget, self.objectName(), self.windowIcon())
    
    
        ##
        #  The TabBar class re-implements some of the functionality of the QTabBar widget
        class TabBar(QtGui.QTabBar):
            onDetachTabSignal = pyqtSignal(int, QtCore.QPoint)
            onMoveTabSignal = pyqtSignal(int, int)
    
            def __init__(self, parent=None):
                QtGui.QTabBar.__init__(self, parent)
    
                self.setAcceptDrops(True)
                self.setElideMode(QtCore.Qt.ElideRight)
                self.setSelectionBehaviorOnRemove(QtGui.QTabBar.SelectLeftTab)
    
                self.dragStartPos = QtCore.QPoint()
                self.dragDropedPos = QtCore.QPoint()
                self.mouseCursor = QtGui.QCursor()
                self.dragInitiated = False
    
    
            ##
            #  Send the onDetachTabSignal when a tab is double clicked
            #
            #  @param    event    a mouse double click event
            def mouseDoubleClickEvent(self, event):
                event.accept()
                self.onDetachTabSignal.emit(self.tabAt(event.pos()), self.mouseCursor.pos())
    
    
            ##
            #  Set the starting position for a drag event when the mouse button is pressed
            #
            #  @param    event    a mouse press event
            def mousePressEvent(self, event):
                if event.button() == QtCore.Qt.LeftButton:
                    self.dragStartPos = event.pos()
    
                self.dragDropedPos.setX(0)
                self.dragDropedPos.setY(0)
    
                self.dragInitiated = False
    
                QtGui.QTabBar.mousePressEvent(self, event)
    
    
            ##
            #  Determine if the current movement is a drag.  If it is, convert it into a QDrag.  If the
            #  drag ends inside the tab bar, emit an onMoveTabSignal.  If the drag ends outside the tab
            #  bar, emit an onDetachTabSignal.
            #
            #  @param    event    a mouse move event
            def mouseMoveEvent(self, event):
    
                # Determine if the current movement is detected as a drag
                if not self.dragStartPos.isNull() and ((event.pos() - self.dragStartPos).manhattanLength() < QtGui.QApplication.startDragDistance()):
                    self.dragInitiated = True
    
                # If the current movement is a drag initiated by the left button
                if (((event.buttons() & QtCore.Qt.LeftButton)) and self.dragInitiated):
    
                    # Stop the move event
                    finishMoveEvent = QtGui.QMouseEvent(QtCore.QEvent.MouseMove, event.pos(), QtCore.Qt.NoButton, QtCore.Qt.NoButton, QtCore.Qt.NoModifier)
                    QtGui.QTabBar.mouseMoveEvent(self, finishMoveEvent)
    
                    # Convert the move event into a drag
                    drag = QtGui.QDrag(self)
                    mimeData = QtCore.QMimeData()
                    mimeData.setData('action', 'application/tab-detach')
                    drag.setMimeData(mimeData)
    
                    # Create the appearance of dragging the tab content
                    pixmap = QtGui.QPixmap.grabWindow(self.parentWidget().currentWidget().winId())
                    targetPixmap = QtGui.QPixmap(pixmap.size())
                    targetPixmap.fill(QtCore.Qt.transparent)
                    painter = QtGui.QPainter(targetPixmap)
                    painter.setOpacity(0.85)
                    painter.drawPixmap(0, 0, pixmap)
                    painter.end()
                    drag.setPixmap(targetPixmap)
    
                    # Initiate the drag
                    dropAction = drag.exec_(QtCore.Qt.MoveAction | QtCore.Qt.CopyAction)
    
                    # If the drag completed outside of the tab bar, detach the tab and move
                    # the content to the current cursor position
                    if dropAction == QtCore.Qt.IgnoreAction:
                        event.accept()
                        self.onDetachTabSignal.emit(self.tabAt(self.dragStartPos), self.mouseCursor.pos())
    
                    # Else if the drag completed inside the tab bar, move the selected tab to the new position
                    elif dropAction == QtCore.Qt.MoveAction:
                        if not self.dragDropedPos.isNull():
                            event.accept()
                            self.onMoveTabSignal.emit(self.tabAt(self.dragStartPos), self.tabAt(self.dragDropedPos))
                else:
                    QtGui.QTabBar.mouseMoveEvent(self, event)
    
    
            ##
            #  Determine if the drag has entered a tab position from another tab position
            #
            #  @param    event    a drag enter event
            def dragEnterEvent(self, event):
                mimeData = event.mimeData()
                formats = mimeData.formats()
    
                if formats.contains('action') and mimeData.data('action') == 'application/tab-detach':
                    event.acceptProposedAction()
    
                QtGui.QTabBar.dragMoveEvent(self, event)
    
    
            ##
            #  Get the position of the end of the drag
            #
            #  @param    event    a drop event
            def dropEvent(self, event):
                self.dragDropedPos = event.pos()
                QtGui.QTabBar.dropEvent(self, event)
    
    if __name__ == '__main__':
        import sys
    
        app = QtGui.QApplication(sys.argv)
    
        mainWindow = QtGui.QMainWindow()
        tabWidget = DetachableTabWidget(mainWindow)
    
        tab1 = QtGui.QLabel('Test Widget 1')    
        tabWidget.addTab(tab1, 'Tab1')
    
        tab2 = QtGui.QLabel('Test Widget 2')
        tabWidget.addTab(tab2, 'Tab2')
    
        tab3 = QtGui.QLabel('Test Widget 3')
        tabWidget.addTab(tab3, 'Tab3')
    
        tabWidget.show()
        mainWindow.setCentralWidget(tabWidget)
        mainWindow.show()
    
        try:
            exitStatus = app.exec_()
            print 'Done...'
            sys.exit(exitStatus)
        except:
            pass