How can I print a QTableWidget by clicking on QPushButton - python

I have an interface with two tab: in the first one i ask the user to enter parameters and in the second one i want to print the following QTableWidget.
So basically on the first tab i have a QPushButton that i called process and normally, when i push on it , i want to send the information to the second Tab.
Right now i just tried to show a new window with the QTableWidget and the good parameters :
class Parameters(QWidget):
def __init__(self):
super(Parameters, self).__init__()
self.matrixsize = QLineEdit()
bouton = QPushButton("define matrix_size")
bouton.clicked.connect(self.appui_bouton)
self.halfmatrix = QCheckBox()
self.halfmatrix.toggled.connect(self.on_checked)
self.define_matrix_size = QGroupBox('Define Parameters')
layout = QGridLayout()
layout.addWidget(self.matrixsize, 0, 0, 1, 1, )
layout.addWidget(bouton, 0, 1, 1, 1)
layout.addWidget(QLabel('select half size mode'
), 1, 0, 1, 1)
layout.addWidget(self.halfmatrix, 1, 1, 1, 1)
self.define_matrix_size.setLayout(layout)
process = QPushButton('process')
process.clicked.connect(self.process)
self.matrix = QTableWidget()
self.layout = QGridLayout()
self.layout.addWidget(self.define_matrix_size)
self.layout.addWidget(matrix)
self.layout.addWidget(process)
self.setLayout(self.layout)
def matrix_size(self):
if self.matrixsize.text() == "":
return 0
else:
return int(self.matrixsize.text())
def appui_bouton(self):
taille = self.matrixsize()
self.matrix.deleteLater()
if self.halfmatrix.isChecked():
self.on_checked()
else:
self.matrix = QTableWidget()
self.matrix.setColumnCount(taille)
self.matrix.setRowCount(taille)
self.layout.addWidget(self.matrix)
self.update()
self.setLayout(self.layout)
def keyPressEvent(self, qKeyEvent):
print(qKeyEvent.key())
if qKeyEvent.key() == Qt.Key_Return or qKeyEvent.key() == Qt.Key_Enter:
self.appui_bouton()
else:
super().keyPressEvent(qKeyEvent)
def on_checked(self):
taille = self.matrixsize()
if taille == 0:
pass
else:
if self.halfmatrix.isChecked():
size = int(taille / 2)
self.matrix.deleteLater()
self.matrix = QTableWidget()
self.matrix.setColumnCount(size)
self.matrix.setRowCount(size)
self.layout.addWidget(self.matrix, 3, 0, 20, 4)
self.update()
self.setLayout(self.layout)
else:
self.appui_bouton()
def process (self):
layout = QHBoxLayout()
test = self.matrix
test.setLayout(layout)
test.show()
So in order to clarify what i said: i have a Window on which you get some parameters (size,...) , when you select those parameters, let's say you take matrixsize==5, then a 5x5 table is added to the window. This table can be after this fill by others parameters (i cut them on the code) by a system of drag and drop.
So now that i got a built table, i want to be able to open a new window with just the table by clicking on the ''process'' button.
So i don't want a dynamical table, i just want a table that keeps the same property (for instance if the matrix has dragonly enable then the new matrix should have the same) . I want to keep every information containing in the cells
I hope i am enoughly clear that is my first time asking questions (after many times reading some answers of course^^)
thanks for your answer and advice !

