I have a really simple GUI program written in python with tkinter:
from Tkinter import *
from ttk import Combobox
class TestFrame(Frame):
def __init__(self, root, vehicles):
dropdownVals = ['test', 'reallylongstring', 'etc']
listVals = ['yeaaaah', 'mhm mhm', 'untz untz untz', 'test']
self.dropdown = Combobox(root, values=dropdownVals)
self.dropdown.pack()
listboxPanel = Frame(root)
self.listbox = Listbox(listboxPanel, selectmode=MULTIPLE)
self.listbox.grid(row=0, column=0)
for item in listVals:
self.listbox.insert(END, item) # Add params to list
self.scrollbar = Scrollbar(listboxPanel, orient=VERTICAL)
self.listbox.config(yscrollcommand=self.scrollbar.set) # Connect list to scrollbar
self.scrollbar.config(command=self.listbox.yview) # Connect scrollbar to list
self.scrollbar.grid(row=0, column=1, sticky=N+S)
listboxPanel.pack()
self.b = Button(root, text='Show selected list values', command=self.print_scroll_selected)
self.b.pack()
root.update()
def print_scroll_selected(self):
listboxSel = map(int, self.listbox.curselection()) # Get selections in listbox
print '=========='
for sel in listboxSel:
print self.listbox.get(sel)
print '=========='
print ''
# Create GUI
root = Tk()
win = TestFrame(root, None)
root.mainloop();
The GUI looks like this:
I click in a few items in the ListBox and hit the button.
The output I get is as expected:
==========
untz untz untz
==========
==========
yeaaaah
untz untz untz
==========
I now choose a value from the ComboBox and suddenly the selection in the ListBox is gone. Hitting the button shows that no value is selected in the ListBox:
==========
==========
My question is: why does the selection of a ComboBox item clear the selection of the ListBox? They are in no way related so this bug really puzzles me!
I finally found the answer. I will leave this question here in case someone else finds it.
The problem was not in the ComboBox but in the ListBox. This becomes clear if I use two (unrelated) ListBoxes. With two ListBoxes the selection will clear when the focus is changed.
From this question and its accepted answer I found that adding exportselection=0 to a ListBox disables the X selection mechanism where the selection is exported.
From effbot listbox about X selection mechanism: selects something in one listbox, and then selects something in another, the original selection disappears.
Related
I created a menu with checkbutton in my tkinter program but i wanted to select only one menu at one time below is code
def menu(self):
menubar = Menu(bg="black",fg="white")
self.file_menu = Menu(menubar,tearoff=0)
self.file_menu.add_command(label="Save",command=self.Save)
self.edit_menu = Menu(menubar, tearoff=0)
self.edit_menu.add_command(label="Undo",command=self.Undo)
self.edit_menu.add_command(label="Redo",command=self.Redo)
self.edit_menu.add_separator()
self.edit_menu.add_command(label="Cut",command=self.Cut)
self.edit_menu.add_command(label="Copy",command=self.Copy)
self.edit_menu.add_command(label="Paste",command=self.Paste)
self.edit_menu.add_separator()
self.edit_menu.add_command(label="Find",command=self.Find)
self.edit_menu.add_command(label="Replace",command=self.Replace)
self.Run_menu = Menu(menubar,tearoff=0)
self.Run_menu.add_command(label="Run",command=self.Run)
Language_menu = Menu(tearoff=0)
Language_menu.add_checkbutton(label="Python")
Language_menu.add_checkbutton(label="Java")
self.Run_menu.add_cascade(label="Language ",menu=Language_menu)
menubar.add_cascade(label="File",menu=self.file_menu)
menubar.add_cascade(label="Edit",menu=self.edit_menu)
menubar.add_cascade(label="Run",menu=self.Run_menu)
self.config(menu=menubar)
i wants that if i will select python or java any one of them then other automatically got deselect
thanks in advance
If you must use check buttons but want these to function such that clicking one unchecks the other then you can do so by creating variables to track the state of the check buttons and updating these as appropriate when clicked.
Menu with check buttons: Here is an example of this:
import tkinter as tk
parent = tk.Tk()
# create variables to track the state of check buttons
check_python = tk.BooleanVar()
check_java = tk.BooleanVar()
# handle the check buttons being clicked by unchecking others
def check_python_clicked():
check_python.set(True)
check_java.set(False)
def check_java_clicked():
check_python.set(False)
check_java.set(True)
# create and display the menu
menu_bar = tk.Menu(bg="black", fg="white")
run_menu = tk.Menu(tearoff=0)
language_menu = tk.Menu(run_menu)
language_menu.add_checkbutton(label='Python', variable=check_python, command=check_python_clicked)
language_menu.add_checkbutton(label='Java', variable=check_java, command=check_java_clicked)
run_menu.add_cascade(label='Language', menu=language_menu)
menu_bar.add_cascade(label="Run", menu=run_menu)
parent.config(menu=menu_bar)
parent.mainloop()
Menu with radio buttons: However, you can use radio buttons and achieve the same functionality with less code. This would be a better approach in this case as your options are mutually exclusive:
import tkinter as tk
parent = tk.Tk()
menu_bar = tk.Menu(bg="black", fg="white")
run_menu = tk.Menu(tearoff=0)
language_menu = tk.Menu(run_menu)
language_menu.add_radiobutton(label='Python')
language_menu.add_radiobutton(label='Java')
run_menu.add_cascade(label='Language', menu=language_menu)
menu_bar.add_cascade(label="Run", menu=run_menu)
parent.config(menu=menu_bar)
parent.mainloop()
So, I have 5 listboxes in which I need to control at the same time, almost as if they were one listbox with columns.
I am trying to find a way in which when I select an item from any one of the listboxes and delete them, it will highlight and delete the other items in the corresponding index.
so far I am only able to delete the other indexed items only when I invoke curselection() on Listbox1, but if a user selects an item on listbox2 and calls the same, it'll throw an error because the variable is looking for listbox1.
I can't seem to find any documentation or examples of how to control multiple listboxes simultaneously anywhere.
Is it possible to have a self.listbox[0, 1, 2, 3].curselection() type of thing? or even an if statement that allows me to check if self.listbox1.curselection() == True: and then execute according.
This is the function anyway:
def removeSeq(self, event=None):
index = self.listbox1.curselection()[0]
print(index)
## self.listbox1.selection_set(1)
## selectedItem = self.listbox2.curselection()
## print(selectedItem)
## self.listbox1.delete(selectedItem)
## self.listbox2.delete(selectedItem)
## self.listbox3.delete(selectedItem)
## self.listbox4.delete(selectedItem)
## self.listbox5.delete(selectedItem)
pass
I've commented most of it out for test purposes, any help would be massively appreciated.
In your binding you can use event.widget to know which widget was clicked on. Then it's just a matter of getting the selection from that widget and applying it to the other listboxes.
Here's a simple example. To delete a row, double-click in any listbox:
import tkinter as tk
class MultiListbox(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
for i in range(5):
lb = tk.Listbox(self, height=10, exportselection=False)
lb.pack(side="left", fill="y")
for j in range(10):
lb.insert("end", f"Listbox {i+1} value {j+1}")
lb.bind("<Double-1>", self.removeSeq)
def removeSeq(self, event):
lb = event.widget
curselection = lb.curselection()
index = curselection[0] if curselection else None
for listbox in self.winfo_children():
listbox.delete(index)
root = tk.Tk()
mlb = MultiListbox(root)
mlb.pack(side="top", fill="both", expand=True)
root.mainloop()
from tkinter import *
from tkinter.ttk import *
root = Tk()
listbox = None
listboxMultiple = None
listboxStr = None
listboxMultipleStr = None
def main():
global root
global listboxStr
global listboxMultipleStr
global listbox
global listboxMultiple
root.protocol("WM_DELETE_WINDOW", exitApplication)
root.title("Title Name")
root.option_add('*tearOff', False) # don't allow tear-off menus
root.geometry('1600x300')
listboxStr = StringVar()
listboxStr.set("ABCD")
listbox = Listbox(root, name="lb1", listvariable=listboxStr, width=120)
listbox.pack(side=LEFT)
listbox.bind("<<ListboxSelect>>", selectListItemCallback)
listboxMultipleStr = StringVar()
listboxMultipleStr.set("")
listboxMultiple = Listbox(root, name="lb2", listvariable=listboxMultipleStr, width=120)
listboxMultiple.pack(side=LEFT)
root.mainloop()
def selectListItemCallback(event):
global listboxMultipleStr
global listbox
global listboxMultiple
print("event.widget is {} and listbox is {} and listboxMultiple is {}\n".format(event.widget, listbox, listboxMultiple))
selection = event.widget.curselection()
listboxMultipleStr.set("")
if selection:
index = selection[0]
data = event.widget.get(index)
newvalue = "{}\n{}".format(data,"SOMETHING")
print("selected \"{}\"\n".format( data ))
print("newvalue is \"{}\"\n".format( newvalue ))
listboxMultiple.insert(END, "{}".format(data))
listboxMultiple.insert(END, "SOMETHING")
#listboxMultipleStr.set( newvalue )
else:
pass
def exitApplication():
global root
root.destroy()
if __name__ == "__main__":
main()
Using python3 on Windows 7. I've setup a callback "selectListItemCallback" for one of my two listbox widgets. And yet, when I click on the text in "lb1" it works as expected, I update "lb2" with the same selected text plus I add another line to "lb2".
The issue is, when I then select the item in "lb2", it still calls the callback and the event.widget is "lb1" and not "lb2".
My intent is to have a list of items in 'lb1' and when I select any of them, then 'lb2' gets filled with info related to the selected 'lb1' item. And, I don't want a selection callback invoked in my 'lb2' widget.
Can you see what I'm doing wrong that could be causing this strange behavior?
Thank you.
I've posted the code; and this does run on my Windows 7 machine using python3. Python 3.8.6 to be exact.
It is because the event fires when the first list box loses the selection when you click on the other list box. The event fires whenever the selection changes, not just when it is set.
If you don't want the first listbox to lose its selection when you click in the second listbox, set exportselection to False for the listboxes. Otherwise, tkinter will only allow one to have a selection at a time.
I have this few lines of code which print the selected content in the Listbox when double clicked but i want to display text beside the cursor like double click the selected content to print to prompt the user to take such action before it can be printed.
I search the listbox documentation and found the is an attribute cursor which you set the cursor type to display when the widget has focus but didn't find something like cursor-text to do that.Is the a way i can work around to achieve that, your suggestions are welcomed.
from tkinter import *
def test(event=None):
print("woow test works")
print(l.get(ACTIVE))
root = Tk()
l = Listbox(root, cursor="tcross")
l.pack()
l.insert(END, ("today"),("tomorrow"))
l.bind("<Double-Button-1>", test)
root.mainloop()
I don't quite understand your English, but you can see this page for an example of using tooltips, which make text appear on mouse hover.
Yes, it's possible to add specific info or comments to listbox lines using Hovertip from idlelib.tooltip. In the example below, Mark Lutz's customizable scrolled listbox works normally, except that right-clicking on any line opens a tip with info/comments for that line.
Ref. : Programming Python 4th ed. Example 9-9
from tkinter import *
from idlelib.tooltip import Hovertip
class ScrolledList(Frame):
def __init__(self, options, parent=None):
Frame.__init__(self, parent)
self.pack(expand=YES, fill=BOTH)
botfrm = Frame(parent)
botfrm.pack(side=BOTTOM)
Label(botfrm,
text="Select a line and right-click for info").pack()
self.makeWidgets(options)
self.myTip = None
def handleList(self, event):
if self.myTip:
self.myTip.__del__()
label = self.lstbx.get(ACTIVE)
self.runCommand(label)
def overflyLine(self, event):
if self.myTip:
self.myTip.__del__()
self.myTip = Hovertip(self.lstbx,f"Comments for {self.lstbx.get(ACTIVE)}")
self.myTip.showtip()
def makeWidgets(self, options):
sbar = Scrollbar(self)
list = Listbox(self, relief=SUNKEN, bg='misty rose')
sbar.config(command=list.yview)
list.config(yscrollcommand=sbar.set)
sbar.pack(side=RIGHT, fill=Y)
list.pack(side=LEFT, expand=YES, fill=BOTH)
for label in options:
list.insert(END, label)
self.lstbx = list
list.bind('<Button-3>', self.overflyLine)
list.bind('<Double-1>', self.handleList)
list.bind('<Return>', self.handleList)
def runCommand(self, selection):
print('You selected:', selection)
if __name__ == '__main__':
options = (('Lumberjack-%s' % x) for x in range(20))
scr = ScrolledList(options).mainloop()
I'm trying to show a context menu when an item in a Listbox widget is right clicked.
The problem is that if a bind to the listbox, the whole Listbox will be active for send the event and it doesn't seem possible to bind to the list items only. I can't use <<ListboxSelect>> because it will be trigged on left click. So I tried to use the methods curselection() but I fell into unwanted results (the right clicked item doesn't have to be selected). I think I need to simulate <<ListboxSelect>> using generate_event() and nearest(). Can someone tell me how to do that or maybe where can i found the defaults binding inside tkinter package ?
You will need to use nearest(event.y). Bind to right-click and popup the menu when the callback is invoked.
import Tkinter
def context_menu(event, menu):
widget = event.widget
index = widget.nearest(event.y)
_, yoffset, _, height = widget.bbox(index)
if event.y > height + yoffset + 5: # XXX 5 is a niceness factor :)
# Outside of widget.
return
item = widget.get(index)
print "Do something with", index, item
menu.post(event.x_root, event.y_root)
root = Tkinter.Tk()
aqua = root.tk.call('tk', 'windowingsystem') == 'aqua'
menu = Tkinter.Menu()
menu.add_command(label=u'hi')
listbox = Tkinter.Listbox()
listbox.insert(0, *range(1, 10, 2))
listbox.bind('<2>' if aqua else '<3>', lambda e: context_menu(e, menu))
listbox.pack()
root.mainloop()