PyQt5 : Get values from multiple QTextEdit obtained through loop - python

Currently working on a user interface through PyQt5, I'm trying to create TextEdit windows depending on the number selected from a Combobox (displayed in MainWindow).
This part is working (cf. first code below) however when I'm trying to get values (cf. second code) from text boxes created in the process, I only managed to get the value from the last text box.
Do you have any idea to get all of them?
Thanks in advance,
#Create text windows depending on number selected from a combo box
class Ui_SecondWindow(object):
def setupUi(self, SecondWindow):
_translate = QtCore.QCoreApplication.translate
SecondWindow.setObjectName("SecondWindow")
SecondWindow.resize(500, 720)
self.grid_layout = QtWidgets.QWidget(SecondWindow)
self.grid_layout.setGeometry(QtCore.QRect(0, 0, 250, 1000))
self.grid_layout.setObjectName("grid_layout")
self.grid = QtWidgets.QGridLayout(self.grid_layout)
self.grid.setContentsMargins(0,0,0,0)
self.grid.setObjectName('grid')
for i in range(1, int_nb_cond+1):
self.enter_nbcond = QtWidgets.QTextEdit(self.grid_layout)
self.enter_nbcond.setMaximumHeight(26)
self.enter_nbcond.setObjectName("enter_nbcond")
self.enter_nbcond.setPlaceholderText(_translate("SecondWindow", f"Name condition {i}:"))
self.enter_nbcond.resize(5,5)
self.grid.addWidget(self.enter_nbcond, i, 0)
#Get values from text edit windows
def ent(self):
print(self.enter_nbcond.toPlainText())

Standard rule: if you run for-loop then you have to keep results/items on list
Before loop you have to create list and inside loop you use .append() to keep widgets QTextEdit on this list.
# --- before loop ---
self.enter_nbcond = []
# --- loop ---
for i in range(1, int_nb_cond+1):
item = QtWidgets.QTextEdit(self.grid_layout)
item.setMaximumHeight(26)
item.setObjectName("enter_nbcond")
item.setPlaceholderText(_translate("SecondWindow", f"Name condition {i}:"))
item.resize(5,5)
self.grid.addWidget(item, i, 0)
self.enter_nbcond.append(item)
And later you have to use for-loop to get all values
def ent(self):
for item in self.enter_nbcond:
print(item.toPlainText())

Related

why command function of tkinter radiobuttons in a loop only works for the last iteration properly

