Qt: QFormLayout - How to find the row from the button - python-3.x

How to find the row number of QFormLayout() from a button which is in that row? I have a delete button on each row of the form. Such that if I click the delete button, that specific row will get deleted. For this, I am planning to use the command QtWidgets.QFormLayout.removeRow(row) command. I have defined the QFormLayout() within my def __init__(self): function like so.
self.attachForm = QFormLayout()
I also have an Add button which calls the self.attachBtn_clicked(self) function given below. So every time the Add button is clicked a new row is added. Any help will be appreciated.
def attachBtn_clicked(self):
hbox = QHBoxLayout()
self.attachForm.addRow('',hbox)
browseBtn = QPushButton("Open")
hbox.addWidget(browseBtn)
addAttachEdit = QLineEdit()
hbox.addWidget(addAttachEdit)
delAttachBtn = QPushButton("x")
delAttachBtn.setFixedSize(15,15)
delAttachBtn.clicked.connect(self.delAttachBtn_clicked)
hbox.addWidget(delAttachBtn)
The objective is now to write the self.delAttachBtn_clicked(self) function which will delete the specific row.

You can iterate through the rows and find the button that matches the sender.
def delAttachBtn_clicked(self):
for i in range(self.attachForm.rowCount()):
if self.sender() == self.attachForm.itemAt(i, QFormLayout.FieldRole).itemAt(2).widget():
self.attachForm.removeRow(i)
return
itemAt(2) is used since delAttachBtn is the 3rd item in each QHBoxLayout.

Related

Listbox resizing itself when new data is added

I am trying to add information to my Listbox and keeping it the size I state when I configure it. Here is my code for the Listbox with the scrollbar and an image of what it looks like.
Picture of the listbox.
taskList = Listbox(setBox, bg="#1B2834",fg="white")
taskList.configure(width=183,height=39)
taskList.pack(side=LEFT,fill=BOTH)
taskScroll = Scrollbar(setBox)
taskScroll.configure(bg="#1B2834",width=18)
taskScroll.pack(side = RIGHT, fill = BOTH)
taskList.config(yscrollcommand = taskScroll.set)
taskScroll.config(command = taskList.yview)
Now, when i click a button the command is to execute this following code:
def savetasks():
#make tasks
letters = string.ascii_uppercase
result_str = ''.join(random.choice(letters) for i in range(4))
num = str(random.randrange(0,9))
taskIDnum = num+result_str
taskIDLBL = Label(taskList, text=taskIDnum,bg="#1B2834", fg="White")
taskIDLBL.configure(padx=20,pady=10)
taskIDLBL.pack()
This code works fine as well, creating new labels with a random ID but it resizes the listbox to look like this...
Picture of the list box after clicking the button to execute the command.
Lastly, the scroll bar is not scrollable and when I create a lot of id's that end up going off my screen I cannot use the scroll bar to scroll down to see them, is there a way to not let the Listbox be resized and is it possible to set the Listbox with max and min-height?
If there is an easier way to do this without using a Listbox please let know, I just need to able to scroll down to see all the other id's and I didn't see any other way to use a scroll bar, that I NEEDED to use a Listbox

How to give button ID when calling a create button function

