I have this QTreeWidget that I would like to be expanded by default.
I have read this same question many times but the solutions aren't working for me. I tried the commands for the root of my tree:
.ExpandAll() and .itemsExpandable()
and for the children .setExpanded(True) with no success.
Here is the code of my test application:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget,
QTreeWidget, QTreeWidgetItem, QVBoxLayout
)
# ----------------------------------------------------------------
unsorted_data = [
['att_0', 'a', 2020],
['att_0', 'a', 2015],
['att_2', 'b', 5300],
['att_0', 'a', 2100],
['att_1', 'b', 5013],
['att_1', 'c', 6500],
]
# Sort data
list_att = []
for elem in range(len(unsorted_data)) :
att_ = unsorted_data[elem][0]
if att_ not in list_att:
list_att.append(att_)
list_att.sort()
n_att = len(list_att)
data = ['']*n_att
tree = ['']*n_att
list_a_number = []
list_b_number = []
list_c_number = []
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My App")
widget = QWidget()
layout = QVBoxLayout()
widget.setLayout(layout)
# QTreeWidget
main_tree = QTreeWidget()
main_tree.setHeaderLabel('Test')
# main_tree.itemsExpandable() # NOT WORKING
# main_tree.expandAll() # NOT WORKING
sublevel_1 = []
for i, att in enumerate(list_att) :
list_a_number.clear()
list_b_number.clear()
list_c_number.clear()
# Create a dictionary
for elem in range(len(unsorted_data)) :
if unsorted_data[elem][0] == att :
if unsorted_data[elem][1]== 'a' :
list_a_number.append(str(unsorted_data[elem][2]))
if unsorted_data[elem][1] == 'b' :
list_b_number.append(str(unsorted_data[elem][2]))
if unsorted_data[elem][1] == 'c' :
list_c_number.append(str(unsorted_data[elem][2]))
data[i] = {'a' : list_a_number, 'b' : list_b_number, 'c' : list_c_number}
# Fill the Tree
items = []
att_id = list_att[i].split('\\')[-1]
tree[i] = QTreeWidgetItem([att_id])
tree[i].setExpanded(True) # NOT WORKING
sublevel_1.append(tree[i])
for key, values in data[i].items():
item = QTreeWidgetItem([key])
item.setCheckState(0, Qt.Checked)
tree[i].addChild(item)
for value in values :
child = QTreeWidgetItem([value])
child.setExpanded(True) # NOT WORKING
child.setCheckState(0, Qt.Checked)
item.addChild(child)
items.append(item)
main_tree.insertTopLevelItems(0, sublevel_1)
layout.addWidget(main_tree)
self.setCentralWidget(widget)
# ------------------------------------------------------------------
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
Try expandAll:
child.expandAll()
You have to use expandAll after placing all the items in the QTreeWidget:
main_tree.insertTopLevelItems(0, sublevel_1)
main_tree.expandAll()
layout.addWidget(main_tree)
Note: One of the errors in your case is that you invoke setExpanded before the item is added to the QTreeWidget. remove useless setExpanded
Related
I have a dictionary and a ComboBox that shows dictionary's values, i need to print in a textEdit the key of the dictionary's value chosen by the user.
Here's my code.
import sys
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QApplication, QDialog, QMainWindow
from PyQt5.uic import loadUi
from math import pi
class MainPage(QMainWindow):
classi = {None : None, "C25/30" : float(14.17),
"C28/35" : float(15.87), "C32/40" : float(18.13),
"C35/45" : float(19.83), "C40/50" : float(22.6),
"C45/55" : float(25.5), "C50/60" : float(28.3)}
Acciaio = {None : None, "B450C" : float(391.3)}
Ferri = {14 : float(1.54), 16 : float(2.01),
18 : float(2.54), 20 : float(3.14), 22 : float(3.8)}
N = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def __init__(self):
super(MainPage, self).__init__()
loadUi('Concrete.ui', self)
x = self.fillCombobox()
y = self.fillCombobox_2()
z = self.fillCombobox_3()
v = self.fillCombobox_4()
w = self.fillCombobox_5()
a = self.fillCombobox_6()
b = self.fillCombobox_7()
c = self.fillCombobox_8()
def fillCombobox(self):
for i in self.classi:
self.comboBox.addItem(i)
def fillCombobox_2(self):
for i in self.Acciaio:
self.comboBox_2.addItem(i)
def retrieveText(self):
x = self.comboBox.currentData()
self.textEdit.setText(x)
app = QApplication(sys.argv)
widget = MainPage()
widget.show()
sys.exit(app.exec_())
I know that the function retrieveText doesn't make what i want but it was a try and also this doesn't give any output.
You have to save the values associated with the keys through itemData and obtain it when an item is selected.
class MainPage(QMainWindow):
classi = {
None: None,
"C25/30": 14.17,
"C28/35": 15.87,
"C32/40": 18.13,
"C35/45": 19.83,
"C40/50": 22.6,
"C45/55": 25.5,
"C50/60": 28.3,
}
def __init__(self):
super(MainPage, self).__init__()
loadUi("Concrete.ui", self)
self.comboBox.currentIndexChanged[int].connect(self.retrieveText)
self.fillCombobox()
def fillCombobox(self):
for key, value in self.classi.items():
self.comboBox.addItem(key, value)
#pyqtSlot(int)
def retrieveText(self, index):
x = self.comboBox.itemData(index)
if x is not None:
self.textEdit.setText(str(x))
I have a GUI where user selects gas components from list and moves it to 'Chosen' and another button takes text from 'Chosen' that has columns: Gas Component, Molecular Weight, Mol%. The first two columns get information from a dictionary that i've created, and last column is user input.
When button is clicked and all values are filled in 'Chosen', it will ask user for a number, n = 1-6 , it will then take rows with n highest mol% in 'Chosen' and create n rows in Results and add Gas Component text with n highest mol% values, to first column in 'Results'
I am currently using dictionaries to keep track of information.
def calculategas(self):
#makes sure dictionaries are clear for any errors on rerunning button
self.sortedmol.clear()
self.componentDic1.clear()
self.componentDic2.clear()
self.componentDic3.clear()
self.mmDict.clear()
self.mfDict.clear()
self.mDict.clear()
self.massFracDict.clear()
self.molarmassDict.clear()
self.item_.clear()
self.lookup.clear()
root = self.chosen.invisibleRootItem()
child_count = root.childCount()
for i in range(child_count):
item = root.child(i)
#Takes text from self.chosen QTreeWidget (Top-right)
component = item.text(0)
molWeight = item.text(1)
componentMol = float(item.text(2))
#creates dictionary of items in self.chosen
self.componentDic1[component] = componentMol
self.componentDic2[molWeight] = componentMol
#Sorts dictionaries above from highest to lowest
self.sortedmol = dict(sorted(self.componentDic1.items(), key=operator.itemgetter(1),
reverse=True)) # Sorted component list - largest to smallest mol%
self.sortedmolar = dict(sorted(self.componentDic2.items(), key=operator.itemgetter(1),
reverse=True)) # Sorted molar mass list - largest to smallest mol%
# change values of self.sortedmol with keys of self.sortedmolar
self.lookup = {v:k for k, v in self.sortedmol.items()}
self.componentDic3 = {self.lookup[v]: float(k) for k, v in self.sortedmolar.items()}
##Copies so original doesn't change
self.mmDict = self.sortedmol.copy()
self.mfDict = self.mmDict.copy()
self.mDict = self.componentDic3.copy()
###Calculations
self.molarmassDict = {k: round(v * self.mmDict[k] / 100, 3) for k, v in self.mDict.items() if
k in self.mmDict}
summolmDict = round(sum(self.molarmassDict.values()), 3)
self.massFracDict = {k: round(self.molarmassDict[k] / summolmDict, 3) for k, v in self.molarmassDict.items()
if
k in self.molarmassDict}
componentNum, ok = QInputDialog.getText(None, 'Number of components', 'How many components do you wish to use?')
if (ok):
#Remove any items in result QTreeWidget
current_item = self.result.invisibleRootItem()
children = []
for child in range(current_item.childCount()):
children.append(current_item.child(child))
for child in children:
current_item.removeChild(child)
#Adds rows to self.result QTreeWidget
for i in range(int(componentNum)):
self.item_[i] = QtWidgets.QTreeWidgetItem(self.result)
self.item_[i].setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
if len(self.sortedmol) > int(componentNum): # takes only # of components user wants
##Adds the number of components user inputs with highest mol% to self.result
root = self.result.invisibleRootItem()
child_count = root.childCount()
for i in range(child_count):
item = root.child(i)
item.setText(0, str(list(self.massFracDict)[i])) # update first column with dictionary keys
else:
###This section will change
root = self.result.invisibleRootItem()
child_count = root.childCount()
for i in range(child_count):
item = root.child(i)
item.setText(0, str(list(self.massFracDict)[i])) # update first column with dictionary keys
Currently everything works except that when there is a duplicate value in mol% or molecular weight it will tend to skip it.
Dictionary self.sortedmol:
Keys = Gas Component text
Values = mol% text
Dictionary self.sortedmolar:
Keys = Molecular Weight text
Values = mol% text
Problems:
If two components have same molecular weight, it will ignore it
If two components have same mol%, it will ignore it
Overall goal: Add rows with n highest mol% in 'Chosen' to 'Result and keep values for later calculations.
Question: Is there any way to fix this error, or use another way to get same desired results?
Step1:
Step2:
If you want to get the n rows where the mol% are the highest then you must use a QSortFilterProxyModel to sort, and another to filter.
from PyQt5 import QtCore, QtGui, QtWidgets
ListRole = QtCore.Qt.UserRole
VisibleRole = QtCore.Qt.UserRole + 1
class TopProxyModel(QtCore.QSortFilterProxyModel):
#property
def number(self):
if not hasattr(self, "_number"):
self._number = -1
return self._number
#number.setter
def number(self, number):
self._number = number
self.invalidateFilter()
def filterAcceptsRow(self, sourceRow, sourceParent):
if self.number < 0:
ix = self.sourceModel().index(sourceRow, 2)
self.sourceModel().setData(ix, False, VisibleRole)
return True
return sourceRow < self.number
class BlankDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
if not index.data(VisibleRole):
option.text = ""
def setModelData(self, editor, model, index):
sm = model
ix = index
while hasattr(sm, "sourceModel"):
ix = sm.mapToSource(ix)
sm = sm.sourceModel()
sm.setData(ix, editor.value(), QtCore.Qt.DisplayRole)
if not sm.data(ix, VisibleRole):
sm.setData(ix, True, VisibleRole)
class ReadOnlyDelegate(QtWidgets.QStyledItemDelegate):
def createEditor(self, parent, option, index):
return None
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
datas = [
("IsonButane", 58.12, 13),
("IsonPentane", 75.12, 3),
("Methane", 16.04, 5),
("Nitrogen", 28.01, 5),
("Hexane", 86.17, 5),
("Hydrogen", 2.02, 13),
("Hydrogen Sulfide", 34.08, 2),
]
add_button = QtWidgets.QPushButton(">>>", clicked=self.add_row)
remove_button = QtWidgets.QPushButton("<<<", clicked=self.remove_row)
select_button = QtWidgets.QPushButton("Calculate", clicked=self.select)
sp = select_button.sizePolicy()
sp.setHorizontalPolicy(QtWidgets.QSizePolicy.Maximum)
select_button.setSizePolicy(sp)
self.listwidget = QtWidgets.QListWidget(
selectionMode=QtWidgets.QAbstractItemView.MultiSelection
)
for data in datas:
item = QtWidgets.QListWidgetItem(data[0])
item.setData(ListRole, data)
self.listwidget.addItem(item)
self.tree_widget = QtWidgets.QTreeWidget(
columnCount=3,
indentation=0,
selectionMode=QtWidgets.QAbstractItemView.MultiSelection,
)
for i, T in enumerate(
(ReadOnlyDelegate, ReadOnlyDelegate, BlankDelegate)
):
delegate = T(self.tree_widget)
self.tree_widget.setItemDelegateForColumn(2, delegate)
self.tree_widget.setHeaderLabels(
["Gas Component", "Molecular Weight", "Mol%"]
)
self.tree_view = QtWidgets.QTreeView(indentation=0)
proxy_sort = QtCore.QSortFilterProxyModel(self)
proxy_sort.setSourceModel(self.tree_widget.model())
proxy_sort.sort(2, QtCore.Qt.DescendingOrder)
self.proxy_top = TopProxyModel(self)
self.proxy_top.number = 0
self.proxy_top.setSourceModel(proxy_sort)
self.tree_view.setModel(self.proxy_top)
lay = QtWidgets.QGridLayout(self)
lay.addWidget(QtWidgets.QLabel("<b>Available Gases:</b>"), 0, 0)
lay.addWidget(self.listwidget)
vlay = QtWidgets.QVBoxLayout()
vlay.addStretch()
vlay.addWidget(add_button)
vlay.addWidget(remove_button)
vlay.addStretch()
lay.addLayout(vlay, 1, 1)
lay.addWidget(QtWidgets.QLabel("<b>Chosen Gases</b>"), 0, 2)
lay.addWidget(self.tree_widget, 1, 2)
lay.addWidget(select_button, 2, 2, alignment=QtCore.Qt.AlignCenter)
lay.addWidget(QtWidgets.QLabel("<b>Result:</b>"), 3, 2)
lay.addWidget(self.tree_view, 4, 2)
#QtCore.pyqtSlot()
def add_row(self):
for item in self.listwidget.selectedItems():
data = item.data(ListRole)
text = item.text()
if self.tree_widget.findItems(text, QtCore.Qt.MatchExactly):
continue
it = self.listwidget.takeItem(self.listwidget.row(item))
item = QtWidgets.QTreeWidgetItem()
self.tree_widget.addTopLevelItem(item)
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
for i, e in enumerate(data):
item.setData(i, QtCore.Qt.DisplayRole, e)
#QtCore.pyqtSlot()
def remove_row(self):
rows = [
self.tree_widget.indexOfTopLevelItem(item)
for item in self.tree_widget.selectedItems()
]
for row in sorted(rows, reverse=True):
item = self.tree_widget.takeTopLevelItem(row)
data = []
for i in range(self.tree_widget.columnCount()):
data.append(item.data(i, QtCore.Qt.DisplayRole))
it = QtWidgets.QListWidgetItem(data[0])
it.setData(ListRole, data)
self.listwidget.addItem(it)
if item is not None:
del item
#QtCore.pyqtSlot()
def select(self):
last_number = max(self.proxy_top.number, 0)
number, ok = QtWidgets.QInputDialog.getInt(
None,
"Number of components",
"How many components do you wish to use?",
last_number,
min=-1,
max=self.tree_widget.topLevelItemCount(),
)
if ok:
self.proxy_top.number = number
for i in range(self.tree_widget.topLevelItemCount()):
it = self.tree_widget.topLevelItem(i)
it.setData(2, VisibleRole, False)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I am making a physics calculator that takes values from the person and uses a calculation to show the answer. the problem is that when i press submit to take the values and save them as variable and show a button to show the answer, the app closes and sublime text shows REPL closed. i have no idea why. please help
import sys
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QToolTip , QCheckBox, QLCDNumber,QLineEdit,QInputDialog#just compile the modules like this
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QLabel
def ObjectDensity():
dflabel = QLabel("Please Enter Density of Fluid: ",w)
dflabel.move(25,220)
dflabel.show()
dfte = QLineEdit(w)
dfte.move(25,260)
dfte.show()
Weight2 = QLabel("Please Enter Weight:",w)
Weight2.move(25,300)
Weight2.show()
Wte = QLineEdit(w)
Wte.move(25,340)
Wte.show()
AIW2 = QLabel("Enter Apperent Immersed Weight: ",w)
AIW2.move(25,380)
AIW2.show()
aiwte = QLineEdit(w)
aiwte.move(25,420)
aiwte.show()
ansbutton = QPushButton("Submit",w)
ansbutton.move(50,480)
ansbutton.show()
ansbutton.clicked.connect(lambda: f1(dfte,Wte,aiwte,ansdo))
# df1 = dfte.text()
# w2 = Wte.text()
# aiw6 = aiwte.text()
def f1(dfte,Wte,aiwte,ansdo):
df1 = int(dfte.text())
w2 = int(Wte.text())
aiw6 = int(aiwte.text())
# ansdo = ((w2/w2-aiw6)*df1)
# ans4 = int(ansdo)
ans = QPushButton("press to show answer",w)
ans.move(50,250)
ans.show()
ans.clicked.connect(lambda:f2(w2,aiw6,df1))
def f2(w2,aiw6,df1):
ansdo = ((w2/w2-aiw6)*df1)
ans4 = int(ansdo)
answer = QLabel(ans4,w)
answer.move(75,300)
answer.show()
def arch():
l_archimedes = QLabel("Archimedes' Law",w)
l_archimedes.move(25,80)
l_archimedes.show()
l_archimedes2 = QLabel("Density of Object/Density of Fluid = Weight/(Weight - Apparent Immersed Weight)",w)
l_archimedes2.move(25,110)
l_archimedes2.show()
Archimedeslaw = QLabel("What Do You Want to Solve For:",w)
Archimedeslaw.move(25,145)
Archimedeslaw.show()
dop = QPushButton("Density of Object",w)
dop.move(25,160)
dop.show()
dop.clicked.connect(ObjectDensity)
DF = QPushButton("Density of Fluid",w)
DF.move(200,160)
DF.show()
Weight = QPushButton("Weight",w)
Weight.move(375,160)
Weight.show()
AIW = QPushButton("Apperent Immersed Weight",w)
AIW.move(500,160)
AIW.show()
app = QApplication(sys.argv)
w = QWidget()
w.resize(900,600)
w.move(460,0)
w.setWindowTitle("Physics Laws")
L1 = QLabel("Welcome To Physics Laws",w,)
L1.move(25,0)
b1 = QPushButton("Archimedes Law",w)
b1.move(25,45)
b1.clicked.connect(arch)
w. show()
sys.exit(app.exec_())
Try it:
import sys
from PyQt5.QtWidgets import (QWidget, QApplication, QPushButton, QLabel,
QToolTip, QCheckBox, QLCDNumber,
QLineEdit, QInputDialog) # ??? just compile the modules like this
from PyQt5.QtGui import QIcon
def ObjectDensity():
dflabel = QLabel("Please Enter Density of Fluid: ",w)
dflabel.move(25,220)
dflabel.show()
dfte = QLineEdit(w)
dfte.move(25,260)
dfte.show()
Weight2 = QLabel("Please Enter Weight:",w)
Weight2.move(25,300)
Weight2.show()
Wte = QLineEdit(w)
Wte.move(25,340)
Wte.show()
AIW2 = QLabel("Enter Apperent Immersed Weight: ",w)
AIW2.move(25,380)
AIW2.show()
aiwte = QLineEdit(w)
aiwte.move(25,420)
aiwte.show()
ansbutton = QPushButton("Submit",w)
ansbutton.move(50,480)
ansbutton.show()
ansbutton.clicked.connect(lambda: f1(dfte,Wte,aiwte )) #,ansdo)) # <------------------
# df1 = dfte.text()
# w2 = Wte.text()
# aiw6 = aiwte.text()
def f1(dfte,Wte,aiwte): #,ansdo): # <------------------
df1 = int(dfte.text())
w2 = int(Wte.text())
aiw6 = int(aiwte.text())
# ansdo = ((w2/w2-aiw6)*df1)
# ans4 = int(ansdo)
ans = QPushButton("press to show answer", w)
#ans.move(50,250)
ans.move(50, 520) # <------------------
ans.show()
answer.hide() # <------------------
ans.clicked.connect(lambda: f2(w2, aiw6, df1))
def f2(w2, aiw6, df1):
print("def f2(w2=`{}`, \naiw6=`{}`, \ndf1=`{}`):".format(w2, aiw6, df1) )
ansdo = ((w2/w2-aiw6)*df1) # w2/w2 = 1 <--- ???
#ans4 = int(ansdo)
#answer = QLabel(ans4, w)
#answer.move(75,300)
answer.setText(str(ansdo)) # <------------------
answer.show() # <------------------
def arch():
l_archimedes = QLabel("Archimedes' Law",w)
l_archimedes.move(25,80)
l_archimedes.show()
l_archimedes2 = QLabel("Density of Object/Density of Fluid = Weight/(Weight - Apparent Immersed Weight)",w)
l_archimedes2.move(25,110)
l_archimedes2.show()
Archimedeslaw = QLabel("What Do You Want to Solve For:",w)
Archimedeslaw.move(25,145)
Archimedeslaw.show()
dop = QPushButton("Density of Object",w)
dop.move(25,160)
dop.show()
dop.clicked.connect(ObjectDensity)
DF = QPushButton("Density of Fluid",w)
DF.move(200,160)
DF.show()
Weight = QPushButton("Weight",w)
Weight.move(375,160)
Weight.show()
AIW = QPushButton("Apperent Immersed Weight",w)
AIW.move(500,160)
AIW.show()
app = QApplication(sys.argv)
w = QWidget()
w.resize(900,600)
w.move(460,0)
w.setWindowTitle("Physics Laws")
L1 = QLabel("Welcome To Physics Laws",w,)
L1.move(25,0)
b1 = QPushButton("Archimedes Law",w)
b1.move(25,45)
b1.clicked.connect(arch)
answer = QLabel(w) # <------------------
answer.move(50, 550) # <------------------
w. show()
sys.exit(app.exec_())
I asked a similar question before, but the result didn't work, and I don't know why.
Here was the original code:
def click_btn_printouts(self):
self.cur.execute("""SELECT s.FullName, m.PreviouslyMailed, m.nextMail, m.learnersDate, m.RestrictedDate, m.DefensiveDate FROM
StudentProfile s LEFT JOIN Mailouts m ON s.studentID=m.studentID""")
self.all_data = self.cur.fetchall()
self.search_results()
self.table.setRowCount(len(self.all_data))
self.tableFields = ["Check","Full name","Previously mailed?","Next mail","learnersDate","Restricted date","Defensive driving date"]
self.table.setColumnCount(len(self.tableFields))
self.table.setHorizontalHeaderLabels(self.tableFields)
self.checkbox_list = []
for i, self.item in enumerate(self.all_data):
FullName = QtGui.QTableWidgetItem(str(self.item[0]))
PreviouslyMailed = QtGui.QTableWidgetItem(str(self.item[1]))
LearnersDate = QtGui.QTableWidgetItem(str(self.item[2]))
RestrictedDate = QtGui.QTableWidgetItem(str(self.item[3]))
DefensiveDate = QtGui.QTableWidgetItem(str(self.item[4]))
NextMail = QtGui.QTableWidgetItem(str(self.item[5]))
self.table.setItem(i, 1, FullName)
self.table.setItem(i, 2, PreviouslyMailed)
self.table.setItem(i, 3, LearnersDate)
self.table.setItem(i, 4, RestrictedDate)
self.table.setItem(i, 5, DefensiveDate)
self.table.setItem(i, 6, NextMail)
chkBoxItem = QtGui.QTableWidgetItem()
chkBoxItem.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
chkBoxItem.setCheckState(QtCore.Qt.Unchecked)
self.checkbox_list.append(chkBoxItem)
self.table.setItem(i, 0, self.checkbox_list[i])
The suggested code to add was this (indentation accurate) to the end of the function:
self.changed_items = set()
self.table.itemChanged.connect(self.log_change)
And add the following function:
def log_change(self):
self.changed_items.add(self.item)
print(self.item)
The expected print was the edited data, but what I get is the data before it was edited.
I can't use QTableView and QtSql unless I can find a way to use it with an SQL query, get every selected record into a list, and stop certain columns from being edited. If anybody knows how to do these, that would be great, I just really have no time to go through all the documentation myself at the moment.
All I want to do is have the user be able to change data from the QTableWidget, and get that changed data as a record.
Basically, my end goal is to have the equivalent of setEditStrategy(QSqlTableModel.OnManualSubmit) for QTableWidget.
I have been trying to figure this out for a while now and I just want it sorted out, it is the last thing I need to do to finish this program for a client.
It is always difficult to answer without a minimal working example, so I produced one myself and put the suggestion from the other post in, modifying it, such that it outputs the changed item's text and its position inside the table.
# runs with Python 2.7 and PyQt4
from PyQt4 import QtGui, QtCore
import sys
class App(QtGui.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent)
self.setMinimumSize(600,200)
self.all_data = [["John", True, "01234", 24],
["Joe", False, "05671", 13],
["Johnna", True, "07145", 44] ]
self.mainbox = QtGui.QWidget(self)
self.layout = QtGui.QVBoxLayout()
self.mainbox.setLayout(self.layout)
self.setCentralWidget(self.mainbox)
self.table = QtGui.QTableWidget(self)
self.layout.addWidget(self.table)
self.button = QtGui.QPushButton('Update',self)
self.layout.addWidget(self.button)
self.click_btn_printouts()
self.button.clicked.connect(self.update)
def click_btn_printouts(self):
self.table.setRowCount(len(self.all_data))
self.tableFields = ["Name", "isSomething", "someProperty", "someNumber"]
self.table.setColumnCount(len(self.tableFields))
self.table.setHorizontalHeaderLabels(self.tableFields)
self.checkbox_list = []
for i, self.item in enumerate(self.all_data):
FullName = QtGui.QTableWidgetItem(str(self.item[0]))
FullName.setFlags(FullName.flags() & ~QtCore.Qt.ItemIsEditable)
PreviouslyMailed = QtGui.QTableWidgetItem(str(self.item[1]))
LearnersDate = QtGui.QTableWidgetItem(str(self.item[2]))
RestrictedDate = QtGui.QTableWidgetItem(str(self.item[3]))
self.table.setItem(i, 0, FullName)
self.table.setItem(i, 1, PreviouslyMailed)
self.table.setItem(i, 2, LearnersDate)
self.table.setItem(i, 3, RestrictedDate)
self.changed_items = []
self.table.itemChanged.connect(self.log_change)
def log_change(self, item):
self.table.blockSignals(True)
item.setBackgroundColor(QtGui.QColor("red"))
self.table.blockSignals(False)
self.changed_items.append(item)
print item.text(), item.column(), item.row()
def update(self):
print "Updating "
for item in self.changed_items:
self.table.blockSignals(True)
item.setBackgroundColor(QtGui.QColor("white"))
self.table.blockSignals(False)
self.writeToDatabase(item)
def writeToDatabase(self, item):
text, col, row = item.text(), item.column(), item.row()
#write those to database with your own code
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
thisapp = App()
thisapp.show()
sys.exit(app.exec_())
You may use this example now to refer to any further problems.
The following is an example of inputs that could potentially be added.
value 0: [ 0 ] [ 100 ]
value 1: [ 200 ] [ 300 ]
value 2: [ 400 ] [ 500 ]
The above is correct
value 0: [ 0 ] [ 800 ]
value 1: [ 700 ] [ 600 ]
value 2: [ 500 ] [ 400 ]
The above is incorrect: Make sure each value is higher than the last
The code below has each value that the user inputs added to an array.
def val_norm(self, MainWindow, count):
self.verticalLayout_2 = QtGui.QVBoxLayout()
self.verticalLayout_2.setMargin(11)
self.verticalLayout_2.setSpacing(6)
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
self.horizontalLayout_2 = QtGui.QHBoxLayout()
self.horizontalLayout_2.setMargin(11)
self.horizontalLayout_2.setSpacing(6)
self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
self.norm_label = QtGui.QLabel(self.Normalization)
self.norm_label.setObjectName(_fromUtf8("norm_label"))
self.horizontalLayout_2.addWidget(self.norm_label)
self.norm_spinBox_[(count*2)-1] = QtGui.QSpinBox(self.Normalization) # All the left side boxes
self.norm_spinBox_[(count*2)] = QtGui.QSpinBox(self.Normalization) # All the right side boxes
self.norm_spinBox_[(count*2)-1].setMaximum(1000) # set the maximums for the left side
self.norm_spinBox_[(count*2)].setMaximum(1000) # set the maximums for the right side
self.horizontalLayout_2.addWidget(self.norm_spinBox_[(count*2)-1]) # adding the actual object to UI (left)
self.horizontalLayout_2.addWidget(self.norm_spinBox_[(count*2)]) # adding the actual object to UI (right)
self.verticalLayout_2.addLayout(self.horizontalLayout_2) # setting up layout
self.verticalLayout.addLayout(self.verticalLayout_2) # setting up layout
# for debugging purposes
self.norm_spinBox_[(count*2)-1].editingFinished.connect(lambda: print(self.norm_spinBox_[(count*2)-1].text()))
self.norm_spinBox_[(count*2)].editingFinished.connect(lambda: print(self.norm_spinBox_[(count*2)].text()))
self.norm_label.setText(_translate("MainWindow", "Value {}".format(self.count), None))
self.count += 1
if self.norm_spinBox_[(count*2)-1].text() > self.norm_spinBox_[count*2].text():
print("That's wrong!")
I thought that using an if statement would work, but I'm clearly misguided on how I should be approaching this. Correct me if I'm wrong, but I'm thinking it's not working because I'm not connecting the if statement to each box after it's been edited.
if self.norm_spinBox_[(count*2)-1].text() > self.norm_spinBox_[count*2].text():
print("That's wrong!")
you could use the valueChanged() signal of QSpinBox to setMinimum() of the following spinboxes according to the value of the former spinbox, so the spinboxes only accept values >= minimum, here a working example:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class SpinboxWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.layout = QVBoxLayout()
# self.signalMapper = QSignalMapper(self)
# self.signalMapper.mapped[str].connect(self.setspbMin)
self.addSpinboxes(10) # add an arbitrary number of spinboxes
self.setLayout(self.layout)
def addSpinboxes(self, n):
spb = []
for i in range(n):
# objectname = 'spinbox_{}'.format(i)
spinbox = QSpinBox()
# spinbox.setObjectName(objectname) # to identify the spinbox later
spinbox.setMaximum(100)
# spinbox.valueChanged.connect(self.signalMapper.map)
# self.signalMapper.setMapping(spinbox, objectname) # sends the objectname with his mapped() signal
spb.append(spinbox) # added in edit
self.layout.addWidget(spinbox)
for i in range(n-1):
spb[i].valueChanged.connect(spb[i + 1].setMinimum)
'''
def setspbMin(self, identifier):
spinbox = self.findChild(QSpinBox,identifier) # don't use QObject.sender() see Documentation
nextIndex = int(identifier.lstrip('spinbox_')) + 1
nextIdentifier = 'spinbox_{}'.format(nextIndex)
nextSpinbox = self.findChild(QSpinBox,nextIdentifier)
try:
nextSpinbox.setMinimum(spinbox.value())
except AttributeError:
pass
'''
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle('plastique')
widget = SpinboxWidget()
widget.setWindowTitle('Spinbox Tester')
widget.show()
sys.exit(app.exec_())
edit:
as suggested by ekhumoro signalMapper is not necessary -> outcommented lines are no longer needed. the valueChanged-signal is connected to setMinimum() of the following spinbox.