In this minimal reproducible example, I have a comboBox and a pushButton. I am trying to activate buttons on the basis of current text selected from the comboBox, but I can't able activate buttons when I tried to verify it first inside if elif else condition, how to activate right function on the basis of current text.
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class MainWindow(QWidget):
def __init__(self):
super(QWidget, self).__init__()
main_layout = QVBoxLayout(self)
self.buttons = []
# Works:
self.combo = QComboBox()
main_layout.addWidget(self.combo)
self.combo.addItems(['PHC'])
self.combo.addItems(['CHC'])
self.combo.addItems(['HSC'])
self.combo.addItems(['DH'])
self.combo.addItems(['LSH'])
# # Connecting comboBox to VerifyFType function
self.combo.currentIndexChanged[str].connect(self.VerifyFType)
self.button_2 = QPushButton('Validate', self)
main_layout.addWidget(self.button_2)
self.buttons.append(self.button_2)
def VerifyFType(self):
print("Entered VerifyFType")
FType = self.combo.currentText()
print(FType)
if(FType == "PHC"):
self.button_2.clicked.connect(lambda: self.PHC_Validate)
elif(FType == "CHC"):
self.button_2.clicked.connect(lambda: self.CHC_Validate)
elif(FType == "DH"):
self.button_2.clicked.connect(lambda: self.DH_Validate)
elif(FType == "HSC"):
self.button_2.clicked.connect(lambda: self.HSC_Validate)
elif(FType == "LSH"):
self.button_2.clicked.connect(lambda: self.LSH_Validate)
else:
"Nothing Matched , No such FType"
def PHC_Validate(self):
print('Entered PHC_Validate')
def CHC_Validate(self):
print('Entered CHC_Validate')
def DH_Validate(self):
print('Entered DH_Validate')
def HSC_Validate(self):
print('Entered HSC_Validate')
def LSH_Validate(self):
print('Entered LSH_Validate')
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Your logic is wrong since you seem to think that connecting the signal to another function will disconnect the signal from the previous function.
The solution is to invoke the appropriate function using the currentText of the QComboBox when the button is pressed.
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.combo = QComboBox()
self.combo.addItems(["PHC", "CHC", "HSC", "DH", "LSH"])
self.button_2 = QPushButton("Validate", self)
self.button_2.clicked.connect(self.handle_clicked)
main_layout = QVBoxLayout(self)
main_layout.addWidget(self.combo)
main_layout.addWidget(self.button_2)
def handle_clicked(self):
FType = self.combo.currentText()
if FType == "PHC":
self.PHC_Validate()
elif FType == "CHC":
self.CHC_Validate()
elif FType == "DH":
self.DH_Validate()
elif FType == "HSC":
self.HSC_Validate()
elif FType == "LSH":
self.LSH_Validate()
else:
"Nothing Matched , No such FType"
def PHC_Validate(self):
print("Entered PHC_Validate")
def CHC_Validate(self):
print("Entered CHC_Validate")
def DH_Validate(self):
print("Entered DH_Validate")
def HSC_Validate(self):
print("Entered HSC_Validate")
def LSH_Validate(self):
print("Entered LSH_Validate")
Related
I just want to show different images whenever I press keys.
Different keys are connected to different functions which show different image or do different things.
There is text at first, and after press spacebar key, the text is deleted and 'image 1' is appeared. I want to change image which is shown on screen whenever I press the keyboard button 'F' or 'J'
, but nothing is happened after I press space bar key.
Here are my codes below.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt
class StartExp(QWidget):
def __init__(self):
super().__init__()
self.expUI()
def expUI(self):
self.setWindowTitle("ver.1")
self.resize(500, 500)
self.information()
def information(self):
# information
self.info1 = QLabel(f"Hi, there!", self)
self.info1.setAlignment(Qt.AlignCenter)
layout = QVBoxLayout()
layout.addWidget(self.info1)
self.setLayout(layout)
def image1(self):
fix_h = 100
fix_w = 100
self.img1 = QLabel(self)
image1 = QPixmap('1.jpg')
self.img1.setPixmap(image1)
self.img1.move(fix_w, fix_h)
def image2(self):
fix_h = 100
fix_w = 100
self.img2 = QLabel(self)
image2 = QPixmap('2.jpg')
self.img2.setPixmap(image2)
self.img2.move(fix_w, fix_h)
def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape:
self.close()
elif e.key() ==Qt.Key_F:
self.clearimage2()
self.image1()
self.update()
elif e.key() ==Qt.Key_J:
self.clearimage1()
self.image2()
self.update()
elif e.key() == Qt.Key_Space:
self.clearinfo()
self.image1()
self.update()
def clearinfo(self):
try:
self.info1.clear()
except:
pass
def clearimage1(self):
try:
self.info1.clear()
except:
pass
def clearimage2(self):
try:
self.image2.clear()
except:
pass
def run():
app = QApplication(sys.argv)
mainExp = StartExp()
sys.exit(app.exec_())
run()
I want to run a AnimationGroup with different changing animations.
But the problem is that the function clear()
self.group = QtCore.QSequentialAnimationGroup(self)
def anim(self):
if X == True:
self.group.addAnimation(self.animation_1)
self.group.addAnimation(self.animation_2)
elif X == False:
self.group.addAnimation(self.animation_3)
self.group.addAnimation(self.animation_4)
self.group.start()
self.group.clear()
displays an error
RuntimeError: wrapped C/C++ object of type QVariantAnimation has been deleted
I can’t constantly create a new group.
def anim(self):
self.group = QtCore.QSequentialAnimationGroup(self)
if X == True:
self.group.addAnimation(self.animation_1)
self.group.addAnimation(self.animation_2)
elif X == False:
self.group.addAnimation(self.animation_3)
self.group.addAnimation(self.animation_4)
self.group.start()
self.group.clear()
I tried to use removeAnimation
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class Rad(QtWidgets.QWidget):
def __init__(self, group, pos, parent=None):
super(Rad, self).__init__(parent)
self.resize(50, 50)
lay = QtWidgets.QVBoxLayout(self)
self.radio = QtWidgets.QRadioButton()
self.label = QtWidgets.QLabel()
self.label.setText('but-{}'.format(pos))
self.group = group
self.pos = pos
lay.addWidget(self.radio)
lay.addWidget(self.label)
self.radio.toggled.connect(self.fun)
self.animation = QtCore.QVariantAnimation()
self.animation.setDuration(1000)
self.animation.valueChanged.connect(self.value)
self.animation.setStartValue(100)
self.animation.setEndValue(0)
self.can = False
def value(self, val):
self.move(self.pos, val)
def fun(self):
self.animation.setStartValue(
100
if not self.can
else 0
)
self.animation.setEndValue(
0
if not self.can
else 100
)
if self.group.animationAt(1) == None :
print("Bad")
else :
print("Good")
self.group.removeAnimation(self.group.animationAt(0))
self.group.addAnimation(self.animation)
self.group.start()
self.can = not self.can
class Test(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.group = QtCore.QSequentialAnimationGroup(self)
self.buts = QtWidgets.QButtonGroup(self, exclusive=True)
wid_1 = Rad(self.group, 200, self)
wid_2 = Rad(self.group, 100, self)
wid_3 = Rad(self.group, 0, self)
self.buts.addButton(wid_1.radio, 0)
self.buts.addButton(wid_2.radio,1)
self.buts.addButton(wid_3.radio, 2)
wid_1.setStyleSheet('background:brown;')
wid_2.setStyleSheet('background:yellow;')
wid_3.setStyleSheet('background:green;')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Test()
w.resize(500, 500)
w.show()
sys.exit(app.exec_())
And now the animation has become sharp
And in the console output
QAnimationGroup::animationAt: index is out of bounds
From what I can deduce is that you want the pressed item to move down and if any item was in the low position then it must return to its position. If so then my answer should work.
You should not use the clear method since that removes and deletes the animation, instead use takeAnimation(0) until there are no animations, and just add the new animations, but that logic should not be inside "Rad" but in the Test class:
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class Rad(QtWidgets.QWidget):
def __init__(self, text, parent=None):
super(Rad, self).__init__(parent)
self.resize(50, 50)
self.radio = QtWidgets.QRadioButton()
self.label = QtWidgets.QLabel()
self.label.setText(text)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.radio)
lay.addWidget(self.label)
self.animation = QtCore.QVariantAnimation()
self.animation.setDuration(1000)
self.animation.valueChanged.connect(self.on_value_changed)
self.animation.setStartValue(100)
self.animation.setEndValue(0)
#QtCore.pyqtSlot("QVariant")
def on_value_changed(self, val):
pos = self.pos()
pos.setY(val)
self.move(pos)
def swap_values(self):
self.animation.blockSignals(True)
start_value = self.animation.startValue()
end_value = self.animation.endValue()
self.animation.setStartValue(end_value)
self.animation.setEndValue(start_value)
self.animation.blockSignals(False)
class Test(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.buts = QtWidgets.QButtonGroup(self, exclusive=True)
wid_1 = Rad("but-200", self)
wid_1.setStyleSheet("background:brown;")
wid_1.move(200, 0)
wid_2 = Rad("but-100", self)
wid_2.setStyleSheet("background:yellow;")
wid_2.move(100, 0)
wid_3 = Rad("but-0", self)
wid_3.setStyleSheet("background:green;")
wid_3.move(0, 0)
self.buts.addButton(wid_1.radio, 0)
self.buts.addButton(wid_2.radio, 1)
self.buts.addButton(wid_3.radio, 2)
self.buts.buttonToggled.connect(self.on_button_toggled)
self.group = QtCore.QSequentialAnimationGroup(self)
self.last_widget = None
#QtCore.pyqtSlot(QtWidgets.QAbstractButton, bool)
def on_button_toggled(self, button, state):
if state:
wid = button.parent()
if self.group.animationCount() > 0:
self.group.takeAnimation(0)
if isinstance(self.last_widget, Rad):
self.last_widget.swap_values()
self.group.addAnimation(self.last_widget.animation)
wid.swap_values()
self.group.addAnimation(wid.animation)
self.group.start()
self.last_widget = wid
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Test()
w.resize(500, 500)
w.show()
sys.exit(app.exec_())
EDITED
I have two options in my QComboBox which are "feet" and "meters".
The app crashes(python stopped working) each time I select the first option on the GUI or if I proceed without selecting any of the two since the value is in "feet" already.
I have the following edited lines of code;
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.value = QtWidgets.QLineEdit()
self.unit = QtWidgets.QComboBox()
self.convert = QtWidgets.QPushButton()
self.setLayout(QtWidgets.QVBoxLayout())
self.value.setObjectName("value")
self.layout().addWidget(self.value)
self.layout().addWidget(self.unit)
self.unit.addItems(["feet", "meters"])
self.layout().addWidget(self.convert)
self.convert.setText("Convert")
self.unit.currentIndexChanged.connect(self.unit_select)
self.convert.clicked.connect(self.convert_click)
# UNIT SELECT
def unit_select(self):
current_index = self.unit.currentIndex()
if current_index == 0:
num = float(self.value.text()) * 0.3047972654
elif current_index == 1:
num = float(self.value.text()) * 1
self.final = num
# CONVERT VALUE
def convert_click(self):
print("Converted value =", self.final)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I need to set "feet" as the default of the QComboBox.
Try it:
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.unit = QtWidgets.QComboBox()
self.setLayout(QtWidgets.QVBoxLayout())
self.layout().addWidget(self.unit)
self.unit.addItems(["feet", "meters"])
self.unit.setCurrentIndex(0) # <-----
self.current_index = 0 # <-----
self.unit.activated[int].connect(self.onActivatedIndex)
self.unit.currentIndexChanged.connect(self.unit_select)
self.unit_select(self.current_index)
#QtCore.pyqtSlot(int)
def onActivatedIndex(self, index):
print("ActivatedIndex", index)
#QtCore.pyqtSlot(int)
def unit_select(self, index):
if index == 0:
print("\nCurrentIndex {} - feet".format(index))
# ...
elif index == 1:
print("\nCurrentIndex {} - meters".format(index))
# ...
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Update:
import re # To test string conversion to float !!!
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.value = QtWidgets.QLineEdit()
self.unit = QtWidgets.QComboBox()
self.convert = QtWidgets.QPushButton()
self.setLayout(QtWidgets.QVBoxLayout())
self.value.setObjectName("value")
self.layout().addWidget(self.value)
self.layout().addWidget(self.unit)
self.unit.addItems(["feet", "meters"])
self.layout().addWidget(self.convert)
self.convert.setText("Convert")
self.unit.currentIndexChanged.connect(self.unit_select)
self.convert.clicked.connect(self.convert_click)
# UNIT SELECT
def unit_select(self):
if re.match("^\d+?\.*\d*?$", self.value.text()) is None: #self.value.text():
QtWidgets.QMessageBox.information(None, 'Warning',
'The string QLineEdit `{}` cannot be converted to float'.format(self.value.text()))
return False
current_index = self.unit.currentIndex()
if current_index == 0:
num = float(self.value.text()) * 0.3047972654
elif current_index == 1:
num = float(self.value.text()) * 1
self.final = num
# CONVERT VALUE
def convert_click(self):
if re.match("^\d+?\.*\d*?$", self.value.text()) is None: #self.value.text():
QtWidgets.QMessageBox.information(None, 'Warning',
'The string QLineEdit `{}` cannot be converted to float'.format(self.value.text()))
else:
self.unit_select()
print("Converted value =", self.final)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Is there a way to make the QCompleter for pyside work more similar to how the Tag editor here on StackOverflow works? Where a user can type a word and then if there is a space it allows the autocomplete to display matching words?
This post seems like it does what i want but it's in C++
How to force QCompleter to check second word in QLineEdit
import os
import sys
import json
from PySide import QtCore, QtGui
class ExampleWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(ExampleWindow, self).__init__(parent)
self.resize(300, 200)
self.strings_model = QtGui.QStringListModel()
self.get_data(self.strings_model)
completer = QtGui.QCompleter()
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
completer.setModel(self.strings_model)
self.ui_input = QtGui.QLineEdit()
self.ui_input.setCompleter(completer)
self.ui_input.setPlaceholderText('enter description...')
self.ui_tags_list = QtGui.QListView()
self.ui_tags_list.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.ui_tags_list.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.ui_tags_list.setModel(self.strings_model)
# main layout
main_layout = QtGui.QVBoxLayout()
main_layout.setContentsMargins(5,5,5,5)
main_layout.setSpacing(5)
main_layout.addWidget(self.ui_input)
main_layout.addWidget(self.ui_tags_list)
main_widget = QtGui.QWidget()
main_widget.setLayout(main_layout)
self.setCentralWidget(main_widget)
# connections
self.ui_input.returnPressed.connect(self.input_entered)
def get_data(self, model):
model.setStringList(["Animals", "Dogs", "Birds", "Cats", "Elephant", "Zebra"])
def append_tag(self, val):
if not val:
return False
if val.lower() in [x.lower() for x in self.strings_model.stringList()]:
return False
self.strings_model.insertRow(self.strings_model.rowCount())
index = self.strings_model.index(self.strings_model.rowCount()-1)
self.strings_model.setData(index, val)
def input_entered(self):
print 'selected word from drop down should be added to lineEdit'
def main():
app = QtGui.QApplication(sys.argv)
ex = ExampleWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You must implement a logic that gets the last string, this will be the word to make the filter in the QComplete through setCompletionPrefix():
class LineEdit(QtGui.QLineEdit):
def __init__(self, *args, **kwargs):
QtGui.QLineEdit.__init__(self, *args, **kwargs)
self.multipleCompleter = None
def keyPressEvent(self, event):
QtGui.QLineEdit.keyPressEvent(self, event)
if not self.multipleCompleter:
return
c = self.multipleCompleter
if self.text() == "":
return
c.setCompletionPrefix(self.cursorWord(self.text()))
if len(c.completionPrefix()) < 1:
c.popup().hide()
return
c.complete()
def cursorWord(self, sentence):
p = sentence.rfind(" ")
if p == -1:
return sentence
return sentence[p + 1:]
def insertCompletion(self, text):
p = self.text().rfind(" ")
if p == -1:
self.setText(text)
else:
self.setText(self.text()[:p+1]+ text)
def setMultipleCompleter(self, completer):
self.multipleCompleter = completer
self.multipleCompleter.setWidget(self)
completer.activated.connect(self.insertCompletion)
def main():
app = QtGui.QApplication(sys.argv)
w = LineEdit()
completer = QtGui.QCompleter(["Animals", "Dogs", "Birds", "Cats", "Elephant", "Zebra"])
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
w.setMultipleCompleter(completer)
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
How could I archive this:
- I need to drag and drop a tab from its tabBar to other tabBar in a splitted widget?
I already subclass the QtabBar and implement the drag and drop events, i already can drag it with the right pixmap and etc, and also i can drop it into the same tabBar, but not in the other one ..
got this error in the output telling me that im not providing the right arguments, here is the code, that i simplified for make it and example, and plus a .JPG of the window.
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)
the ColtabBar is the subclass where im doing the drag and drop events.
IMAGE - >
After many hours and have eaten many manyyyy pages of Qt today over the web, I did it in my way, now I can drag and drop tabs from one tabBar to the other and vice-versa and not just from selection the current tab, i could select every tab that I want in my tab bar and will show me the pixmap of the little tab while dragging...
Here is the code:
** EDITED **
I made it more bullet proof, I had a bug when I was using more than 2 tabs with the index, now is working better, and when I drop it in the same widget it return the event and not execute the code, plus the hovering tabs select with the right mouse button as well .. I hope this can help anybody in the future.
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)
#---------------------------------------------------------------------------------#
Pic ->
Found this thread useful. Used your solution to create a self contained generic example in PyQt5. May help someone in the future.
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_())
This is modified from someone elses code, perhaps one of the examples above.
Anyhow, it's a minimal code for tab-to-tab or tab-to-window drag / droping of tab's contents.
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_())