If I put a radio button in a function and draw them; the first time they are drawn you cannot hover over them without making them look like they are all selected.
The same code out of a function does not exhibit this behaviour.
from Tkinter import *
def App(master):
v = StringVar()
v.set('python') # initialize
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth = 3)
runtimeFrame.pack(fill = X, pady = 5, padx = 5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=v, value=mode, indicatoron = 1 )
b.pack(side = LEFT)
if __name__ == '__main__':
master = Tk()
App(master)
#The following code chunk is the same as that in App()
#------------------------
v = StringVar()
v.set('python') # initialize
lable1 = Label(master, text=' hovering over below radio buttons will cause them to Not look selected as expected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth = 3)
runtimeFrame.pack(fill = X, pady = 5, padx = 5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=v, value=mode, indicatoron = 1 )
b.pack(side = LEFT)
#------------------------
mainloop()
Once you have made a selection this does not happen again.
Am I doing something wrong? Is there a workaround, because my code has to be in a function!
This is the second elementary bug I have found in Tkinter. Is there something better for Python GUI development?
ps: I'm using python 2.7
The place where you store the variable object (StringVar, v, in your case) must persist so that this odd behavior wont show up. My guess is we're seeing this behavior because v, goes out of scope, something is going wrong. Aside from using a global, I can't think of a way to do this from a function.
Broken code:
from Tkinter import *
def App(master):
v = StringVar()
v.set('python')
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth=3)
runtimeFrame.pack(fill=X, pady=5, padx=5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=v, value=mode, indicatoron=1)
b.pack(side=LEFT)
if __name__ == '__main__':
master = Tk()
App(master)
mainloop()
Example Fix:
from Tkinter import *
def App(master, radio_var):
radio_var.set('python')
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth=3)
runtimeFrame.pack(fill=X, pady=5, padx=5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=radio_var, value=mode, indicatoron=1)
b.pack(side=LEFT)
if __name__ == '__main__':
master = Tk()
radio_var = StringVar()
App(master, radio_var)
mainloop()
Consider that if you have more than one variable that needs to persist you can pass in a list or dictionary of variables.
Also, just in case "has to be in a function" is a homework assignment requirement, consider wrapping the code in a class. I'm not a tk expert, but that would seem the preferred manner of organizing your code.
Example fix 2:
from Tkinter import *
class App(object):
def __init__(self, master):
self.radio_var = StringVar()
self.radio_var.set('python')
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth=3)
runtimeFrame.pack(fill=X, pady=5, padx=5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=self.radio_var, value=mode, indicatoron=1)
b.pack(side=LEFT)
if __name__ == '__main__':
master = Tk()
app = App(master)
mainloop()
Notice a minor change in
app = App(master)
This is required so that the App instance is not garbage collected prematurely. This would effectively pull the self.radio_var out of scope and we're back at square one.
Try this, it works for me.
v = 0 # this is global variable
def some_function():
global v
v = IntVar()
v.set(0)
rb1 = Radiobutton (parent, variable = v, value = 0)
rb1.pack()
rb2 = Radiobutton (parent, variable = v, value = 1)
rb2.pack()
Make your radio buttons and then you get your radio buttons as they should.
I know that passed a lot of time, but I've tried all the strategies showed here and none of them work with me. What worked for me was simple "rewrite" the event handler for mouse motion event. It's not a perfect solution because I print some kind of garbage to the terminal, but well, this is not a problem in my particular case.
server_name = IntVar()
server_name.set(1)
server_name_rb_1 = Radiobutton(container_3, text="Server", variable=server_name, value=1)
server_name_rb_1.select()
server_name_rb_1.pack()
server_name_rb_2 = Radiobutton(container_3, text="Local", variable=server_name, value=2)
server_name_rb_2.deselect()
server_name_rb_2.pack()
server_name_rb_2.bind('<Motion>',lambda e: print(str(server_name.get())) )
P.S.: You don't need to rewrite all functions just rewrite one of them must be enough.
i was able to fix it by simply adding state=NORMAL, to all the radio buttons. i appreciate that this is the default behavior but it looks like that tkinter kind of doesn't remember this.
I found a solution that it actually works fine so maybe it can be helpful for other desperate people like me.
First thing, I created separate radioButtons so that I can rapidly access to the variable names; finally, I binded the mouse "Leave" event, of the unselected widget, to "break" (doing this I undid the normal behaviour of the widget aka when the mouse is over the radio button now it does nothing).
As to now, I'm not having any problems and it works just fine.
Below, an example code:
...
self.__var__ = IntVar()
self.__var__.set(2)
unselected_btn = Radiobutton(self.__frame__,
text="One", padx=20,
variable=self.__var__,
value=1
)
unselected_btn.grid(row=0, column=1, sticky=E)
selected_btn = Radiobutton(self.__frame__,
text="Two",
padx=20,
variable=self.__var__,
value=2
)
selected_btn.grid(row=0, column=0, sticky=W)
unselected_btn.bind("<Leave>", lambda e: "break")
...
NOTE: here I used grid, but you can simply change it to pack or place
I will admit you found quite an odd "glitch". I'm not entirely prepared to call it a glitch seeing as I'm not sure what is causing it. It seemed to be stemming from the variable, argument. A work around would be instead of getting the variable, get the actual text of the widget via the .cget('text') method.
My changes to your App function:
def App(master):
v = StringVar()
v.set('python') # initialize
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth = 3)
runtimeFrame.pack(fill = X, pady = 5, padx = 5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, value=mode, indicatoron = 1 ) # I omitted the variable argument, which seemed to be the root of your troubles.
b.pack(side = LEFT)
b.deselect() # manually deselects each instance of the Radiobutton.
b.select() # manually selects a default instance of the Radiobutton.
Related
enter code hereI am rendering an array of buttons onto the screen and want to implement a right-click function. I have left click working with the default "command=" option on the widget, but for some reason, I can't seem to get the button bind to kick in. My code looks like this:
for key, value in sorted_widget_properties:
if key not in self._filter_list:
continue
colour = value[appearance_mode_index]
if row > 18:
offset = 4
row = 1
# Light mode colours
if row == 1:
pad_y = (10, 0)
else:
pad_y = 5
lbl_property = ctk.CTkLabel(master=widget_frame, text=' ' + key)
lbl_property.grid(row=row, column=1 + offset, sticky='w', pady=pad_y)
btn_property = ctk.CTkButton(master=widget_frame,
border_width=1,
fg_color=colour,
width=button_width,
height=button_height,
text='',
command=lambda widget_property=key: self.colour_picker(widget_property),
corner_radius=3)
btn_property.grid(row=row, column=0 + offset, padx=5, pady=pad_y)
self.widgets[key] = {"widget": btn_property, "button": btn_property, "colour": colour,
'label': lbl_property}
# Set a binding so that we can paste a colour, previously copied into our clipboard
self.widgets[key]['widget'].bind("<Button-3>",
lambda widget_property=key: self._paste_colour(widget_property))
row += 1
I have a print statement in the _paste_colour class method, and it appears that the function is never called and nothing is ever printed:
def _paste_colour(self, widget_property):
print('PASTE COLOUR!"')
new_colour = pyperclip.paste()
if len(new_colour) != 7:
self._status_bar.set_status_text(status_text='Attempt to paste a bad colour code - ignored.')
self._set_widget_colour(widget_property=widget_property, new_colour=new_colour)
self._status_bar.set_status_text(
status_text=f'Colour {new_colour} assigned to widget property {widget_property}.')
Any suggestions appreciated.
Thanks,
Clive
Next time please provide a minimal reproducible example. You have a lot of variables that are not defined in the code you show and the tkinter class is not provided either. Also we do not know if you get any error messages. This makes it difficult to troubleshoot. It might be that you are just missing an event parameter, but not sure if I get your issue correctly.
import tkinter as tk
root = tk.Tk()
root.geometry("200x200")
widget_frame = tk.Frame(root).grid(row=1, column=1)
def right_click(x):
print('right clicked')
print(x)
def left_click():
print('left clicked')
lbl_property = tk.Label(master=widget_frame, text='Label')
lbl_property.grid(row=0, column=0, sticky='w')
btn_property = tk.Button(master=widget_frame,
text='button',
command=left_click
)
btn_property.grid(row=0, column=1, padx=5, pady=5)
param='some parameter'
btn_property.bind("<Button-3>", lambda event, x=param: right_click(x))
root.mainloop()
OK, apologies, I really should have done a bit more prep.
Anyway, taking the above example and replacing with a customtkinter widget. I have reproduced the problem:
import tkinter as tk
import customtkinter as ctk
root = tk.Tk()
root.geometry("200x200")
widget_frame = tk.Frame(root).grid(row=1, column=1)
def right_click(x):
print('right clicked')
print(x)
def left_click():
print('left clicked')
lbl_property = tk.Label(master=widget_frame, text='Label')
lbl_property.grid(row=0, column=0, sticky='w')
btn_property = ctk.CTkButton(master=widget_frame,
text='button',
command=left_click
)
btn_property.grid(row=0, column=1, padx=5, pady=5)
param='some parameter'
btn_property.bind("<Button-3>", lambda event, x=param: right_click(x))
root.mainloop()
Running the above, masks out the right click binding. No errors, just doesn't work.
I'll raise the issue on GitHub on the customtkinter forum.
I'm using Tkinter for the GUI for my project (if you type ingredients you want into a textbox, it will return recipes using API.. and so on). I'm trying to save the user input into a variable so that I can use it later. However, the get() function seems like not catching anything. I've read a different post but not sure what I'm doing it wrong. Here is my code:
import tkinter as tk
import tkinter.font as font
# globally declare the expression variable
expression = ""
def getSentence():
global expression
# clear the entry fields
deleteEntryFields()
# ask a question
field2.insert(0, 'What do you want to eat? You can express and we will find something for you!')
expression = v.get()
return expression
def getIngredients():
pass
def searchWithSentence(sentence):
pass
def searchIngredients(ingredients):
pass
################################################################################
# This is where I'm testing if the user input is saved to a variable expression.
def enter():
field1.insert(0, str(expression))
print(expression)
################################################################################
def clear():
global expression
expression = ""
def deleteEntryFields():
field1.delete(0, 'end')
field2.delete(0, 'end')
# Driver code
if __name__ == "__main__":
# create a GUI window
master = tk.Tk()
v = tk.StringVar()
field1 = tk.Entry(master, textvariable=v)
field2 = tk.Entry(master)
field1.insert(0, 'Please type here')
field2.insert(0, 'Results will be shown here.')
field1.place(x=20, y=20, width=730, height=50)
field2.place(x=20, y=80, width=730, height=500)
# set the background colour of GUI window
master.configure(background="lemon chiffon")
# set the title of GUI window
master.title("Recipe Finder")
# set the configuration of GUI window
master.geometry("1050x600")
# set font
myFont = font.Font(family='Verdana', size=9, weight='bold')
# Buttons
button1 = tk.Button(master, text=' What do you feel like eating? ', fg='black', bg='salmon',
command=getSentence, height=5, width=30)
button1.place(x=770, y=100)
button1['font'] = myFont
button2 = tk.Button(master, text=' Type the ingredients you have! ', fg='black', bg='orange',
command=getIngredients, height=5, width=30)
button2.place(x=770, y=200)
button2['font'] = myFont
Clear = tk.Button(master, text=' CLEAR ', fg='black', bg='white',
command=clear, height=5, width=30)
Clear.place(x=770, y=300)
Clear['font'] = myFont
Enter = tk.Button(master, text=' ENTER ', fg='black', bg='white',
command=enter, height=5, width=30)
Enter.place(x=770, y=400)
Enter['font'] = myFont
# start the GUI
master.mainloop()
Previously I was not using StringVar(), but when I did some research, it says it can be one of the ways to get the get() function worked. But nothing happened... How can I effectively get user input and save it to global variable?
Any advice will be appreciated!
You are using the Entry itself to contain an explanation what to type?
To make that work you could bind a callback that clears the entry to the <FocusIn> event.
This will make sure that the entry field is empty when the user wants to type something.
And to be consistent, you would have to restore that text on a <FocusOut> event in the case that the user didn't fill in anything.
While this is used in places where screen space is extremely limited, it doesn't feel like good UI design.
A more common method is to put a Label with a description before the Entry.
I want a neat and clean UI for my program it's working well but UI is terribly bad. Can anyone help me to fix this?
I am trying to use grid and place, but due to lack of knowledge of python. I am unable to do it.
#wap to demonstrate use of check button
import tkinter
from tkinter import messagebox
def pd():
if (var1.get())==1:
l1.config(text="You Have Selected Pendrive")
elif (var1.get())==0:
l1.config(text="")
def mcard():
if (var2.get())==1:
l2.config(text="You Have Selected Memory card")
elif (var2.get())==0:
l2.config(text="")
def hdd():
if (var3.get())==1:
l3.config(text="You Have Selected HDD")
elif (var3.get())==0:
l3.config(text="")
def per():
print("Successfully compiled")
msgbox=tkinter.messagebox.askquestion("Closing program","Are you sure?",)
if msgbox=="yes":
win.destroy()
else:
tkinter.messagebox.showinfo('Return','You will now return to the application screen')
win=tkinter.Tk()
var1=tkinter.IntVar()
var2=tkinter.IntVar()
var3=tkinter.IntVar()
win.geometry("500x500+0+0")
cb1=tkinter.Checkbutton(text="Pendrive",height=2,width=15,variable=var1,font=5,cursor="dot",bg="grey",anchor="w",command=pd)
cb2=tkinter.Checkbutton(text="Memory Card",height=2,width=15,variable=var2,font=5,cursor="dot",bg="grey",anchor="w",command=mcard)
cb3=tkinter.Checkbutton(text="HDD",height=2,width=15,variable=var3,font=5,cursor="dot",bg="grey",anchor="w",command=hdd)
b1=tkinter.Button(text="Submit",height=2,width=15,command=per)
# from this line problem begins with UI.
l1=tkinter.Label(height=2,width=30)
l2=tkinter.Label(height=2,width=30)
l3=tkinter.Label(height=2,width=30)
cb1.grid(column=0,row=1)
cb2.grid(column=0,row=2)
cb3.grid(column=0,row=3)
l1.place(x=40,y=300)
l2.place(x=40,y=350)
l3.place(x=40,y=400)
b1.place(x=125,y=450)
win.mainloop()
Actual results are different than I expected it to be. the widgets are not aligned well(I want them to centered)and there isn't enough padding between them( there should be enough space so that it won't look messy ).
If you want better aligned then rather don't use width, height, place() but pack() and grid() and its options. If you use wrong option in pack(), grid() then you will see error message with all available options for pack or grid. pack and grid use different options.
More in documentation: place(), pack(), grid()
import tkinter as tk
from tkinter import messagebox
def pd():
if var1.get():
l1.config(text="You Have Selected Pendrive")
else:
l1.config(text="")
def mcard():
if var2.get():
l2.config(text="You Have Selected Memory card")
else:
l2.config(text="")
def hdd():
if var3.get():
l3.config(text="You Have Selected HDD")
else:
l3.config(text="")
def per():
print("Successfully compiled")
msgbox = messagebox.askquestion("Closing program","Are you sure?",)
if msgbox == "yes":
win.destroy()
else:
messagebox.showinfo('Return', 'You will now return to the application screen')
win = tk.Tk()
win.geometry("500x500+0+0")
var1 = tk.IntVar()
var2 = tk.IntVar()
var3 = tk.IntVar()
cb1 = tk.Checkbutton(win, text="Pendrive", variable=var1, font=5, cursor="dot", bg="grey", anchor="w", command=pd)
cb2 = tk.Checkbutton(win, text="Memory Card", variable=var2, font=5, cursor="dot",bg="grey", anchor="w", command=mcard)
cb3 = tk.Checkbutton(win, text="HDD", variable=var3, font=5, cursor="dot", bg="grey", anchor="w", command=hdd)
cb1.pack(ipadx=10, ipady=10, fill='both', expand=True)
cb2.pack(ipadx=10, ipady=10, fill='both', expand=True)
cb3.pack(ipadx=10, ipady=10, fill='both', expand=True)
l1 = tk.Label(win, background='#bbbbbb')
l2 = tk.Label(win, background='#cccccc')
l3 = tk.Label(win, background='#bbbbbb')
l1.pack(ipadx=10, ipady=10, fill='both', expand=True)
l2.pack(ipadx=10, ipady=10, fill='both', expand=True)
l3.pack(ipadx=10, ipady=10, fill='both', expand=True)
b1 = tk.Button(win, text="Submit", command=per)
b1.pack(side='bottom', ipadx=10, ipady=10, fill='both', expand=True)
win.mainloop()
I've been attempting to make a simple GUI, and have been working my way through tkinter's various functions. However, I can't for the life of me figure out why this doesn't work.
from tkinter import Tk, Label, Button, Radiobutton,IntVar,StringVar
class TestGUI:
def __init__(self, master):
self.master = master
master.title("Test GUI")
self.mode = IntVar()
self.modetext = StringVar()
self.modetext.set("What does this do?")
self.modelabel = Label(master,textvariable=self.modetext)
self.modelabel.pack()
self.close_button = Button(master, text="Close", command=master.destroy)
self.close_button.pack()
R1 = Radiobutton(master, text="Mode 1", variable=self.mode, value=0, command=self.modeset)
R1.pack()
R2 = Radiobutton(master, text="Mode 2", variable=self.mode, value=1, command=self.modeset)
R2.pack()
R3 = Radiobutton(master, text="Mode 3", variable=self.mode, value=2, command=self.modeset)
R3.pack()
def modeset(self):
self.modetext.set("Mode is " + str(self.mode.get()))
print(self.mode.get())
root = Tk()
T_GUI = TestGUI(root)
root.mainloop()
What it should do, as far as I can tell, is display three radio buttons which set the value of mode, and display "Mode is [mode]" in the label and print the value of mode when one is selected.
Instead, the label is never displayed and choosing a radio button doesn't change the value of mode.
Can anyone give me a clue on what is happening?
Looking at your explanation, StringVar and IntVar are causing problems here.
Specifying master on them should solve your issue.
self.mode = IntVar(master=self.master)
self.modetext = StringVar(master=self.master)
Something to do with Pyzo probably because on most IDEs, omitting master don't cause any problems.
I know that there are a lot of questions dealing with tkinter but I have looked at a bunch of them and none of them seem to help me.
import tkinter
class Calculator:
def __init__(self):
window = tkinter.Tk()
window.geometry("200x300")
window.title("Calculator")
lbl = tkinter.Label(window, text="placeholder", bg="blue", textvariable="labelText")
lbl.grid(row=0, column=0, columnspan=3)
self.firstNumArray = []
self.secondNumArray = []
self.operation = ""
self.currentNum = "first"
def appendNumber(self, number):
print("Appending Number")
if self.currentNum == "first":
self.firstNumArray.append(number)
print("".join(str(x) for x in self.firstNumArray))
lbl.config(text="".join(str(x) for x in self.firstNumArray))
window.update()
else:
self.secondNumArray.append(number)
for i in range(1,4):
string = "Creating button at ({0},{1})".format(0,i)
print(string)
button = tkinter.Button(text=i, command=lambda: appendNumber(self, i))
button.grid(row=1, column=i-1)
for i in range(1,4):
string = "Creating button at ({0},{1})".format(1,i)
print(string)
button = tkinter.Button(text=i+3, command=lambda: appendNumber(self, i+3))
button.grid(row=2, column=i-1)
for i in range(1,4):
string = "Creating button at ({0},{1})".format(2,i)
print(string)
button = tkinter.Button(text=i+6, command=lambda: appendNumber(self, i+6))
button.grid(row=3, column=i-1)
div = tkinter.Button(text="/")
mult = tkinter.Button(text="*")
add = tkinter.Button(text="+")
sub = tkinter.Button(text="-")
add.grid(row=1, column=3)
sub.grid(row=2, column=3)
mult.grid(row=3, column=3)
div.grid(row=4, column=3)
button = tkinter.Button(text="0")
button.grid(row=4, column=1)
window.mainloop()
calc = Calculator()
When I launch the program the window opens. When I click on a button the text in the label does not change. I have tried using a StringVar as the textvariable and then calling the set() function, but that did not work either. I think it has to do with the scope of the function. I had to place the appendNumber() function inside the __init__() because for some reason self.lbl = tkinter.Label() makes nothing pop up at all.
There are a few problems with your code.
labelText should, of course, be a StringVar and not a string...
labelText = tkinter.StringVar()
lbl = tkinter.Label(window, bg="blue", textvariable=labelText)
lbl.grid(row=0, column=0, columnspan=3)
Now you can use labelText.set to update the text. Also, no need for self parameter or window.update
def appendNumber(number):
if self.currentNum == "first":
self.firstNumArray.append(number)
labelText.set("".join(str(x) for x in self.firstNumArray))
else:
self.secondNumArray.append(number)
You can put all the buttons in one loop using // (integer (!) division) and % (modulo) operations. Also, be aware that the variable in the lambda is evaluated when the function is called, not when it is declared, i.e. all the lambdas will use the last value of i (9 in this case) -- see e.g. here. As a remedy, use lambda n=i+1: appendNumber(n).
for i in range(9):
btn = tkinter.Button(text=i+1, command=lambda n=i+1: appendNumber(n))
btn.grid(row=i//3+1, column=i%3)
Not really a problem, but since you don't need a reference to those buttons, you can make your code a bit more compact (same for the others):
tkinter.Button(text="/").grid(row=1, column=3)