Call a class to add tabs in a QTabWidget - python

I am trying to create tabs each time that i press a button that i added to a toolbar. For this i create a windows in Pyqt, and call the class NewTab, which has the QWidget that i want to add in the QTabWidget.
This is my code:
class Window(QMainWindow):
def __init__(self):
self.tabWidget = QTabWidget()
self.tabWidget.setTabsClosable(True)
self.setCentraWidget(self.tabWidget)
self.setLayout(NewTab().newTab.tab_layout)
class NewTab(QWidget):
list_1 = [] #These lists are for another method that i will use later
list_2 = []
def __init__(self):
QWidget.__init__(self)
self.newTab()
def newTab(self):
self.new_tab = QWidget()
Window.tabWidget.addTab(self.new_tab)
plot = MatPlotLibFigure() #This is another class that i add
#in the splitter as a widget
self.splitter = QSplitter(Qt.Vertical)
self.splitter.addWidget(plot)
self.splitter2 = QSplitter(Qt.Horizontal)
self.splitter2.addWidget(self.splitter)
self.tab_layout = QHBoxLayout(Window.tabWidget)
self.tab_layout.addWidget(self.splitter2)
print "New Tab created"
def Close(self):
pass
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
I do not know what happens. It does not work. What am i doing wrong? Hope you can help me

Related

Qt: Dead lock detected - Sender is QPushButton(), receiver is PyQtSlotProxy() - for specific indexes in a QPushButton list

I am new to pyqt, and I tried to make an application window that contains a list of buttons that are able to toggle a different window. Since I want the number of these buttons to be of a varying quantity, I created a list of QPushButton elements for iterating over them, creating as many as defined by the length of the list, nevertheless I noticed a very weird behavior :
The following code ...
import sys
from random import randint
from PyQt5 import QtWidgets
class AnotherWindow(QtWidgets.QWidget):
"""
This "window" is a QWidget. If it has no parent,
it will appear as a free-floating window.
"""
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel("Another Window % d" % randint(0, 100))
layout.addWidget(self.label)
self.setLayout(layout)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self,windows):
super().__init__()
self.windows=[]
self.buttons=[]
l=QtWidgets.QVBoxLayout()
for i in range(len(windows)):
window=AnotherWindow()
self.windows.append(window)
button=QtWidgets.QPushButton(f'window {windows[i]}')
print(i," ",button)
self.buttons.append(button)
self.buttons[i].clicked.connect(self.toggle_window,i)
l.addWidget(self.buttons[i])
w = QtWidgets.QWidget()
w.setLayout(l)
self.setCentralWidget(w)
print(len(self.windows))
def toggle_window(self,i):
if self.windows[i].isVisible():
self.windows[i].hide()
else:
self.windows[i].show()
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
windows=[0,1,2,3]
windows=[str(i) for i in windows]
print(windows)
w = MainWindow(windows)
w.show()
app.exec()
produced the following error but only when the 4rth button (window 3) is pressed.
Qt: Dead lock detected while activating a BlockingQueuedConnection: Sender is QPushButton( ... ), receiver is PyQtSlotProxy( ... )
In effort to validate the code, I tried to narrow the list into a linear declaration of a static number of QPushButton instances, indicating that the issue occurs, only when I try to put the buttons on a list. For instance, the following script does not present any similar unpredictable behavior:
import sys
from random import randint
from PyQt5 import QtWidgets
class AnotherWindow(QtWidgets.QWidget):
"""
This "window" is a QWidget. If it has no parent,
it will appear as a free-floating window.
"""
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel("Another Window % d" % randint(0, 100))
layout.addWidget(self.label)
self.setLayout(layout)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.window0 = AnotherWindow()
self.window1 = AnotherWindow()
self.window2 = AnotherWindow()
self.window3 = AnotherWindow()
l = QtWidgets.QVBoxLayout()
button0 = QtWidgets.QPushButton("window 0")
button0.clicked.connect(self.toggle_window0)
l.addWidget(button0)
button1 = QtWidgets.QPushButton("window 1")
button1.clicked.connect(self.toggle_window1)
l.addWidget(button1)
button2 = QtWidgets.QPushButton("window 2")
button2.clicked.connect(self.toggle_window2)
l.addWidget(button2)
button3 = QtWidgets.QPushButton("window 3")
button3.clicked.connect(self.toggle_window3)
l.addWidget(button3)
w = QtWidgets.QWidget()
w.setLayout(l)
self.setCentralWidget(w)
def toggle_window0(self, checked):
if self.window0.isVisible():
self.window0.hide()
else:
self.window0.show()
def toggle_window1(self):
if self.window1.isVisible():
self.window1.hide()
else:
self.window1.show()
def toggle_window2(self):
if self.window2.isVisible():
self.window2.hide()
else:
self.window2.show()
def toggle_window3(self, checked):
if self.window3.isVisible():
self.window3.hide()
else:
self.window3.show()
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()
To test it further, I extended the list to a list of random lengths (more than 10), where I reassured that the issue persist for specific indexes each time. For example if I create 20 buttons using the first approach, the same bug appears for - the 4rth, the 12fth and the last index exclusively - but not for the rest of them. I even tested it on a different machine. Having also searched in forums, I could not find a solution.
Do I do anything completely wrong here? Does anyone understands better to indicate why is this happening?
I kindly thank you in advance!
Environment: Ubuntu 22.04
Pyqt version : 1.9 (under conda)
Your problem is the following:
self.buttons[i].clicked.connect(self.toggle_window,i)
You are passing i as second argument to connect and expect the toggle_window function to be called with this argument. This is not happening. In toggle_window, i will always be False. See musicamente's comment regarding what this second argument to connect does.
What you should do instead is connect the button click to a function of your window object. From there, you can of course do a callback to a function of your main window as illustrated below:
import sys
from random import randint
from PyQt5 import QtWidgets
class AnotherWindow(QtWidgets.QWidget):
def __init__(self, parent, i):
super().__init__()
self.parent = parent
self.i = i
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel("Another Window {}".format(i))
layout.addWidget(self.label)
self.setLayout(layout)
def toggle(self):
print("Toggling windows {}".format(self.i))
if self.isVisible():
self.hide()
self.parent.window_toggled(self.i, False)
else:
self.show()
self.parent.window_toggled(self.i, True)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, windows):
super().__init__()
self.windows=[]
self.buttons=[]
l=QtWidgets.QVBoxLayout()
for i,title in enumerate(windows):
window=AnotherWindow(self, i)
self.windows.append(window)
button=QtWidgets.QPushButton(title)
button.clicked.connect(window.toggle)
l.addWidget(button)
self.buttons.append(button)
w = QtWidgets.QWidget()
w.setLayout(l)
self.setCentralWidget(w)
def window_toggled(self, i, visible):
print("Window {} is now {}".format(i, "visible" if visible else "hidden"))
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
windows = ["window {}".format(i) for i in range(12)]
w = MainWindow(windows)
w.show()
app.exec()

