I'm new to PyQt and I'm trying to create a system which dynamically adds widgets when the user presses add.
I want the same self.comboBox widget to get added above the Add button. I will also make a remove button but I believe that will be no problem.
Moreover, I would like certain checkboxes to appear next to the self.combobox when the user chooses an option (aka option is not -Select-).
Finally, how can I store the user's choices in the checkboxes and the combobox? Do I declare a variable or what exactly?
My code was too much to read, so this instead:
class myWindow(QWidget):
def __init__(self):
super().__init__()
self.init()
self.organize()
def init(self):
self.label = QLabel("Label")
self.comboBox = QComboBox(self)
self.comboBox.addItem("-Select-")
self.comboBox.addItem("1")
self.comboBox.addItem("2")
self.comboBox.addItem("3")
self.addbtn = QPushButton("Add")
self.addbtn.clicked.connect(self.addComboBox)
def organize(self):
grid = QGridLayout(self)
self.setLayout(grid)
grid.addWidget(Label, 0, 0, 0, 2)
grid.addWidget(self.comboBox, 1, 2, 1, 3)
grid.addWidget(self.addbtn, 2, 2)
def addComboBox(self):
#Code to add a combo box just like self.comboBox above addbtn and below all existing comboBoxes.
What I want
Sorry. If I understand you correctly, your example might look something like this:
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class CheckableComboBox(QComboBox):
def __init__(self, parent = None):
super(CheckableComboBox, self).__init__(parent)
self.parent = parent
self.setView(QListView(self))
self.view().pressed.connect(self.handleItemPressed)
self.setModel(QStandardItemModel(self))
def handleItemPressed(self, index):
item = self.model().itemFromIndex(index)
if item.checkState() == Qt.Checked:
item.setCheckState(Qt.Unchecked)
else:
item.setCheckState(Qt.Checked)
self.on_selectedItems()
def checkedItems(self):
checkedItems = []
for index in range(self.count()):
item = self.model().item(index)
if item.checkState() == Qt.Checked:
checkedItems.append(item)
return checkedItems
def on_selectedItems(self):
selectedItems = self.checkedItems()
self.parent.lblSelectItem.setText("")
for item in selectedItems:
self.parent.lblSelectItem.setText("{} {} "
"".format(self.parent.lblSelectItem.text(), item.text()))
class ExampleWidget(QGroupBox):
def __init__(self, numAddWidget):
QGroupBox.__init__(self)
self.numAddWidget = numAddWidget
self.numAddItem = 1
self.setTitle("Title {}".format(self.numAddWidget))
self.initSubject()
self.organize()
def initSubject(self):
self.lblName = QLabel("Label Title {}".format(self.numAddWidget), self)
self.lblSelectItem = QLabel(self)
self.teachersselect = CheckableComboBox(self)
self.teachersselect.addItem("-Select {}-".format(self.numAddItem))
item = self.teachersselect.model().item(0, 0)
item.setCheckState(Qt.Unchecked)
self.addbtn = QPushButton("ComboBoxAddItem...", self)
self.addbtn.clicked.connect(self.addTeacher)
def organize(self):
grid = QGridLayout(self)
self.setLayout(grid)
grid.addWidget(self.lblName, 0, 0, 1, 3)
grid.addWidget(self.lblSelectItem, 1, 0, 1, 2)
grid.addWidget(self.teachersselect, 1, 2)
grid.addWidget(self.addbtn, 3, 2)
def addTeacher(self):
self.numAddItem += 1
self.teachersselect.addItem("-Select {}-".format(self.numAddItem))
item = self.teachersselect.model().item(self.numAddItem-1, 0)
item.setCheckState(Qt.Unchecked)
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.numAddWidget = 1
self.initUi()
def initUi(self):
self.layoutV = QVBoxLayout(self)
self.area = QScrollArea(self)
self.area.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.scrollAreaWidgetContents.setGeometry(0, 0, 200, 100)
self.layoutH = QHBoxLayout(self.scrollAreaWidgetContents)
self.gridLayout = QGridLayout()
self.layoutH.addLayout(self.gridLayout)
self.area.setWidget(self.scrollAreaWidgetContents)
self.add_button = QPushButton("Add Widget")
self.layoutV.addWidget(self.area)
self.layoutV.addWidget(self.add_button)
self.add_button.clicked.connect(self.addWidget)
self.widget = ExampleWidget(self.numAddWidget)
self.gridLayout.addWidget(self.widget)
self.setGeometry(700, 200, 350, 300)
def addWidget(self):
self.numAddWidget += 1
self.widget = ExampleWidget(self.numAddWidget)
self.gridLayout.addWidget(self.widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyApp()
w.show()
sys.exit(app.exec_())
Related
I want to create a table in PyQt5 that has a combobox in each column header. When I try to do it, the following error is returned:
TypeError: setHorizontalHeaderItem(self, int, QTableWidgetItem): argument 2 has unexpected type 'QComboBox'
Apparently the function setHorizontalHeaderItem() doesn't accept widgets as items. So is there a way to achieve this? If not, I would settle with putting the comboboxes above the headers, but they should be aligned with the size of each column, even if the user changes the width with the mouse. I don't know if this is possible either.
My code:
from PyQt5 import QtWidgets
import numpy as np
class App(QtWidgets.QWidget):
def __init__(self):
super(App,self).__init__()
self.data = np.random.rand(5,5)
self.createTable()
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.table)
self.setLayout(self.layout)
self.showMaximized()
def createTable(self):
self.header = []
self.table = QtWidgets.QTableWidget(len(self.data), len(self.data[0]))
for i in range(len(self.data[0])):
self.header.append(QtWidgets.QComboBox())
self.header[-1].addItem('Variable')
self.header[-1].addItem('Timestamp')
self.table.setHorizontalHeaderItem(i,self.header[-1])
for i in range(len(self.data)):
for j in range(len(self.data[0])):
self.table.setItem(i,j,QtWidgets.QTableWidgetItem(str(self.data[i][j])))
if __name__ == '__main__':
app = QtWidgets.QApplication([])
ex = App()
app.exec_()
QHeaderView does not support widgets as items so you must create a custom header as I show below:
from PyQt5 import QtCore, QtWidgets
import numpy as np
class HorizontalHeader(QtWidgets.QHeaderView):
def __init__(self, values, parent=None):
super(HorizontalHeader, self).__init__(QtCore.Qt.Horizontal, parent)
self.setSectionsMovable(True)
self.comboboxes = []
self.sectionResized.connect(self.handleSectionResized)
self.sectionMoved.connect(self.handleSectionMoved)
def showEvent(self, event):
for i in range(self.count()):
if i < len(self.comboboxes):
combo = self.comboboxes[i]
combo.clear()
combo.addItems(["Variable", "Timestamp"])
else:
combo = QtWidgets.QComboBox(self)
combo.addItems(["Variable", "Timestamp"])
self.comboboxes.append(combo)
combo.setGeometry(self.sectionViewportPosition(i), 0, self.sectionSize(i)-4, self.height())
combo.show()
if len(self.comboboxes) > self.count():
for i in range(self.count(), len(self.comboboxes)):
self.comboboxes[i].deleteLater()
super(HorizontalHeader, self).showEvent(event)
def handleSectionResized(self, i):
for i in range(self.count()):
j = self.visualIndex(i)
logical = self.logicalIndex(j)
self.comboboxes[i].setGeometry(self.sectionViewportPosition(logical), 0, self.sectionSize(logical)-4, self.height())
def handleSectionMoved(self, i, oldVisualIndex, newVisualIndex):
for i in range(min(oldVisualIndex, newVisualIndex), self.count()):
logical = self.logicalIndex(i)
self.comboboxes[i].setGeometry(self.ectionViewportPosition(logical), 0, self.sectionSize(logical) - 5, height())
def fixComboPositions(self):
for i in range(self.count()):
self.comboboxes[i].setGeometry(self.sectionViewportPosition(i), 0, self.sectionSize(i) - 5, self.height())
class TableWidget(QtWidgets.QTableWidget):
def __init__(self, *args, **kwargs):
super(TableWidget, self).__init__(*args, **kwargs)
header = HorizontalHeader(self)
self.setHorizontalHeader(header)
def scrollContentsBy(self, dx, dy):
super(TableWidget, self).scrollContentsBy(dx, dy)
if dx != 0:
self.horizontalHeader().fixComboPositions()
class App(QtWidgets.QWidget):
def __init__(self):
super(App,self).__init__()
self.data = np.random.rand(10, 10)
self.createTable()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.table)
self.showMaximized()
def createTable(self):
self.header = []
self.table = TableWidget(*self.data.shape)
for i, row_values in enumerate(self.data):
for j, value in enumerate(row_values):
self.table.setItem(i, j, QtWidgets.QTableWidgetItem(str(value)))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
When I click open in Example, open the windowncheck window, in the window windowncheck I check to checkbox_5 and checkbox_6 then I press close, when I reopen windowncheck, checkbox_5 and checkbox_6 are no longer available. how to checkbox_5 and checkbox_6 not lost.
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
qbtn = QPushButton('Quit', self)
qbtn.clicked.connect(QApplication.instance().quit)
qbtn.resize(50,50)
qbtn.move(50, 50)
qbtn.setStyleSheet("color:red")
qbtn.setStyleSheet("background-color:green")
newwindow_button = QPushButton('open', self)
newwindow_button.clicked.connect(self.windown)
newwindow_button.resize(50,50)
newwindow_button.move(150, 50)
self.setGeometry(300, 300, 350, 250)
self.setWindowTitle('Check state')
self.show()
def windown(self):
self.ff = Windowcheck()
class Windowcheck(QWidget):
def __init__(self, parent=None):
super(Windowcheck, self).__init__(parent)
self.listCheckBox = ["Checkbox_1", "Checkbox_2", "Checkbox_3", "Checkbox_4", "Checkbox_5",
"Checkbox_6", "Checkbox_7", "Checkbox_8", "Checkbox_9", "Checkbox_10" ]
self.listLabel = ['', '', '', '', '', '', '', '', '', '', ]
grid = QGridLayout()
for i, v in enumerate(self.listCheckBox):
self.listCheckBox[i] = QCheckBox(v)
self.listLabel[i] = QLabel()
grid.addWidget(self.listCheckBox[i], i, 0)
grid.addWidget(self.listLabel[i], i, 1)
self.button = QPushButton("CheckBox")
self.button.clicked.connect(self.checkboxChanged)
self.button_close = QPushButton("close")
self.button_close.clicked.connect(self.close)
self.labelResult = QLabel()
grid.addWidget(self.button, 10, 0, 1,2)
grid.addWidget(self.button_close,11, 0, 1,2)
grid.addWidget(self.labelResult, 12, 0, 1,2)
self.setLayout(grid)
self.show()
def checkboxChanged(self):
self.labelResult.setText("")
for i, v in enumerate(self.listCheckBox):
self.listLabel[i].setText("True" if v.checkState() else "False")
self.labelResult.setText("{}, {}".format(self.labelResult.text(),
self.listLabel[i].text()))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
The programs load the information of their status in the RAM while it is running, after closing the application it is lost so that is why the information is not persistent. If you want the application to save a state prior to closing then you must store that information on the hard drive through a file. For example in Qt you can use QSettings to do that job:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.settings = QSettings()
self.ff = Windowcheck()
qbtn = QPushButton("Quit", self)
qbtn.clicked.connect(QApplication.instance().quit)
qbtn.setGeometry(50, 50, 50, 50)
qbtn.setStyleSheet("color:red; background-color:green")
newwindow_button = QPushButton("open", self)
newwindow_button.clicked.connect(self.ff.show)
newwindow_button.setGeometry(150, 50, 50, 50)
self.setGeometry(300, 300, 350, 250)
self.setWindowTitle("Check state")
self.readSettings()
def readSettings(self):
for i in self.settings.value("indexes", [], type=list):
self.ff.listCheckBox[int(i)].setChecked(True)
def saveSettings(self):
indexes = self.ff.indexes
self.settings.setValue("indexes", indexes)
def closeEvent(self, event):
super().closeEvent(event)
self.saveSettings()
class Windowcheck(QWidget):
def __init__(self, parent=None):
super(Windowcheck, self).__init__(parent)
texts = [
"Checkbox_1",
"Checkbox_2",
"Checkbox_3",
"Checkbox_4",
"Checkbox_5",
"Checkbox_6",
"Checkbox_7",
"Checkbox_8",
"Checkbox_9",
"Checkbox_10",
]
self.listCheckBox = []
self.listLabel = []
grid = QGridLayout(self)
for i, text in enumerate(texts):
checkbox = QCheckBox(text)
label = QLabel()
grid.addWidget(checkbox, i, 0)
grid.addWidget(label, i, 1)
self.listCheckBox.append(checkbox)
self.listLabel.append(label)
self.button = QPushButton("CheckBox")
self.button.clicked.connect(self.checkboxChanged)
self.button_close = QPushButton("close")
self.button_close.clicked.connect(self.close)
self.labelResult = QLabel()
grid.addWidget(self.button, 10, 0, 1, 2)
grid.addWidget(self.button_close, 11, 0, 1, 2)
grid.addWidget(self.labelResult, 12, 0, 1, 2)
#property
def indexes(self):
return [
i for i, checkbox in enumerate(self.listCheckBox) if checkbox.isChecked()
]
def checkboxChanged(self):
self.labelResult.clear()
texts = []
for label, checkbox in zip(self.listLabel, self.listCheckBox):
text = "True" if checkbox.isChecked() else "False"
label.setText(text)
texts.append(text)
self.labelResult.setText(", ".join(texts))
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
I'm working in a simple gui with a QtoolBox can increase the item dynamically (with button "add item"). I need to add, for each item, a new widget like table (for example TableView class) but I can't understand how can I do it. Could anybody help me? (follow the code I wrote until now)
from PyQt5.QtWidgets import *
import sys
data = {'col1':['1','2','3','4'],
'col2':['1','2','1','3'],
'col3':['1','1','2','1']}
class TableView(QTableWidget):
def __init__(self, data, *args):
QTableWidget.__init__(self, *args)
self.data = data
self.setData()
self.resizeColumnsToContents()
self.resizeRowsToContents()
def setData(self):
horHeaders = []
for n, key in enumerate(sorted(self.data.keys())):
horHeaders.append(key)
for m, item in enumerate(self.data[key]):
newitem = QTableWidgetItem(item)
self.setItem(m, n, newitem)
self.setHorizontalHeaderLabels(horHeaders)
class Window(QWidget):
def __init__(self):
super().__init__()
self.layout = QGridLayout()
self.setLayout(self.layout)
self.numAddWidget = 1
self.initUi()
# Add toolbar and items
def initUi(self):
self.add_button = QPushButton("Add Widget")
self.layout.addWidget(self.add_button, 0, 0)
self.add_button.clicked.connect(self.addPage)
self.toolbox = QToolBox()
self.layout.addWidget(self.toolbox, 1, 0)
def addPage(self):
self.numAddWidget += 1
label = QLabel()
a=self.toolbox.addItem(label, "item {}".format(self.numAddWidget))
self.table=TableView(data, 4, 3)
self.toolbox.addWidget(self.table)
print(a)
app = QApplication(sys.argv)
screen = Window()
screen.show()
sys.exit(app.exec_())
Let's say I have an application with a number of QGroupBoxes like so:
import sys
from PyQt4 import QtGui, QtCore
class Main(QtGui.QWidget):
# pylint: disable=too-many-statements
def __init__(self, main):
super(Main, self).__init__()
self.grid_layout = QtGui.QGridLayout()
self.line_edit = QtGui.QLineEdit()
self.grid_layout.addWidget(self.create_settings_group(), 0, 0, 2, 1)
self.push_button = QtGui.QPushButton("go", self)
self.grid_layout.addWidget(self.create_controls_group(), 0, 1)
self.setLayout(self.grid_layout)
main.setCentralWidget(self)
def create_settings_group(self):
group_box_settings = QtGui.QGroupBox(self)
group_box_settings.setTitle("group1")
grid = QtGui.QGridLayout()
grid.addWidget(self.line_edit, 0, 0)
group_box_settings.setLayout(grid)
return group_box_settings
def create_controls_group(self):
group_box_settings = QtGui.QGroupBox(self)
group_box_settings.setTitle("group2")
grid = QtGui.QGridLayout()
grid.addWidget(self.push_button, 0, 0, 1, 2)
group_box_settings.setLayout(grid)
return group_box_settings
class GUI(QtGui.QMainWindow):
def __init__(self):
super(GUI, self).__init__()
self.ui = Main(self)
self.show()
app = QtGui.QApplication(sys.argv)
ex = GUI()
app.exec_()
When I open my simple application I see that the cursor is blinking in the line edit. But I just want the push button in another group box to be highlighted and to have enter press connected to it? how do I do that? using self.push_button.setFocus() doesn't do anything.
You can try setting the button Default property:
self.push_button.setDefault(True)
self.push_button.setFocus()
You have to set the focus a moment after showing up for it you can use a QTimer::singleShot() or QMetaObject::invokeMethod():
1. QTimer::singleShot()
...
self.push_button = QtGui.QPushButton("go", self)
self.grid_layout.addWidget(self.create_controls_group(), 0, 1)
self.push_button.setDefault(True)
QtCore.QTimer.singleShot(0, self.push_button.setFocus)
2. QMetaObject::invokeMethod()
...
self.push_button = QtGui.QPushButton("go", self)
self.grid_layout.addWidget(self.create_controls_group(), 0, 1)
self.push_button.setDefault(True)
QtCore.QMetaObject.invokeMethod(self.push_button,
"setFocus",
QtCore.Qt.QueuedConnection)
I created a QWizard object which contains several pages, I tried to open a file when NextButton of the specific page was clicked. At first, I triedQWizard.NextButton.clicked.connect(), came with :
"AttributeError: 'WizardButton' object has no attribute 'clicked''.
After that, I searched "WizardButton" in Qt Assistant, couldn't get any useful signals to emit, is it possible to implement that like QPushButton.clicked.connect()?
here's the specific page:
class AccountPage(QWizardPage):
def __init__(self, parent=None):
super(AccountPage, self).__init__(parent)
self.setTitle("Account Information")
NameLabel = QLabel("&Name:")
NameLineEdit = QLineEdit()
NameLabel.setBuddy(NameLineEdit)
EmailLabel = QLabel("&Email Address:")
EmailLineEdit = QLineEdit()
EmailLabel.setBuddy(EmailLineEdit)
PwdLabel = QLabel("&Password:")
PwdLineEdit = QLineEdit()
PwdLabel.setBuddy(PwdLineEdit)
self.registerField('Name*', NameLineEdit)
self.registerField('EmailAddress*', EmailLineEdit)
self.registerField('Password*', PwdLineEdit)
layout = QGridLayout()
layout.addWidget(NameLabel, 0, 0)
layout.addWidget(NameLineEdit, 0, 1)
layout.addWidget(EmailLabel, 1, 0)
layout.addWidget(EmailLineEdit, 1, 1)
layout.addWidget(PwdLabel, 2, 0)
layout.addWidget(PwdLineEdit, 2, 1)
self.setLayout(layout)
QWizard.NextButton.clicked.connect(self.setMB)
def setMB(self):
try:
with open('mb.ini', 'w') as actInfo:
actInfo.write('sj')
except IOError as err:
print('mb.ini error:' + str(err))
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Window(QWizard):
def __init__(self):
super(Window, self).__init__()
self.accoutPage = AccountPage()
self.secondPage = Page2()
self.thirdPage = Page3()
self.addPage(self.accoutPage)
self.addPage(self.secondPage)
self.addPage(self.thirdPage)
self.button(QWizard.NextButton).clicked.connect(self.accoutPage.setMB)
class AccountPage(QWizardPage):
def __init__(self, parent=None):
super(AccountPage, self).__init__(parent)
self.setTitle("Account Information")
self.NameLabel = QLabel("&Name:")
self.NameLineEdit = QLineEdit()
self.NameLabel.setBuddy(self.NameLineEdit)
self.EmailLabel = QLabel("&Email Address:")
self.EmailLineEdit = QLineEdit()
self.EmailLabel.setBuddy(self.EmailLineEdit)
self.PwdLabel = QLabel("&Password:")
self.PwdLineEdit = QLineEdit()
self.PwdLabel.setBuddy(self.PwdLineEdit)
self.registerField('Name*', self.NameLineEdit)
self.registerField('EmailAddress*', self.EmailLineEdit)
self.registerField('Password*', self.PwdLineEdit)
layout = QGridLayout()
layout.addWidget(self.NameLabel, 0, 0)
layout.addWidget(self.NameLineEdit, 0, 1)
layout.addWidget(self.EmailLabel, 1, 0)
layout.addWidget(self.EmailLineEdit, 1, 1)
layout.addWidget(self.PwdLabel, 2, 0)
layout.addWidget(self.PwdLineEdit, 2, 1)
self.setLayout(layout)
self.i = 0 #if you need to call it once
def setMB(self):
if self.i == 0:
print(self.PwdLineEdit.text(), self.i)
try:
with open('mb.ini', 'w') as actInfo:
actInfo.write('sj')
except IOError as err:
print('mb.ini error:' + str(err))
finally:
self.i += 1
class Page2(QWizardPage):
def __init__(self):
super(Page2, self).__init__()
class Page3(QWizardPage):
def __init__(self):
super(Page3, self).__init__()
def main():
app = QApplication(sys.argv)
app.setStyle('plastique')
window = Window()
window.setWizardStyle(1)
window.show()
app.exec_()
if __name__ == "__main__":
sys.exit(main())