I hope I can explain the problem properly enough... so basically, I am in the progress of creating a Gui.
In this extracted piece of code I want to iterate over my port_str list and make a frame with the name of each element and some radio buttons in the frame;
and for three of these buttons R1_wire, R2_wire, etc. I call another function sel_wire_1 with 'command' to either make the buttons (e.g.R1_discipline) active or disabled.
The problem is now that if I want to print the value of the selected radio button in one of the first frames it always shows me the name of the last element in the list and the value(either 1 or two) of the last element of the port_str list and this depended on activation in sel_wire__1 also only works for the last frame (the last element of the list port_str). Also this dependend selection of sel_wire_1 does inly work for the last created frame.
So I have several identical frames in front of me with different 'labels'(out of the list) but the command function only works correctly for the last frame(last element).
I tried so many things but I still don't know how to fix that as I'm new to tkinter and not that good with OOP. I did exactly the same thing in functional programming and it worked perfectly. So what is the problem in this oop case then?
Thanks to everyone who can help in advance
class MSM:
def __init__(self,top):
#create main frame, holds everything
self.main_frame= Frame(self.top,width = 50, height = 100, bd = 1)
self.main_frame.pack(fill=BOTH, expand=1)
#creat a canvas
self.my_canvas = Canvas(self.main_frame)
self.my_canvas.pack_propagate(0)
self.my_canvas.pack(side = LEFT, fill = BOTH, expand = 1)
self.second_frame = Frame(self.my_canvas)
port_str = [port1,port2,port3,port4,port5]
for port in port_str:
self.pins(port) #here i call the pins() function in a for loop to create s frame with some radiobuttons for each element of the port_str list
def pins(self,port):
#here im defining a frame and the label of each frame which are elements of the port_str list
self.frame_portdetails = Frame(self.frame_portdetails_top)
self.frame_portdetails.pack(fill= 'both', expand = 1)
self.var_wiretype = IntVar()
self.R1_wiretype = Radiobutton(self.frame_portdetails, text= ("wire",port),variable=self.var_wiretype, value=1,command= self.sel_wiretype_1)
self.R1_wiretype.grid( row=1,column=1,sticky='W')
self.R2_wiretype = Radiobutton(self.frame_portdetails, text=("wreal",port),variable=self.var_wiretype, value=2,command=self.sel_wiretype_1)
self.R2_wiretype.grid( row=1,column=2,sticky= 'W' )
#discipline here just more buttons are being defined
self.var_discipline = IntVar()
self.R1_discipline = Radiobutton(self.frame_portdetails, text="logic", variable=self.var_discipline, value=1 )
self.R1_discipline.grid(row=2,column=1,sticky='W')
self.R2_discipline = Radiobutton(self.frame_portdetails, text="vreal", variable=self.var_discipline, value=2)
self.R2_discipline.grid(row=2,column=2,sticky='W')
self.R3_discipline = Radiobutton(self.frame_portdetails, text="ireal", variable=self.var_discipline, value=3)
self.R3_discipline.grid(row=2,column=3,sticky='W')
self.R4_discipline = Radiobutton(self.frame_portdetails, text="other", variable=self.var_discipline, value=4 )
self.entry1 = ttk.Entry (self.frame_portdetails, width = 10 )
self.entry1.grid(row=3,column=2)
self.R4_discipline.grid(row=3,column=1,sticky='W')
def sel_wiretype_1(self): #this should either make a button active or
#disabled but is seems this is only being applied to the last element
#of the list/ the last frame(and the buttons inside) that is being
#created
if(self.var_wiretype.get()==1):
self.R1_discipline['state']='active'
self.R2_discipline['state']='disabled'
self.R3_discipline['state']='disabled'
self.var_discipline.set(1)
else:
self.R1_discipline['state']='disabled'
self.R2_discipline['state']='active'
self.R3_discipline['state']='active'
self.var_discipline.set(2)
if __name__ == "__main__":
root = tk.Tk()
app = MSM(root)
root.mainloop(

Displaying one cell from a selected row using pyqt5

im trying to create an UI with PyQt5 which has a tableWidget and a label that will display the text in every 4th column of the table, by order while the user scrolls through.
I cant seem to get the text in the selected cell from the table.. closest i got is this:
def open_csv_in_table (self):
f = open ("test.csv")
fData = csv.reader(f)
csvTable = list(fData)
self.tableWidget.setRowCount(len(csvTable))
self.tableWidget.setColumnCount(len(csvTable[0])-4)
for line in range( len(csvTable)):
for row in range(len(csvTable[0])):
self.tableWidget.setItem(line, row,QtWidgets.QTableWidgetItem(csvTable[line][row]))
self.tableWidget.setColumnWidth(0 , 10) # ID
self.tableWidget.setColumnWidth(1 , 150) # TEST NAME
self.tableWidget.setColumnWidth(2 , 50) # STATUS
self.tableWidget.setColumnWidth(3 , 300) # REMARKS
self.tableWidget.setColumnWidth(4 , 737) # LONG DESCRIPTION
def label_display(self):
self.label.setText(str(self.tableWidget.itemClicked))
print(str(self.tableWidget.itemClicked))
And im calling the display function with:
self.open_csv_in_table()
self.tableWidget.itemClicked.connect (lambda: self.label_display())
itemClicked is a signal that does not have the information of the item clicked so that is not the way to get that value. The signals what to do is pass the data through the arguments of the slot, in your case you must change to:
def label_display(self, item):
self.label.setText(item.text())
and
self.open_csv_in_table()
self.tableWidget.itemClicked.connect(self.label_display)

How to change font size of child QLabel widget from the groupBox

How can use different font & size for the child Widgets in the GroupBox and the tittle for the GroupBox in python
def panel(self):
groupBox = QtGui.QGroupBox("voltage Monitor")
groupBox.setFont(QtGui.QFont('SansSerif', 13)) # the title size is good
..
self.Voltage_Label = []
..
vbox = QtGui.QGridLayout()
self.Voltage_Label.append(QtGui.QLabel("voltage1 ")) # i need to have diff Font & size for these
self.Voltage_Label.append(QtGui.QLabel("voltage2 "))
self.Voltage_Label.append(QtGui.QLabel("voltage3 "))
..
vbox.addWidget(self.Voltage_Label[i], i, 0)
..
groupBox.setLayout(vbox)
return groupBox
I tired this
self.Voltage_Label.setFont(QtGui.QFont('SansSerif', 10))
I get this error
!! self.Voltage_Label.setFont(QtGui.QFont('SansSerif', 10))
AttributeError: 'list' object has no attribute 'setFont' !!
but for something like thistitle1 = QtGui.QLabel("Sample Title") as a child widget i can change it by
title1.setFont(QtGui.QFont('SansSerif', 10))
While I was waiting for an answer I wanted to give it a try and found this method/solution for my question:
self.Voltage_Label = []
self.Voltage_Label.append(QtGui.QLabel("voltage1 ")) # i need to have diff Font & size for these
self.Voltage_Label.append(QtGui.QLabel("voltage2 "))
self.Voltage_Label.append(QtGui.QLabel("voltage3 "))
.
.
for i in xrange(5):
newfont = QtGui.QFont("Times", 8, QtGui.QFont.Bold)
self.Voltage_Label[i].setFont(newfont)
You were trying to invoke the method setFont() of an object of the class list (which hasn't this method), not of the QtGui.QLabel object.
You can use a list comprehension for a better scalability and performance:
voltages = ["voltage1 ", "voltage2 ", "voltage3 "]
# Populates the list with QLabel objects
self.Voltage_Label = [QtGui.QLabel(x) for x in voltages]
# Invokes setFont() for each object
[x.setFont(QtGui.QFont("Times", 8, QtGui.QFont.Bold)) for x in self.Voltage_Label]
If you need more voltage labels you only have to modify the list voltages.
And then even:
[vbox.addWidget(self.Voltage_Label[i], i, 0) for i in range(len(self.Voltage_Label))]
also, you can try
font = QtGui.QFont("Times", 8, QtGui.QFont.Bold)
[label.setFont(font) for label in self.Voltage_Label]
if you are creating a font object every time you iterate over an item of self.Voltage_Label it will cost you some memory. so, therefore, you can share the same one with all the labels. whenever memory matters you can use this technique. but if you want to change the font on all label objects it won't change the font in other QLabel objects.

gtk.TreeView does not change after changing its gtk.ListStore:

Used: python 2.79, gtk 2.0
Dear python and PyGtk-users
In my program I want to give the opportunity to show searchresults of a great datafile in a gtk.TreeView using the method in the code. In the function that creates the ListStore I also create the csv-file the store is using. Now I can see, that the csv-file has changed after a new search with different keywords, but the TreeView does not, it still keeps the result of the first search, though I clear the store everytime self.search is running. It's really enerving if you need to restart the program for every new search...
I already tried some things, like creating a proper function for the datacreatin for the store, but nothing serves and the truth is that I don't have a lot of ideas...
And everything I found in the internet about this was about autorefreshing of the store with a timer or other topics more profound, seems like everyone instead of me knows how to do this...
Can anyone tell me, what mistake I am making? How do I refresh a TreeView?(as you can see self.search is called by a button...) here the relevant part of the code:
import gtk, csv
class BASE:
#...
#some other funtions, irrelevant for the problem
#...
def search(self, widget, event, data=None):
#...
#all the searchingstuff and the creation of 'inwrite.csv'
#...
with open('/home/emil/Documents/inwrite.csv', 'r') as storefile:
lines = storefile.readlines()
store = gtk.ListStore(str, str, str, str, str, str)
store.clear()
i=0
while i < len(lines):
line = [lines[i]]
csvfile = csv.reader(line, delimiter=',')
for row in csvfile:
temp = (row[0], row[1], row[2], row[3], row[4], row[5])
store.append(temp)
i = i + 1
self.tabview = gtk.TreeView(store)
self.tabview.set_rules_hint(True)
self.tabview.set_reorderable(True)
self.sw.add(self.tabview)
self.tabview.show()
self.create_columns(self.tabview)
def __init__(self):
#...
#creating the Window, entries,...
#...
self.button1 = gtk.Button('Buscar')
self.button1.connect('clicked', self.search, 'button 1')
#...
#packing and showing the whole GUI
#...
def create_columns(self, tabview):
rendererText = gtk.CellRendererText()
rendererText.props.wrap_width = 80
rendererText.props.wrap_mode = gtk.WRAP_WORD
column = gtk.TreeViewColumn('Codigo', rendererText, text=0)
column.set_sort_column_id(0)
self.tabview.append_column(column)
#...
#creating 5 more columns by the same principle
#...
def main(self):
gtk.main()
if __name__=='__main__':
base = BASE()
run = base
run.main()
thanks for your help, tell me if you need more information!
I do not know what is exactly wrong with your program (if you want to know that, you should provide a working minimal example).
Nevertheless, you do not need to manually update the TreeView because the TreeView always shows the content of the ListStore or TreeStore. You also do not have to create the ListStore everytime. You can create it one time and clear and insert the new entries when the event is emitted.
Here is a small example that shows how it works:
#from gi.repository import Gtk # uncomment to use PyGObject
import gtk as Gtk
class TestCase:
def __init__(self):
win = Gtk.Window()
button = Gtk.Button('Show')
button.connect('clicked', self.on_button_clicked)
self.clicked = 0
self.liststore = Gtk.ListStore(int)
self.liststore.append((self.clicked,))
view = Gtk.TreeView(self.liststore)
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn('demo', renderer, text=0)
view.append_column(column)
box = Gtk.HBox()
box.pack_start(button, True, True, 0)
box.pack_start(view, True, True, 0)
win.add(box)
win.connect('delete-event', Gtk.main_quit)
win.show_all()
def on_button_clicked(self, button):
self.clicked += 1
self.liststore.clear()
self.liststore.append((self.clicked,))
if __name__ == "__main__":
TestCase()
Gtk.main()
It also seems that you first create your csv file and read it afterwards. I don't know about the rest of your program but it is probably better to use the data directly and insert it into the ListStore and not read it from the csv file. It might be even possible then to just remove and insert the new entries and not clear the whole ListStore and insert everything again. That would be more efficient with bigger amounts of data.

How to sort items in Qt QListview using Qt.UserRole

I'm having some problem sorting the items in my QListView using values in a field I specified.
Basically what I'm trying to do is this:
Detect faces in a collection of photos and display them in a QListView
Cluster the faces (images)
Update the view by placing items in the list (which are face images) belonging to the same cluster in together. Concretely, if item 1, 3, 5 are in one cluster and items 2, 4, 6 are in another, then items 1, 3, 5 should be displayed (in whatever permutations) before any of items 2, 4, 6 are displayed or vice versa.
The way I went about doing this is to set one of the UserRole field for each QStandardItem in my list to the cluster label and then try to get the QStandardModel to sort according to this UserRole. This would then display items in the same cluster (i.e. with the same cluster label in the UserRole) next to each other.
I'm able to set the UserRole successfully for the items but calling the sort function on the QStandardModel did not sort the items even though when I set the sort role to be the default DisplayRole (i.e. sort according to the text label of each face) it worked as intended.
Can anyone tell me what is wrong with my code or offer an alternative method? I've googled sorting list and I found the following link on QSortFilterProxyModel but as I'm quite new to Qt, I'm not able to adapt it to my situation.
Thanks in advance to any replies.
Here is the relevant code:
import os
from PySide.QtGui import QListView, QStandardItemModel, QStandardItem, QIcon
from PySide.QtCore import Qt
class FacesView(QListView):
"""
View to display detected faces for user to see and label.
"""
UNCLUSTERED_LABEL = -1
CLUSTER_ROLE = Qt.UserRole + 1
def __init__(self, *args):
super(FacesView, self).__init__(*args)
self._dataModel = QStandardItemModel()
self.setModel(self._dataModel)
# Layout items in batches instead of waiting for all items to be
# loaded before user is allowed to interact with them.
self.setLayoutMode(QListView.Batched)
def updateFaceClusters(self, labels):
"""Update the cluster label for each face.
#param labels: [1 x N] array where each element is an integer
for the cluster the face belongs to."""
assert(len(labels) == self._dataModel.rowCount())
# Put the cluster label each item/face belong to in the
# CLUSTER_ROLE field.
for i in xrange(self._dataModel.rowCount()):
index = self._dataModel.index(i, 0)
self._dataModel.setData(index, labels[i], self.CLUSTER_ROLE)
# Use cluster label as sort role
self._dataModel.setSortRole(self.CLUSTER_ROLE)
# This does NOT seem to sort the items even though it works fine
# when sort role is the default Qt.DisplayRole.
self._dataModel.sort(0)
print("Finished updating face clusters")
def itemsInList(self):
"""Returns the label for a face and the path to its image.
#return: (label, path)"""
items = []
for i in xrange(self._dataModel.rowCount()):
label = self._dataModel.index(i, 0).data(Qt.DisplayRole)
imagePath = self._dataModel.index(i, 0).data(Qt.UserRole)
clusterLabel = self._dataModel.index(i, 0).data(self.CLUSTER_ROLE)
items.append((imagePath, label, clusterLabel))
return items
def addItem(self, label, imagePath):
"""Add an item to list view
#param label: The label associated with the item.
#param imagePath: Path to image for the icon."""
if os.path.exists(imagePath):
icon = QIcon(imagePath)
else:
icon = QIcon(':/res/Unknown-person.gif')
item = QStandardItem(icon, label)
item.setEditable(True)
# Add image path to the UserRole field.
item.setData(imagePath, Qt.UserRole)
# Add cluster label to image. CLUSTER_ROLE is where I intend
# to put the item's cluster label.
item.setData(self.UNCLUSTERED_LABEL, self.CLUSTER_ROLE)
# Prevent an item from dropping into another item.
item.setDropEnabled(False)
# Add item to list indirectly by adding it to the model.
self._dataModel.appendRow(item)
def clear(self):
self._dataModel.clear()
There's nothing wrong with the code you posted. So there must be something wrong with how you are using it. How are you generating the cluster labels?
Here's a test script using your FacesView class that sorts as you intended:
from random import randint
from PySide.QtGui import QWidget, QPushButton, QVBoxLayout, QApplication
from facesview import FacesView
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
self.list = FacesView(self)
self.button = QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout = QVBoxLayout(self)
layout.addWidget(self.list)
layout.addWidget(self.button)
def handleButton(self):
labels = []
self.list.model().setRowCount(0)
for row in range(10):
labels.append(randint(0, 3))
text = 'Item(%d) - Cluster(%d)' % (row, labels[-1])
self.list.addItem(text, 'icon.png')
self.list.updateFaceClusters(labels)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Categories