You can just create a new QTableWidget with no parent (which makes it a top level window), and then show it:
class Parameters(QWidget):
# ...
def process(self):
rows = self.matrix.rowCount()
columns = self.matrix.columnCount()
self.newTable = QTableWidget(rows, columns)
for row in range(rows):
for column in range(columns):
source = self.matrix.item(row, column)
if source:
self.newTable.setItem(row, column, QTableWidgetItem(source))
self.newTable.show()
Note that I created the new table as an instance attribute. This allows to avoid the garbage collection in case it was a local variable (resulting in the widget showing and disappearing right after), but has the unfortunate effect that if you click on the process button again and a window already exists, it gets deleted and "overwritten" with a new window. If you want to have more process windows at the same time, you could add them to a list:
class Parameters(QWidget):
def __init__(self):
super(Parameters, self).__init__()
# ...
self.processTables = []
def process(self):
rows = self.matrix.rowCount()
columns = self.matrix.columnCount()
# note that now "newTable" is *local*
newTable = QTableWidget(rows, columns)
self.processTables.append(newTable)
# ...
Some suggestions about your code:
there's absolutely no need to create a new table each time you want to change its size; just use setRowCount and setColumnCount on the existing one, and if you don't want to keep previous values, use clear();
don't use two functions that do almost the same things (appui_bouton and on_checked) and call each other, just use one function that checks for both aspects;
don't call update() unnecessarily: when you change the properties of a widget (or add a new widget to a layout) update is called already; while it's not an actual issue (Qt automatically manages when updates actually happen, avoiding repainting if not necessary), calling it just adds unnecessary noise to your code;
be more careful when adding widgets to a grid layout (I'm referring to the code on on_checked): don't use the rowSpan and columnSpan if not required; also, using a value that high is completely useless, as there are no other widgets in that row, and there's actually only one column in that layout; also, don't call setLayout() again;
if you need a numerical value, then use a QSpinBox, not a QLineEdit.
The function to update the existing table can be rewritten more easily, and you should connect both the button and the checkbox to it:
class Parameters(QWidget):
def __init__(self):
super(Parameters, self).__init__()
self.matrixsize = QSpinBox()
bouton = QPushButton("define matrix_size")
bouton.clicked.connect(self.appui_bouton)
self.halfmatrix = QCheckBox()
self.halfmatrix.toggled.connect(self.appui_bouton)
# ...
def appui_bouton(self):
taille = self.matrixsize.value()
if self.halfmatrix.isChecked():
taille //= 2
if not taille:
return
self.matrix.setColumnCount(taille)
self.matrix.setRowCount(taille)

Related

how to set a unviersal button that can do mutiple function in python

I want to make a financial calculator by using GUI, which can calculate some basic calculation functions.
Currently my program has main two functions future_value and present_value.
But when I calculate, it will call my previous function and print out the result on the terminal. For example, my previous function is future_value, and now there is a new function called present_value. When I press the calculation button, It will also call future_value in the terminal and print it out,but I only want present_value to be called.
I want to set the cauculation button as universal button, so whenever there is a blank in a block, then computer know I need to calculte that specific number(function).
One of my idea is that create a function that like a title(heading) function, and that function is connected to this button, and within this function, there is a list of different functions that do something like calculte interest rate, or present_value, etc, but my main problem is how should I do it, how should I use if statement or other statement to work on it.
from PySide2.QtWidgets import QApplication
from PySide2.QtUiTools import QUiLoader
class COMPOUND_INTEREST:
def __init__(self):
self.ui = QUiLoader().load("bb\IA compound designer.ui")
self.ui.setWindowTitle("Compound interest")
self.ui.pushButton.clicked.connect(self.future_value)
self.ui.pushButton.clicked.connect(self.present_value)
def future_value(self):
N = int(self.ui.lineEdit.text())
I = float(self.ui.lineEdit_2.text())
PV = float(self.ui.lineEdit_3.text())
C_Y = int(self.ui.lineEdit_7.text())
jb = (1 + (I / (100 * C_Y))) ** N
res = abs(jb * PV)
print(res)
self.ui.lineEdit_6.setText(str(round(res, 2)))
def present_value(self):
F_V = float(self.ui.lineEdit_6.text())
N = int(self.ui.lineEdit.text())
I = float(self.ui.lineEdit_2.text())
C_Y = int(self.ui.lineEdit_7.text())
x = (1 + (I / (100 * C_Y))) ** N
res = -abs(F_V / x)
print(res)
self.ui.lineEdit_3.setText(str(round(res, 2)))
app = QApplication([])
COMPOUND_INTEREST = COMPOUND_INTEREST()
COMPOUND_INTEREST.ui.show()
app.exec_()
Based on what I understood on your post, you want a single button to change its functionality based on wether the Line Edits are filled with numbers or empty, right?
Just be careful because it gets more complex than that. For example: what if both of the Line Edits are filled with numbers? Or both of them are empty?
In the example below, I teach you how to check those 4 conditions, and ask the user to choose which method that should be executed it happens that both Edits are filled.
If both are empty, an error is displayed on the screen telling the user to fill at least one of them.
from PySide2.QtWidgets import QApplication, QMainWindow, QWidget, QMessageBox
from PySide2.QtWidgets import QLabel, QLineEdit, QPushButton, QDialog
from PySide2.QtWidgets import QDialogButtonBox, QGridLayout, QVBoxLayout
class OurCondition(QDialog):
def __init__(self):
QDialog.__init__(self)
layout = QVBoxLayout()
self.setLayout(layout)
text = "Both <b>Present</b> and <b>Future</b> fields are filled. Which function "
text += "do you want to execute?"
layout.addWidget(QLabel(text))
bbox = QDialogButtonBox()
bbox.addButton('Future Value', QDialogButtonBox.AcceptRole)
bbox.addButton('Present Value', QDialogButtonBox.AcceptRole)
bbox.addButton('Cancel', QDialogButtonBox.RejectRole)
bbox.accepted.connect(self.accept)
bbox.rejected.connect(self.reject)
bbox.clicked.connect(self.clickedBeforeAccept)
layout.addWidget(bbox)
# Set Default Option
self.conditionOption = 0
# As we are using bbox on clickedBeforeAccept below, we must reference
# bbox for later usage:
self.bbox = bbox
# Custom function to be called when a QDialogButtonBox button
# is clicked.
def clickedBeforeAccept(self, button):
if (self.bbox.buttonRole(button) == QDialogButtonBox.AcceptRole):
if (button.text() == 'Future Value'):
self.conditionOption = 1
else:
self.conditionOption = 2
def exec(self):
self.exec_()
# Return which option the user selected. Default is 0 (None).
if (self.result() == QDialog.Accepted):
return self.conditionOption
class Scene(QWidget):
def __init__(self):
QWidget.__init__(self)
layout = QVBoxLayout()
self.setLayout(layout)
editN = QLineEdit()
editI = QLineEdit()
editPV = QLineEdit()
editFV = QLineEdit()
editCY = QLineEdit()
grid = QGridLayout()
grid.addWidget(QLabel("N"), 0, 0)
grid.addWidget(editN, 0, 1)
grid.addWidget(QLabel("I"), 1, 0)
grid.addWidget(editI, 1, 1)
grid.addWidget(QLabel("Present:"), 2, 0)
grid.addWidget(editPV, 2, 1)
grid.addWidget(QLabel("Future:"), 3, 0)
grid.addWidget(editFV, 3, 1)
grid.addWidget(QLabel("C/Y"), 4, 0)
grid.addWidget(editCY, 4, 1)
layout.addLayout(grid)
layout.addSpacing(12)
button = QPushButton("Calculate")
layout.addWidget(button)
# Connecting methods to each widget:
button.clicked.connect(self.calculate)
# As we may reference these variables within the methods
# of our class, we bind them to the scene class
self.editI = editI
self.editN = editN
self.editPV = editPV
self.editFV = editFV
self.editCY = editCY
# Note I'm not using QUiLoader. So self.ui is not available
# here.
def future_value(self):
N = int(self.editN.text())
I = float(self.editI.text())
PV = float(self.editPV.text())
C_Y = int(self.editCY.text())
jb = (1 + (I / (100 * C_Y))) ** N
res = abs(jb * PV)
print(res)
self.editFV.setText(str(round(res, 2)))
def present_value(self):
F_V = float(self.editFV.text())
N = int(self.editN.text())
I = float(self.editI.text())
C_Y = int(self.editCY.text())
x = (1 + (I / (100 * C_Y))) ** N
res = -abs(F_V / x)
print(res)
self.editPV.setText(str(round(res, 2)))
def calculate(self):
# As each element in the scene is being referenced from
# this class, we can access them using the self variable.
#
# To choose which operation, we must inform the application
# which widget is empty before calling a calculation method:
fv_empty = (self.editFV.text() == "")
pv_empty = (self.editPV.text() == "")
# If both inputs are empty, show an error dialog. That cannot
# happen based on the current rules.
if (fv_empty and pv_empty):
text = 'Both Present and Future Values Fields are blank. '
text += 'Fill at least one of them.'
dialog = QMessageBox()
dialog.setWindowTitle('Error')
dialog.setText(text)
dialog.setIcon(QMessageBox.Critical)
dialog.exec_()
# If both inputs are filled, create a rule and ask the user if
# it wants to continue, otherwise, do nothing:
elif (not fv_empty and not pv_empty):
dialog = OurCondition()
choice = dialog.exec()
# Inside OutCondition class we defined if the user
# clicked on Future Value button, choice is set to 1.
#
# If Present Value button is clicked, choice is set to 2.
#
# If it clicks on Cancel, or x button, choice is set to 0.
if (choice == 1):
self.future_value()
elif (choice == 2):
self.present_value()
# At least one field is empty:
else:
# If Future Field is empty, then call future_value to fill it
if (fv_empty):
self.future_value()
# If Present Field is empty, then call present_value to fill it
elif (pv_empty):
self.present_value()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowTitle('Compound Test')
scene = Scene()
self.setCentralWidget(scene)
if __name__ == '__main__':
app = QApplication()
win = MainWindow()
win.show()
app.exec_()
If you want, you can also use QComboBoxes to let the user choose from multiple options, if you need 3 or more methods to execute from the single button. As the number of options grows, it gets harder to create a logic using the conditions of wether each input is empty or filled.
With only 2 of them, that's ok. But with 3 or more, I strongly recomend you to rethink your User Interface and add a QComboBox.
If that's still not what you wanted, please edit your question to state what is that you're struggling at.
What you want is to learn how to parse an input. Basically you need to make an algorithm that checks if the user has input the interest rate.
Then, you make a conditional statement that says
if inputHasInterestRate() == false:
presentValue()
else:
futureValue()

Dynamically add QTableView to dynamically created tab pages (QTabWidget)

I am trying to have a series of QTableView created at runtime and added to newly created pages of a multipage QTabWidget.
All seems to go fine, but the QTableView don't show up.
The QTabWidget gets zeroed (reset to no pages) and refurbished (...) flawlessly (at least it looks like so) depending on the selection of a combobox (and the dictionaries therein related).
I am also using a delegate callback to include a column of checkboxes to the QTableView (thanks to https://stackoverflow.com/a/50314085/7710452), which works fine stand alone.
Here is the code.
Main Window
EDIT
as recommended by eyllanesc, here is the standalone module (jump to the end of the post for details on the part I think is problematic):
"""
qt5 template
"""
import os
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
from PyQt5 import uic
from configparser import ConfigParser, ExtendedInterpolation
from lib.SearchControllers import findGuis, get_controller_dict, show_critical, show_exception
import resources.resources
from lib.CheckBoxesDelegate import CheckBoxDelegate
myForm_2, baseClass = uic.loadUiType('./forms/setup.ui')
class MainWindow(baseClass):
def __init__(self, config_obj: ConfigParser,
config_name: str,
proj_name: str,
*args,
**kwargs):
super().__init__(*args, **kwargs)
self.ui = myForm_2()
self.ui.setupUi(self)
# your code begins here
self.setWindowTitle(proj_name + " Setup")
self.ui.logo_lbl.setPixmap(qtg.QPixmap(':/logo_Small.png'))
self.config_obj = config_obj
self.config_name = config_name
self.proj_filename = proj_name
self.proj_config = ConfigParser(interpolation=ExtendedInterpolation())
self.proj_config.read(proj_name)
self.guis_dict = {}
self.components = {}
self.cdp_signals = {}
self.root_path = self.config_obj['active']['controllers']
self.tableViews = []
self.tabs = []
self.iniControllersBox()
self.setActSignals()
self.load_bulk()
self.set_signals_table()
self.update_CurController_lbl()
self.update_ControllersTab() # here is where the action gets hot
# your code ends here
self.show() # here crashes if I passed the new tab to the instance of
# QTabView. otherwise it shows empty tabs
#########################################################
def load_bulk(self):
# get the list of running components into a dictionary
for i in self.list_controllers:
i_path = os.path.join(self.root_path, i)
print(i)
self.components[i] = get_controller_dict(i_path,
self.config_obj,
'Application.xml',
'Subcomponents/Subcomponent',
'Name',
'src')
for j in self.components[i]:
print(j)
signals_key = (i , j)
tgt = os.path.join(self.root_path, self.components[i][j])
self.cdp_signals[signals_key] = get_controller_dict(i_path,
self.config_obj,
self.components[i][j],
'Signals/Signal',
'Name',
'Type',
'Routing')
def set_signals_table(self):
self.ui.MonitoredDevicesTable.setHorizontalHeaderItem(0, qtw.QTableWidgetItem('GUI caption'))
self.ui.MonitoredDevicesTable.setHorizontalHeaderItem(1, qtw.QTableWidgetItem('Monitored Signal'))
def setActSignals(self):
self.ui.controllersBox.currentIndexChanged.connect(self.update_guis_list)
self.ui.controllersBox.currentIndexChanged.connect(self.update_CurController_lbl)
self.ui.controllersBox.currentIndexChanged.connect(self.update_ControllersTab)
def update_ControllersTab(self):
self.ui.componentsTab.clear() # this is the QTabWidget
self.tabs = []
self.tableViews = []
curr_controller = self.ui.controllersBox.currentText()
for i in self.components[curr_controller]:
if len(self.cdp_signals[curr_controller, i]) == 0:
continue
self.tabs.append(qtw.QWidget())
tabs_index = len(self.tabs) - 1
header_labels = ['', 'Signal', 'Type', 'Routing', 'Input']
model = qtg.QStandardItemModel(len(self.cdp_signals[curr_controller, i]), 5)
model.setHorizontalHeaderLabels(header_labels)
# in the next line I try to create a new QTableView passing
# the last tab as parameter, in the attempt to embed the QTableView
# into the QWidget Tab
self.tableViews.append(qtw.QTableView(self.tabs[tabs_index]))
tbw_Index = len(self.tableViews) - 1
self.tableViews[tbw_Index].setModel(model)
delegate = CheckBoxDelegate(None)
self.tableViews[tbw_Index].setItemDelegateForColumn(0, delegate)
rowCount = 0
for row in self.cdp_signals[curr_controller, i]:
for col in range(len(self.cdp_signals[curr_controller, i][row])):
index = model.index(rowCount, col, qtc.QModelIndex())
model.setData(index, self.cdp_signals[curr_controller, i][row][col])
try:
self.ui.componentsTab.addTab(self.tabs[tabs_index], i) # no problems, some controllers ask up to
except Exception as ex:
print(ex)
def update_CurController_lbl(self):
self.ui.active_controller_lbl.setText(self.ui.controllersBox.currentText())
def iniControllersBox(self):
self.list_controllers = [os.path.basename(f.path) for f in os.scandir(self.root_path) if f.is_dir() and str(
f.path).upper().endswith('NC')]
self.ui.controllersBox.addItems(self.list_controllers)
for i in range(self.ui.controllersBox.count()):
self.ui.controllersBox.setCurrentIndex(i)
newKey = self.ui.controllersBox.currentText()
cur_cntrlr = os.path.join(self.config_obj['active']['controllers'], self.ui.controllersBox.currentText())
self.guis_dict[newKey] = findGuis(cur_cntrlr, self.config_obj)
self.ui.controllersBox.setCurrentIndex(0)
self.update_guis_list()
def update_guis_list(self, index=0):
self.ui.GuisListBox.clear()
self.ui.GuisListBox.addItems(self.guis_dict[self.ui.controllersBox.currentText()])
if __name__ == '__main__':
config = ConfigParser()
config.read('./config.ini')
app = qtw.QApplication([sys.argv])
w = MainWindow(config, './config.ini',
'./test_setup_1.proj')
sys.exit(app.exec_())
and here the external to add the checkboxes column:
class CheckBoxDelegate(QtWidgets.QItemDelegate):
"""
A delegate that places a fully functioning QCheckBox cell of the column to which it's applied.
"""
def __init__(self, parent):
QtWidgets.QItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
"""
Important, otherwise an editor is created if the user clicks in this cell.
"""
return None
def paint(self, painter, option, index):
"""
Paint a checkbox without the label.
"""
self.drawCheck(painter, option, option.rect, QtCore.Qt.Unchecked if int(index.data()) == 0 else QtCore.Qt.Checked)
def editorEvent(self, event, model, option, index):
'''
Change the data in the model and the state of the checkbox
if the user presses the left mousebutton and this cell is editable. Otherwise do nothing.
'''
if not int(index.flags() & QtCore.Qt.ItemIsEditable) > 0:
return False
if event.type() == QtCore.QEvent.MouseButtonRelease and event.button() == QtCore.Qt.LeftButton:
# Change the checkbox-state
self.setModelData(None, model, index)
return True
if event.type() == QtCore.QEvent.MouseButtonPress or event.type() == QtCore.QEvent.MouseMove:
return False
return False
def setModelData (self, editor, model, index):
'''
The user wanted to change the old state in the opposite.
'''
model.setData(index, 1 if int(index.data()) == 0 else 0, QtCore.Qt.EditRole)
The 1st picture shows the layout in QTDesigner, the 2nd the result (emtpy tabs) when avoiding the crashing.
the QTabWidget has no problems in zeroing, or scale up, back to as many tab as I need, it's just that I have no clue on how to show the QTabview. My approach was to try to embed the QTabView in the tabpage passing it as parameter to the line creating the new QTabView.
Since I am using rather convoluted dictionaries, calling an XML parser to fill them up, not to mention the config files, I know even this version of my script is hardly reproduceable/runnable.
If someone had the patience of focusing on the update_ControllersTab method though, and tell me what I am doing wrong handling the QWidgets, it'd be great.
Again the basic idea is to clear the QTabWidget any time the user selects a different controller (combo box on the left):
self.ui.componentsTab.clear() # this is the QTabWidget
self.tabs = [] # list to hold QTabView QWidgets (pages) throughout the scope
self.tableViews = [] # list to hold QTabView(s) thorughout the scope
count how many tabs (pages) and hence embedded TabViews I need with the new controllers selected.
and then for each tab needed:
create a new tab (page)
self.tabs.append(qtw.QWidget())
tabs_index = len(self.tabs) - 1
create a new QTabView using a model:
header_labels = ['', 'Signal', 'Type', 'Routing', 'Input']
model = qtg.QStandardItemModel(len(self.cdp_signals[curr_controller, i]), 5)
model.setHorizontalHeaderLabels(header_labels)
self.tableViews.append(qtw.QTableView(self.tabs[tabs_index]))
tbw_Index = len(self.tableViews) - 1
self.tableViews[tbw_Index].setModel(model)
populate the TableView with data, and then finally add the tab widget (with the suppposedly embedded QTableView to the QTabWidget (the i argument is a string from my dbases Names:
self.ui.componentsTab.addTab(self.tabs[tabs_index], i)
This method is called also by the __init__ to initialize and apparently all goes error free, until the last 'init' statement:
`self.show()`
at which point the app crashes with:
Process finished with exit code 1073741845
on the other hand, if here instead of trying to embed the QTableView:
self.tableViews.append(qtw.QTableView(self.tabs[tabs_index]))
I omit the parameter, that is:
self.tableViews.append(qtw.QTableView())
the app doesn't crash anymore, but of course no QtableViews are shown, only empty tabpages:
As stupid as this may sound the problem is in... the delegate class that creates the checkboxes in the first column (see https://stackoverflow.com/a/50314085/7710452)
I commented out those two lines:
delegate = CheckBoxDelegate(None)
self.tableViews[tbw_Index].setItemDelegateForColumn(0, delegate)
and... bingo!
the CheckBoxDelegate works fine in the example shown in the post (a single QTableView form). I also tinkered around adding columns and rows, and moving the checkbox column back and forth with no problems. In that standalone. But as soon as I add the class and set the delegate, i am back at square zero, app crashing with:
Process finished with exit code 1073741845
so I am left with this problem now. Thnx to whomever read this.
Problem solved, see comment to post above.

Get state of checkbox cellWidget in QTableWidget PyQT

I work on python plugin for QGIS. In this plugin I have created a QTableWidget with 3 columns. These columns are QCheckbox, QTableWidgetItem and QComboBox. I would like to retrieve the values contained in these 3 columns. For the moment I managed to get the values of QComboBox and QTableWidgetItem but I can't seem to get the value of the QCheckBox.
liste = ['Carte 1','Carte 2','Carte 3','Carte 4','Carte 5','Carte 6']
combo_box_options = ["A4 Paysage","A4 Portrait", "A3 Paysage","A3 Portrait"]
self.dlg_format = Dialog_format()
self.dlg_format.tableWidget.setRowCount(len(liste))
for index in range(len(liste)):
item = QTableWidgetItem(liste[index])
self.dlg_format.tableWidget.setItem(index, 1, item)
self.dlg_format.tableWidget.setColumnWidth(0, 20)
self.dlg_format.tableWidget.setColumnWidth(1, 350)
combo = QComboBox()
for t in combo_box_options:
combo.addItem(t)
self.dlg_format.tableWidget.setCellWidget(index, 2, combo)
widget = QWidget()
checkbox = QCheckBox()
checkbox.setCheckState(Qt.Checked)
playout = QHBoxLayout(widget)
playout.addWidget(checkbox)
playout.setAlignment(Qt.AlignCenter)
playout.setContentsMargins(0,0,0,0)
widget.setLayout(playout)
self.dlg_format.tableWidget.setCellWidget(index, 0, widget)
self.dlg_format.show()
result = self.dlg_format.exec_()
if result:
for index in range(len(liste)):
text = self.dlg_format.tableWidget.item(index, 1).text()
format = self.dlg_format.tableWidget.cellWidget(index, 2).currentText()
check = self.dlg_format.tableWidget.cellWidget(index, 0).checkState() #Does not work
The QWidget is what is set as cell widget, not the checkbox, and that widget obviously has no checkState attribute.
There are various possibilities for this scenario.
Make the checkbox an attribute of the widget:
widget = QWidget()
widget.checkbox = QCheckBox()
playout.addWidget(widget.checkbox)
# ...
check = self.dlg_format.tableWidget.cellWidget(index, 0).checkbox.checkState()
Make the checkbox's checkState function a reference of the widget (note: no parentheses!) so that you can access it with the existing cellWidget(index, 0).checkState():
checkbox = QCheckBox()
widget.checkState = checkbox.checkState
Since all happens within the same scope (the function), you can totally ignore the cellWidget and use a list of tuples that contains the widgets:
widgets = []
for index in range(len(liste)):
# ...
widgets.append((item, combo, checkbox))
# ...
if result:
for item, combo, checkbox in widgets:
text = item.text()
format = combo.currentText()
check = checkbox.checkState()
Note that:
checkState()
returns a Qt.CheckState enum, which results in 2 (Qt.Checked) for a checked box; if you need a boolean, use isChecked() instead;
you can use enumerate instead of range, since you are iterating through the list items anyway: for index, text in enumerate(liste):;
if you don't need to add item data and the contents of the combo are always the same, just use combo.addItems(combo_box_options);
setting the column width for every cycle is pointless, just do it once outside the for loop;
if you use QHBoxLayout(widget) there's no need for widget.setLayout(playout), as the widget argument on a layout already sets that layout on the widget;
instance attribute are created in order to make them persistent (it ensures that they are not garbage collected and allows future access); from your code it seems unlikely that you're going to use that dialog instance after that function returns, so making it a member of the instance (self.dlg_format) is unrequired and keeps resources unnecessarily occupied: the dialog would be kept in memory even after it's closed, and would be then deleted and overwritten as soon as it's created again; just make it a local variable (dlg_format = Dialog_format());

PySide: How Could I trigger the current clicked QPushbutton, not other later added

I am new to PySide. In my program, I encountered a problem that when I click one button, it triggers other button later added. Thanks!
self.addContentButton = QtGui.QPushButton('Add')
self.addContentButton.clicked.connect(self.addContent)
def addContent(self):
'''
slot to add a row that include a lineedit, combobox, two buttons
'''
self.contentTabHBoxWdgt = QtGui.QWidget()
self.contentName = QtGui.QLineEdit('line edit')
self.conetentTypeBox = QtGui.QComboBox()
self.conetentTypeBox.addItem('elem1')
self.conetentTypeBox.addItem('elem2')
self.contentSave = QtGui.QPushButton('save',parent = self.contentTabHBoxWdgt)
self.contentSave.clicked.connect(self.contntSaveAct)
self.contentDelete = QtGui.QPushButton('delete',parent=self.contentTabHBoxWdgt)
self.contentDelete.clicked.connect(self.contntDel)
self.contentTabHBox = QtGui.QHBoxLayout()
self.contentTabHBox.addWidget(self.contentName)
self.contentTabHBox.addWidget(self.conetentTypeBox)
self.contentTabHBox.addWidget(self.contentSave)
self.contentTabHBox.addWidget(self.contentDelete)
self.contentTabHBoxWdgt.setLayout(self.contentTabHBox)
self.contentTabVBox.addWidget(self.contentTabHBoxWdgt)
def contntDel(self):
'''
slot to delete a row
'''
msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, '', 'Be sure to delete')
okBttn = msgBox.addButton('Yes', QtGui.QMessageBox.AcceptRole)
noBttn = msgBox.addButton('Cancel', QtGui.QMessageBox.RejectRole)
ret = msgBox.exec_()
if msgBox.clickedButton() == okBttn:
self.contentTabVBox.removeWidget(self.contentDelete.parentWidget());
When I Add one row and click its delete button, it does not work as expected.While I add two or three row , I click one delete button , it remove one row that is not the clicked delete button belong to. How could I achieve this function. Ths!
Your problem is because you aren't really taking advantage of object oriented programming properly.
All rows in your example call the same instance of the method contntDel. This method uses self.contentDelete which always contains a reference to the last row added.
What you need to do is separate out everything related to a row to a new class. When you add a row, create a new instance of this class and pass in the contentTabVBox. That way each row (or instance of the new class you will write) will have it's own delete method.
Without a complete code example, I can't provide a complete solution, but this should give you a rough idea:
class MyRow(object):
def __init__(self,contentTabVBox, rows):
self.contentTabVBox = contentTabVBox
self.my_list_of_rows = rows
self.addContent()
def addContent(self):
# The code for your existing addContent method here
def contntDel(self):
# code from your existing contntDel function here
# also add (if Ok button clicked):
self.my_list_of_rows.remove(self)
class MyExistingClass(??whatever you have here normally??):
def __init__(....):
self.addContentButton = QtGui.QPushButton('Add')
self.addContentButton.clicked.connect(self.addContent)
self.my_list_of_rows = []
def addContent(self):
my_new_row = MyRow(self.contentTabVBox,self.my_list_of_rows)
# You mustsave a reference to my_new_row in a list or else it will get garbage collected.
self.my_list_of_rows.append(my_new_row)
Hope that helps!

Is storing pyQT widgets in a list bad form?

So I am building I program that manages a bunch of custom slider widgets. Currently, I have a slider_container(class) that holds a list of slider objects(class). These slider objects are then inserted into the layout in the main window. This has been working well while I was only adding and moving the position of the sliders up and down. But when I try to delete the sliders, everything goes bad. When ever there the list of slider is manipulated (add, move or delete a slider), the clear and rebuild functions are called in the main window as seen below.
def clear_layout(self, layout):
print "Cleared Layout..."
while layout.count() > 0:
item = layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
def rebuild_slider_display(self):
""" Delete the old sliders, then add all the new sliders """
self.clear_layout(self.slider_layout)
print "Rebuild layout..."
print len(self._widgets.widgets)
for i, widget in enumerate(self._widgets.widgets):
print widget.slider_name, " ", i
self.slider_layout.insertWidget(i, widget)
print "Layout widget count: ", self.slider_layout.count()
Currently I am running into this error on this line "self.slider_layout.insertWidget(i, widget)"
RuntimeError: wrapped C/C++ object of type SliderWidget has been deleted
My hunch is that storing the actual widget in the widget container is bad form. I think what is happening when I deleteLater() a widget, is that it isnt just deleting a widget from the list, it actually deletes the widget class that was store in the widget container itself.
Hopefully that is explained clearly, thanks for your help in advance.
Edit:
Here is the widget class:
class SliderWidget(QWidget, ui_slider_widget.Ui_SliderWidget):
""" Create a new slider. """
def __init__(self, name, slider_type, digits, minimum, maximum, value, index, parent=None):
super(SliderWidget, self).__init__(parent)
self.setupUi(self)
self.slider_name = QString(name)
self.expression = None
self.accuracy_type = int(slider_type)
self.accuracy_digits = int(digits)
self.domain_min = minimum
self.domain_max = maximum
self.domain_range = abs(maximum - minimum)
self.numeric_value = value
self.index = index
#self.name.setObjectName(_fromUtf8(slider.name))
self.update_slider_values()
self.h_slider.valueChanged.connect(lambda: self.update_spinbox())
self.spinbox.valueChanged.connect(lambda: self.update_hslider())
self.edit.clicked.connect(lambda: self.edit_slider())
# A unique has for the slider.
def __hash__(self):
return super(Slider, self).__hash__()
# How to compare if this slider is less than another slider.
def __lt__(self, other):
r = QString.localAware.Compare(self.name.toLower(), other.name.toLower())
return True if r < 0 else False
# How to compare if one slider is equal to another slider.
def __eq__(self, other):
return 0 == QString.localAwareCompare(self.name.toLower(), other.name.toLower())
And here is the actually creation of the widget in the widget container:
def add_slider(self, params=None):
if params:
new_widget = SliderWidget(params['name'], params['slider_type'], params['digits'], params['minimum'],
params['maximum'], params['value'], params['index'])
else:
new_widget = SliderWidget('Slider_'+str(self.count()+1), 1, 0, 0, 50, 0, self.count())
#new_widget.h_slider.valueChanged.connect(self.auto_save)
#new_widget.h_slider.sliderReleased.connect(self.slider_released_save)
new_widget.move_up.clicked.connect(lambda: self.move_widget_up(new_widget.index))
new_widget.move_down.clicked.connect(lambda: self.move_widget_down(new_widget.index))
self.widgets.append(new_widget)
Thanks for all the help!
The problem I was having was with the way I cleared the layout. It is important to clear the layout from the bottom to the top as seen below.
for i in reversed(range(layout.count())):
layout.itemAt(i).widget().setParent(None)

Categories

Resources