Please excuse my ignorance as I'm new to Python and am experimenting.
I am creating a dashboard that allows users to open a file. When a user opens a file, the software adds a row with the file name, progress bar and a few buttons all on the same row. The row creation is done by calling a function which increments the row value every time its called to ensure it doesn't replace the existing row but instead adds another row beneath it to represent the latest file opened by the user.
Lets say there are two rows on the dashboard with their respective file names and buttons. My problem is when I go to press the button on the first row, it thinks that I'm pressing the button on the second row simply because the counter is set to 2 when adding a second row and I am trying to identify which button the user is pressing by checking the row but am unable to do that due to the counter.
Is there anyway I can "bind" the row value to the button when declaring it when calling the create button function so that they keep some form of ID and I can tell which row the user is interacting with?
I have tried assigning the button an ID when calling the function to identify which button is being pressed and on what row, but the button gets overwritten on all rows when the function is called again.
I have also tried to check what button the user is pressing by checking the text of the button, but that also gets overwritten when the function is re-called regardless if the names of the buttons are different on the UI itself.
Any help is very much appreciated. Hopefully I was clear enough.
def OpenFile(self): # user opening file which creates row with button
print("Opening")
btnCreation(self)
def btnCreation(self): # function to create buttons dynamically
incrementFunction(self)
global EXEBtn
EXEBtn = Button(Main_frame,
text="Execute",
width=8,
command=self.Execute)
EXEBtn.grid(row=RowCounter, column=2, padx=3)
def incrementFunction(self):
global RowCounter
RowCounter = RowCounter + 1
def Execute(self):
# check which row the execute button was pressed on to do something
A simple and quick solution is just to give the function the current RowCounter value with a lambda expression like this:
def btnCreation(self): # function that creates button dynamically
incrementFunction(self)
global EXEBtn
EXEBtn = Button(Main_frame,
text="Execute",
width=8,
command=lambda e=RowCounter: self.Execute(e))
EXEBtn.grid(row=RowCounter, column=2, padx=3)
The specific change here is command=lambda e=RowCounter.get(): self.Execute(e), changing the way the buttons command works. Basically it is creating a lambda expression that saves the value of RowCounter, which will be passed to the Execute() function when the button is pressed.

QTableView: Prevent a user from navigating away from a spesific row