How can I create new buttons with buttons and plot them in QGraphicScene with an array in PyQt5

I have an application where I have several button widgets on a QGraphicScene and I am trying to make this button widgets to make new buttons on QGraphicScene when they are clicked.
My code is as follows:
buttons = []
class SeveralButtons(QtWidgets.QWidget):
def __init__(self,id,x,y):
super(SeveralButtons,self).__init__()
self.id = id
self.x = x
self.y = y
self.setGeometry(x,y,1,1)
self.button1 = QtWidgets.QPushButton("B1")
self.button2 = QtWidgets.QPushButton("B2")
self.button1.clicked.connect(self.p1b)
self.severalButtonsLayout = QtWidgets.QGridLayout()
self.severalButtonsLayout.addWidget(self.button1, 0,0,)
self.severalButtonsLayout.addWidget(self.button2, 0,1,)
self.setLayout(self.severalButtonsLayout)
def p1b(self):
ph = SeveralButtons(0,self.x-200,self.y-200)
buttons.append(ph)
UiWindow._scene.addWidget(ph)
And my main class is like this:
class UiWindow(QtWidgets.QMainWindow):
factor = 1.5
def __init__(self, parent=None):
super(UiWindow, self).__init__(parent)
self.setFixedSize(940,720)
self._scene = QtWidgets.QGraphicsScene(self)
self._view = QtWidgets.QGraphicsView(self._scene)
self._view.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
self._view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self._view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.initButtons()
self.setCentralWidget(self._view)
def initButtons(self):
self.p = SeveralButtons(0,500,500)
buttons.append(self.p)
self._scene.addWidget(self.p)
def updateButtons(self,phrase):
for b in buttons:
if b != buttons[0]:
self._scene.addWidgets(b)
# ADD BUTTON WIDGET IN buttons ARRAY TO THE _scene OBJECT
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = UiWindow()
ui.show()
sys.exit(app.exec_())
As it is shown in here I am trying to update widgets in main window with button click but I get QGraphicsProxyWidget::setWidget: cannot embed widget 0x24ac1a93000; already embedded error.
How can I overcome this problem or what is the sane way to make this work? My main goal in this program is that every button group can create their children button group when clicked. Doing this with classes is way to go or should I stick to methods in main window class when creating recursive widgets?
Thanks in advance.
EDIT:
class SeveralButtons(QtWidgets.QWidget):
b1Signal = QtCore.pyqtSignal()
def __init__():
self.button1 = QtWidgets.QPushButton()
self.button1.clicked.connect(self.b1)
...
def b1(self):
sb = SeveralButtons()
buttons.append(sb)
self.b1Signal.emit()
class UiWindow(QtWidgets.QMainWindow):
def __init__():
...
self.sb1 = SeveralButtons()
buttons.append(sb1)
self._scene.addWidget(self.sb1)
self.sb1.b1Signal.connect(self.updateButtons)
def updateButtons():
for b in buttons:
if b != buttons[0]:
self._scene.addWidget(b)
The SeveralButtons class should not be responsible of creating new buttons outside itself.
You should emit that signal and connect it to the function of its parent, which will then create a new instance of the same class and also connect the signal.
class SeveralButtons(QtWidgets.QWidget):
b1Signal = QtCore.pyqtSignal()
def __init__():
super().__init__()
layout = QtWidgets.QHBoxLayout(self)
self.button1 = QtWidgets.QPushButton()
self.button1.clicked.connect(self.b1Signal)
layout.addWidget(self.button1)
class UiWindow(QtWidgets.QMainWindow):
def __init__():
# ...
self.buttons = []
self.addButtons()
def addButtons():
newButtons = SeveralButtons()
newButtons.b1Signal.connect(self.addButtons)
self.buttons.append(newButtons)
self._scene.addWidget(newButtons)

