Getting text from a specified tkinter label on mouse click - python

books = []
result = db.answer_query(query)
for item in result:
book = Label(search_page,text=item,font="none 12 bold",pady=2)
book.grid(row=row_count, column=0, sticky = W)
books.append(book)
row_count += 1
for book in books:
book.bind("<Button-1>", lambda e: BookInfoPage(book.cget("text")))
book.bind("<Enter>", lambda event, h=book: h.configure(fg="#638cbb"))
book.bind("<Leave>", lambda event, h=book: h.configure(fg="#000000"))
This is the current code which loads multiple labels and places them on successive rows of each index in a list returned from db.answer_query(query). This function returns a list of string values.
The current issue that I have is that for the "<Button-1>" event, I pass the parameter book.cget("text") into the function BookInfoPage() which is a function that provides information about the clicked book name (provided from the labels). I want to pass text from a specified tkinter label on mouse click into the function BookInfoPage() but currently it passes the text from the last label only, no matter which label is clicked.
This is because book variable is recognised as the last entity in the list from the for loop. Is there any way around this issue?
I want to be able to pass text from a specified tkinter label on mouse click into the function BookInfoPage().
Here is a video (gif) of the current working application: https://imgur.com/a/9sjynxm
See that even though I click on "harry potter" it gives me the result for "the great gasby", the last entry in the list.

The event object passed to the callback can tell you which widget you clicked on. Instead of throwing away the event object, use it:
book.bind("<Button-1>", lambda event: BookInfoPage(event.widget.cget("text")))
An arguably better solution is to call a proper function, and use the event object to get the widget that was clicked on. Using a function is easier to write, easier to read, and easier to debug.
def show_book(event):
book = event.widget
BookInfoPage(book.cget("text"))
book.bind("<Button-1>", show_book)

Related

Tkinter optionmenu won't allow me to pass the frame of the object I want to update depending on the choice made

I have a list of frames that each have an optionmenu with the same list of choices. When a choice is made in that specific optionmenu, I've only been able to get the last entry widget to change, not the corresponding one. In other widgets I've been able to use something like "lambda F=F:function(args)" but that isn't working here.
I've tried a trace on the variable in the option menu, I've tried wrapper functions, I've tried every combination of a lambda in the command section of the optionmenu widget. Most approaches create errors, some, like the one attached, modify the bottom frame/entry but not the correct corresponding one.
This doesn't seem like it should be too hard. If the option for the top frame selected is "Continuous" or "Discrete", the entry next to it should be 'normal' state with "?..?" in the box, if it is categorical, it should change to be 'disabled' with no contents. I could do this easily if I could somehow pass the Frame dictionary key to the "updateOnChange" function, but I can't, it only allows a single argument to be passed and that is the string value of mType[F].
from tkinter import *
def updateOnChange(type):
print(type)
if type.upper()=='CATEGORICAL':
rangeEntry[F].delete(0,END)
rangeEntry[F].config(state='disabled')
print("runCat")
else:
rangeEntry[F].config(state='normal')
rangeEntry[F].delete(0,END)
rangeEntry[F].insert(0,'?..?')
print("runCont")
mType={}
frame={}
om={}
rangeEntry={}
root=Tk()
Frames=['FrameOne','FrameTwo']
miningTypes=['Continuous','Categorical','Discrete']
for F in Frames:
mType[F]=StringVar(root)
if F=='FrameOne':
mType[F].set("Continuous")
else:
mType[F].set("Categorical")
frame[F]=Frame(root,borderwidth=3,relief=SUNKEN)
frame[F].pack(side=TOP,fill=X)
rangeEntry[F]=Entry(frame[F],width=20,font=("Arial",12))
om[F]=OptionMenu(frame[F],mType[F],*miningTypes,command=updateOnChange)
om[F].pack(side=LEFT)
rangeEntry[F].pack(side=LEFT)
mainloop()
``
Your updateOnChange function hard-coded the entry to be changed as rangeEntry[F], which points to the last Entry widget created in your for loop. To properly associate each entry, you should pass the widget as a parameter:
def updateOnChange(type, entry):
if type.upper()=='CATEGORICAL':
entry.delete(0,END)
entry.config(state='disabled')
print("runCat")
else:
entry.config(state='normal')
entry.delete(0,END)
entry.insert(0,'?..?')
print("runCont")
And then pass the parameter in your command:
om[F]= OptionMenu(frame[F],mType[F],*miningTypes,command=lambda e, i=rangeEntry[F]: updateOnChange(e, i))

Tkinter listbox change highlighted item programmatically