I am having trouble preventing the user form changing the current selection, if the save action did not complete successfully. I can re-select a row using the QTableView's selection models' currentRowChanged signal but although the selection change, the blue selection indicator does not. See the image below.
Example: In the image below the user attempted to add a new row nr 537. But the save action got an error and I don't want the user to navigate away from row 537 before the record is either deleted or edited and then saved
Question: How do I move the blue line to the current selection? (the current selection is the last row) (The QTableView's Selection Behavior is set to select rows)
Here is the code I got so far:
def __init__(self, parent):
...
self.__tableViewSelectionModel = self.__ui.tableView.selectionModel()
self.__tableViewSelectionModel.currentRowChanged.connect(self.rowChanged)
def rowChanged(self, current=None, previous=None):
if save() == True:
self.__ui.tableView.clearSelection()
self.__ui.tableView.selectRow(previous.row())
Replacing this:
self.__ui.tableView.clearSelection()
self.__ui.tableView.selectRow(previous.row())
with this:
QtCore.QTimer.singleShot(0.00001, lambda: self.__ui.tableView.selectRow(previous.row()))
produced the desired outcome

populate a list box with .csv items

I have this.
scrollbar = tk.Scrollbar(listbox)
scrollbar.pack(side=RIGHT, fill=Y)
listbox = tk.Listbox(root)
listbox.insert(1,'a')
listbox.pack()
listbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=listbox.yview)
with open('testCUR.csv', newline='') as csvfile:
spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|')
for row in spamreader:
print(', '.join(row),'\n')
I was wondering how to correctly populate the listbox with values from a .csv file?
And also why I cannot use the scroll bar correctly?
Once the list box is populated I would like to put the value of the last cell from the selection to a variable for use in a URL string. I havent found any tutorials for this so was looking for help here.
I have tried this.
listbox = tk.Listbox(root, height=1)
listbox.place(x=300,y=75)
with open('testCUR.csv', 'r') as f:
reader = csv.reader(f)
your_list = list(reader)
for item in your_list:
listbox.insert(end, item)
or this inputs only first entry
with open('testCUR.csv', 'r') as f:
reader = csv.reader(f)
your_list = list(reader)
for item in your_list:
listbox.insert(1, item)
Once I have all the values in the list box and it is scrollable which I would love if it was just damn.
listbox = tk.Listbox(root, height=1, scroll=auto)
I need to be able to use only the currency code which is the last value in the .csv file and use it as the selected value and then use it in a variable for the url. The .csv file looks like this.
Algeria د.ج DZD
Andorra € EUR
Angola Kz AOA
Anguilla $ XCD
Antigua and Barbuda $ XCD
Argentina $ ARS
Armenia AMD
Aruba ƒ AWG
Ascension Island £ (*none*)
I am also trying pandas but am new to it because it looks much easier and cleaner to use.
csv_file = ('testCUR.csv')
df = pd.read_csv(csv_file)
saved_col = df['CODE']
for item in df:
listbox.insert(end, saved_col)
Always error. NameError: name 'end' is not defined.
Happens with END also
I was wondering how to correctly populate the listbox with values from a .csv file?
To insert text into a listbox you must give it an index to tell it where to insert. The index is a string that is either a number, or the string "end". In your case you used a variable named end, which of course doesn't exist.
You can insert the text like this:
listbox.insert("end", item)
And also why I cannot use the scroll bar correctly?
You haven't described why your scrollbar is not correct.
Making a scrollbar works requires two-way conversation. The scrollbar must be told what widget to scroll (via the command attribute, and the widget needs to know which scrollbar to update when it is scrolled (via the yscrollcommand or xscrollcommand attribute).
It's also good to explicitly set whether the scrollbar is horizontal or vertical, though in your case it's vertical which is the default.
And finally, it's generally the best practice to make the scrollbar and the widget being scrolled to have the same parent. You made the mistake of making the scrollbar a child of the listbox. Instead, make it a child of whatever the listbox is a child of. You also made the mistake of trying to make it the parent of the listbox before you created the listbox. A widget must exist before you can give it children.
Here is how to create the listbox and scrollbar:
listbox = tk.Listbox(root)
scrollbar = tk.Scrollbar(root, orient="vertical", command=listbox.yview)
listbox.configure(yscrollcommand=scrollbar.set)

Tkinter insert a Combobox inside a Treeview widget

For example, lets create a Treeview widget using a class as follows:
class FiltersTree:
def __init__(self, master, filters):
self.master = master
self.filters = filters
self.treeFrame = Frame(self.master)
self.treeFrame.pack()
self._create_treeview()
self._populate_root()
def _create_treeview(self):
self.dataCols = ['filter', 'attribute']
self.tree = ttk.Treeview(self.master, columns = self.dataCols, displaycolumns = '#all')
Populate root, insert children as usual. At the end of the codeblock, you can see where I want to put a Combobox in the tree, using a Combo object:
def _populate_root(self):
# a Filter object
for filter in self.filters:
top_node = self.tree.insert('', 'end', text=filter.name)
# a Field object
for field in filter.fields:
mid_node = self.tree.insert(top_node, 'end', text = field.name)
# insert field attributes
self.insert_children(mid_node, field)
def insert_children(self, parent, field):
name = self.tree.insert(parent, 'end', text = 'Field name:',
values = [field.name])
self.tree.insert(parent, 'end', text = 'Velocity: ',
values = [Combo(self)]) # <--- Combo object
...
Next the class definition of Combo follows. The way I understand it, the combobox widget inherits from and must be placed inside the Labelframe widget from ttk:
class Combo(ttk.Frame):
def __init__(self, master):
self.opts = ('opt1', 'opt2', 'etc')
self.comboFrame = ttk.Labelframe(master, text = 'Choose option')
self.comboFrame.pack()
self.combo = ttk.Combobox(comboFrame, values=self.opts, state='readonly')
self.combo.current(1)
self.combo.pack()
So is this completely wrong? I want to have the ability to change between units (eg m/s, ft/s, etc) from within the Treeview widget.
Any suggestions, plz?
The treeview widget doesn't support embedded widgets. The values for the values attribute are treated as strings.
By default, a Treeview is a static display of a forest of lists of strings. However, with work, after carefully reading Treeview references, one can make a Treeview fairly interactive. For this question, I would bind left click to an event handler that compares the mouse x,y to the bounding box (.bbox) for the units attribute cell. If in the box, display a Combobox, initialized with the current value (such as 'flops'), directly on top of the units attribute cell.
Tkinter.ttk Treeview reference and Tcl/tk treeview reference
Of course, it might be easier to put the Treeview in a frame with with a separate Combobox.

Resources