How to open and close my SubWindow In QMdiArea in PyQt5?

In QMidArea how to open a SubWindow? My Programs as follows. My intention to open/attach my second program in SubWindow. But Nothing Will happen. visible Only blank Window. How to resolve it?
How to attach my file in QMdi Sub-window ? and after my work, how to close the sub-window properly?
Main Programme
import sys,os
from PyQt5.QtWidgets import *
from sample_countrypage import Countrypage
class MainPage(QMainWindow):
def __init__(self):
super().__init__()
self.mdi = QMdiArea()
self.setWindowTitle(" Sample Programme")
self.setGeometry(100,100,1600,600)
self.Ui()
self.show()
def Ui(self):
self.btn1=QPushButton("Country")
self.btn1.setFixedSize(100, 30)
self.btn1.clicked.connect(self.countrypage)
self.left_layout = QVBoxLayout()
self.main_layout = QHBoxLayout()
self.left_layout.setContentsMargins(3,5,5,3)
self.left_layout.addWidget(self.btn1)
self.left_layout.addStretch()
self.main_layout.setSpacing(5)
self.main_layout.setContentsMargins(0,0,0,0)
self.main_layout.addLayout(self.left_layout)
self.main_layout.addStretch()
self.setLayout(self.main_layout)
widget = QWidget()
widget.setLayout(self.main_layout)
self.setCentralWidget(widget)
def countrypage(self):
print("country page")
self.countrywindow = Countrypage()
subwindow = QMdiSubWindow()
subwindow.setWidget(self.countrywindow)
self.mdi.addSubWindow(subwindow)
# subwindow.setFixedSize(500,500)
subwindow.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
mainwindow = MainPage()
app.setStyle("fusion")
mainwindow.show()
sys.exit(app.exec_())
Second Program
import sys,os
from PyQt5.QtWidgets import *
class Countrypage(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Country Page")
self.btn1 = QPushButton("Accept")
self.btn1.clicked.connect(self.result)
self.btn2 = QPushButton("Re Enter")
self.form_layout = QFormLayout()
self.form_layout.addRow("Country",QLineEdit())
self.form_layout.addRow("continent",QLineEdit())
self.layout_btn = QHBoxLayout()
self.layout_btn.addStretch()
self.layout_btn.addWidget(self.btn1)
self.layout_btn.addWidget(self.btn2)
self.layout_country = QVBoxLayout()
self.layout_country.addLayout(self.form_layout)
self.layout_country.addLayout(self.layout_btn)
self.layout_country.addStretch()
self.setLayout(self.layout_country)
def result(self):
print("bye")
exec .close()
if __name__=="__main__":
app = QApplication(sys.argv)
countrywin = Countrypage()
countrywin.show()
sys.exit(app.exec_())
First of all, there are two main issues with your code:
You never added the mdi area to the main layout (and you also tried to set the layout for the QMainWindow, which is forbidden);
exec is a python builtin, and has no close attribute; if you want to close the widget, you have to call self.close();
Then, the setWidget() method of QMdiSubWindow reparents the widget:
QMdiSubWindow takes temporary ownership of widget;
This means that if you want to close the sub window that contains the widget from that widget, you have to check the parent and eventually close it, as soon as you verify that it's an instance of QMdiSubWindow.
class Countrypage(QWidget):
# ...
def result(self):
print("bye")
# ensure that the parent is actually a subwindow
if isinstance(self.parent(), QMdiSubWindow):
self.parent().close()
else:
self.close()
Alternatively, you can use a custom signal and connect that when creating the subwindow.
class Countrypage(QWidget):
closeRequested = pyqtSignal()
# ...
def result(self):
print("bye")
self.closeRequested.emit()
class MainPage(QMainWindow):
# ...
def countrypage(self):
print("country page")
self.countrywindow = Countrypage()
subwindow = QMdiSubWindow()
subwindow.setWidget(self.countrywindow)
self.mdi.addSubWindow(subwindow)
subwindow.show()
self.countrywindow.closerequested.connect(subwindow.close)
If you want to close the active subwindow from the mdi area (or outside of it) and no matter what that sub window is, just call self.mdi.closeActiveSubWindow().
Note that if you're going to create multiple Countrypage instances, there's no point in creating an instance attribute (self.countrywindow) as it will always be overwritten as soon as another instance will be created. Adding the widget to the subwindow and that subwindow to the mdi area will automatically create a persistent reference (due to the parenting); if you need a python reference to existing pages, then create a list as an instance member in the __init__ (eg. self.pages = []) and add the new instances to that list.

Pyqt4 Qcombobox signal is not firing upon user input but it does when done with .setCurrentIndex

The QComboBox currentIndexChanged Signal is not firing when a new item is selected from user. But it does fire when .setCurrentIndex is used within the code. (line 91 and 92).
I have a QTabWidget. In tab1 I have a Qvbox into which three Qhboxes are added. Each Qhbox is from the class Kaskade and contains two widgets, a QCombobox and a QstackedWidget. Depending of the current Index of the QCombobox the QstackWidget will either show a QLCD number or a Qspinbox.
If the user changes the QCombobox index in the GUI the currentIndexChanged Signal is not emitted, although the QCombobox shows the new item.
What am I missing? Any kind of help is appreciated.
This is my test code
# -*- coding: utf-8 -*-
from PyQt4 import QtCore, QtGui
import sys
class Kaskade(QtGui.QWidget):
def __init__(self,sp,sp_min,sp_max, parent = None):
super(Kaskade, self).__init__(parent)
self._sp=sp
self._sp_min=sp_min
self._sp_max=sp_max
self.sp()
self.hbox_gen()
def mode_changed(self,i):
print "Mode has changed to", i
self.sp_stack.setCurrentIndex(i)
def sp(self):
self.sp_stack=QtGui.QStackedWidget(self)
self.sp1 = QtGui.QWidget()
self.sp2 = QtGui.QWidget()
self.sp1UI()
self.sp2UI()
self.sp_stack.addWidget(self.sp1)
self.sp_stack.addWidget(self.sp2)
def sp1UI(self):
self.sp1_layout=QtGui.QHBoxLayout()
self.sp1_lcd=QtGui.QLCDNumber(self)
self.sp1_layout.addWidget(self.sp1_lcd)
#self.sp1.connect(lcd_pv.display)
self.sp1.setLayout(self.sp1_layout)
def sp2UI(self):
self.sp2_layout=QtGui.QHBoxLayout()
self.sp2_spinBox=QtGui.QSpinBox()
self.sp2_spinBox.setRange(self._sp_min,self._sp_max)
self.sp2_spinBox.setValue(self._sp)
self.sp2_layout.addWidget(self.sp2_spinBox)
self.sp2.setLayout(self.sp2_layout)
def hbox_gen(self):
self.mode=QtGui.QComboBox(self)
self.mode.addItem("OFF")
self.mode.addItem("ON")
self.mode.currentIndexChanged.connect(self.mode_changed)
self.hbox = QtGui.QHBoxLayout()
self.hbox.addWidget(self.mode)
self.hbox.addWidget(self.sp_stack)
class tabdemo(QtGui.QTabWidget):
def __init__(self, parent = None):
super(tabdemo, self).__init__(parent)
self.tab1 = QtGui.QWidget()
self.tab2 = QtGui.QWidget()
self.tab3 = QtGui.QWidget()
self.addTab(self.tab1,"Tab 1")
self.addTab(self.tab2,"Tab 2")
self.addTab(self.tab3,"Tab 3")
self.tab1UI()
self.tab2UI()
self.tab3UI()
self.setWindowTitle("Heizung")
def tab1UI(self):
K1=Kaskade(28,5,40)
K2=Kaskade(30,5,40)
K3=Kaskade(35,5,40)
K1.mode.setCurrentIndex(1)
K3.mode.setCurrentIndex(1)
vbox = QtGui.QVBoxLayout()
vbox.addLayout(K1.hbox)
vbox.addLayout(K2.hbox)
vbox.addLayout(K3.hbox)
self.tab1.setLayout(vbox)
self.setTabText(1,"Tab1")
def tab2UI(self):
self.setTabText(1,"Tab2")
def tab3UI(self):
self.setTabText(2,"Tab3")
def main():
app = QtGui.QApplication(sys.argv)
ex = tabdemo()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You must add Kaskade to vbox in Tab 1, not the layout. In addition we must do self.hbox layout of Kaskade:
class Kaskade(QtGui.QWidget):
[...]
def hbox_gen(self):
[...]
self.hbox = QtGui.QHBoxLayout(self)
[...]
class tabdemo(QtGui.QTabWidget):
[...]
def tab1UI(self):
[...]
vbox = QtGui.QVBoxLayout()
vbox.addWidget(K1)
vbox.addWidget(K2)
vbox.addWidget(K3)
self.tab1.setLayout(vbox)
[...]

Add custom items to QListWidget

How can I add customized items to a QListWidget with a background color that I choose, and add a bottom border to each item, like this draft example in the picture below.
This is the code that I wrote:
from PyQt5 import QtWidgets, QtGui
import sys
class CustomListHead(QtWidgets.QWidget):
def __init__(self):
super(CustomListHead, self).__init__()
self.project_title = QtWidgets.QLabel("Today")
self.set_ui()
def set_ui(self):
grid_box = QtWidgets.QGridLayout()
grid_box.addWidget(self.project_title, 0, 0)
self.setLayout(grid_box)
self.show()
class CustomListItem(QtWidgets.QWidget):
def __init__(self):
super(CustomListItem, self).__init__()
self.project_title = QtWidgets.QLabel("Learn Python")
self.task_title = QtWidgets.QLabel("Learn more about forms, models and include")
self.set_ui()
def set_ui(self):
grid_box = QtWidgets.QGridLayout()
grid_box.addWidget(self.project_title, 0, 0)
grid_box.addWidget(self.task_title, 1, 0)
self.setLayout(grid_box)
self.show()
class MainWindowUI(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindowUI, self).__init__()
self.list_widget = QtWidgets.QListWidget()
self.set_ui()
def set_ui(self):
custom_head_item = CustomListHead()
item = QtWidgets.QListWidgetItem(self.list_widget)
item.setSizeHint(custom_head_item.sizeHint())
self.list_widget.setItemWidget(item, custom_head_item)
self.list_widget.addItem(item)
custom_item = CustomListItem()
item = QtWidgets.QListWidgetItem(self.list_widget)
item.setSizeHint(custom_item.sizeHint())
self.list_widget.addItem(item)
self.list_widget.setItemWidget(item, custom_item)
vertical_layout = QtWidgets.QVBoxLayout()
vertical_layout.addWidget(self.list_widget)
widget = QtWidgets.QWidget()
widget.setLayout(vertical_layout)
self.setCentralWidget(widget)
self.show()
app = QtWidgets.QApplication(sys.argv)
ui = MainWindowUI()
sys.exit(app.exec_())
I see you have QListWidgetItem with you.
From documentation you can customize each widget item, customize it and add to your listwidget:
The appearance of the text can be customized with setFont(), setForeground(), and setBackground(). Text in list items can be aligned using the setTextAlignment() function. Tooltips, status tips and "What's This?" help can be added to list items with setToolTip(), setStatusTip(), an
d setWhatsThis().
http://doc.qt.io/qt-5/qlistwidgetitem.html#details

Categories