I have a listbox in Tkinter and I would like to change the item selected programatically when the user presses a key button. I have the keyPressed method but how do I change the selection in the Listbox in my key pressed method?
Because listboxes allow for single vs. continuous vs. distinct selection, and also allow for an active element, this question is ambiguous. The docs explain all the different things you can do.
The selection_set method adds an item to the current selection. This may or may not unselect other items, depending on your selection mode.
If you want to guarantee that you always get just that one item selected no matter what, you can clear the selection with selection_clear(0, END), then selection_set that one item.
If you want to also make the selected item active, also call activate on the item after setting it.
To understand about different selection modes, and how active and selected interact, read the docs.
If you need ListboxSelect event to be also triggered, use below code:
# create
self.lst = tk.Listbox(container)
# place
self.lst.pack()
# set event handler
self.lst_emails.bind('<<ListboxSelect>>', self.on_lst_select)
# select first item
self.lst.selection_set(0)
# trigger event manually
self.on_lst_select()
# event handler
def on_lst_select(self, e = None):
# Note here that Tkinter passes an event object to handler
if len(self.lst.curselection()) == 0:
return
index = int(self.lst.curselection()[0])
value = self.lst.get(index)
print (f'new item selected: {(index, value)}')

More on passing arguments to tkinter button command

I have a number of test files in a single directory. I'm trying to write a GUI to allow me to select and run one of them.
So, I have a loop that scans the directory and creates buttons:
for fnm in glob.glob ('Run*.py'):
tstName = fnm[3:-3] # Discard fixed part of filename
btn = Button (self, text=tstName,
command=lambda: self.test(tstName))
btn.grid (row=rowNum, column=0, pady=2)
rowNum += 1
This creates my GUI correctly, with buttons labelled say, A and B but when I press on the button labelled A it passes B to the test method.
I've looked around and found this question How can I pass arguments to Tkinter button's callback command? but the answer doesn't go on to use the same variable name, with a different value, to configure another widget. (In fact, by tying the variable name to the widget name it almost implies that the technique won't work in this case, as I've found.)
I'm very new to Python, but am quite familiar with creating this kind of GUI using Tcl/TK and I recognise this problem - the value of tstName is being passed when I press the button, but I want it to pass the value the variable had when I created it. I know how I'd fix that in Tcl/Tk - I'd define a command string using [list] at creation time which would capture the value of the variable.
How do I do the same in Python?
You need to bind the current value of tstName at the time you define the button. The way you're doing it, the value of tstName will whatever it is at the time you press the button.
To bind the value at the time that you create the button, use the value of tstName as the default value of a keyword parameter to the lambda, like so:
btn = Button(..., command=lambda t=tstName: self.test(t))

Python Tkinter: Changing other button's text from event handler of one button

I have the following problem when using tkinter to create a very simple window containing a matrix of buttons: When one of the buttons is clicked, the event handler changes the text of that button using the configure method on the button widget. This works. But I also want to change the text in one of the other buttons, which does not work. The method I use is that on creating the button, I store the object returned by the Button method before I use the grid geometry manager to place it. This object looks like ".123456789L" when printed and seems to be a pointer to the widget. I also use configure on this to change the button text. But somehow it seems to be wrong, because it works sometimes, and most of the times not. There's unfortunately no error message, just nothing happens when calling configure. I checked and it seems to be the correct pointer to the widget. Do I have to use a special way to affect a widget other that the one that called the event handler? These are the relevant parts of the code:
# CREATING THE BUTTONS:
buttons={} # global
for i in range(3):
for j in range(3):
button = Tkinter.Button(self,text='foo')
buttons[button]=(i,j)
button.grid(column=j,row=i)
button.bind( "<Button-1>", self.OnButtonClick )
# CHANGING BUTTONS:
def find_button(i,j):
"""Return the pointer to the other button to be changed when a button has been clicked."""
for button,key in buttons.items():
if key==(i,j): return button
def OnButtonClick(self,event):
print "You clicked the button",buttons[event.widget]
i,j=buttons[event.widget]
old_button=find_button(i,j) # This is simplified, I don't actually pass i,j, but other values. But I checked this and it returns the reference to the correct button. But this simplified version works the same way, just assume a different button that the one pressed would be returned.
old_button.configure(text = 'blabla') # THIS DOES NOT WORK
event.widget.configure(text = 'something') # THIS WORKS
I have the same problem and i solve it with:
buttons[button]=(i,j,button)
and in the function OnButtonClicK:
i,j,old_button=buttons[event.widget]
old_button.configure(text = 'blabla') # THIS DOES NOT WORK

getting the label value of a button in Gtk

Am learning Gtk. I wanted to build a calculator, in which i want to display the number pressed , in the textbox. I have completed it, by calling different functions for different buttons clicked, and appending the value in the textbox with the value of the button pressed. Using python 2.7.3
Is there a way to obtain the label value of the button pressed so that i can use a single function instead of 10 functions from 0 to 9?
Thanks in advance
Button callbacks include the widget itself, and you can also pass data. See here.
instead of reading the label of the GtkButton, which is pretty much error prone, you should associate the value represented by the button to the button instance itself, e.g.:
button = Gtk.Button(label='1')
button._value = 1
# add button to the container
button.connect('clicked', on_button_clicked)
button = Gtk.Button(label='2')
button._value = 2
# add button to the container
button.connect('clicked', on_button_clicked)
and then read the value from the button instance inside the signal handler, e.g.:
def on_button_clicked(button):
print 'you pressed the button of value: %d' % (button._value)
GtkWidget instances in Python are Python object, and thus behave like any other Python object.

Categories

Resources