As someone new to tkinter for Python 3, I am having a couple of little issues navigating how to generate a GUI (in context of a Python script) despite reading the documentation and Stack Overflow answers. My objective is to create a frame with 7 radio button choices each corresponding to a screen resolution size which when selected and the submit button is pressed, the selected radio button will pass its value to a variable. However when I implement my GUI, I get two issues.
The first is that my frame opens correctly with the radio buttons, but another frame, which is blank and is titled "tk" appears. Regardless of what I do (i.e. use root.withdraw() etc. as others have mentioned), this blank window still appears.
The second and more baffling issue I am having is that when generated, all but the first radio button is selected, not normally with a dot in the center, but with a hyphen. Now the user can press on the option he/she wants and it will all unselect except for the choice, but it doesn't look normal and would probably confuse the user. I read about setting tristatevariable to none yet that didn't work (or at least in my trial). I also tried to force a deselect() function on all of the radio buttons before they generate and that didn't work either. Also, keep in mind that the radio buttons' variable must handle a string and not an int. What is happening here and how can I fix it?
The code snippet that pertains to both of these seemingly related issues is as follows:
if urldata == None:
class ResolutionInputGUI:
def __init__(self, master):
self.master = master
master.title("My GUI")
self.label = tk.Label(master, text="Your Screen Resolution Is: " + screenres + "\n")
self.label.pack()
MODES = [
("500×500", "500×500"),
("1280×800", "1280×800"),
("1280×1024", "1280×1024"),
("1440×900", "1440×900"),
("1680×1050", "1680×1050"),
("1920×1080", "1920×1080"),
("1920×1200", "1920×1200")
]
resolution = tk.StringVar()
resolution.set("500×500")
for text, mode in MODES:
self.radiobutton = tk.Radiobutton(master, text=text, variable=resolution, value=mode)
self.radiobutton.pack(anchor=tk.W)
self.submit_button = tk.Button(master, text="Submit", command=self.submit)
self.submit_button.pack()
self.cancel_button = tk.Button(master, text="Cancel", command=self.cancelbutton)
self.cancel_button.pack()
def submit(self):
global screenres
screenres = self.radiobutton.get()
root.quit()
self.master.destroy()
print(screenres)
def cancelbutton(self):
raise SystemExit
root = tk.Tk()
my_gui = ResolutionInputGUI(root)
root.mainloop()
Any help would be greatly appreciated as I cant seem to solve this issue and tkinter seems to be much more complicated than originally thought. Also, is there anything else that I am doing inefficiently here or to make the end user experience more "friendly?" Thank you so much!
The first is that my frame opens correctly with the radio buttons, but another frame, which is blank and is titled "tk" appears
This is because you are calling Tk() twice. I see one of them near the end, and you must have another elsewhere in your code.
all but the first radio button is selected, not normally with a dot in the center, but with a hyphen.
This is because you are using a local variable. Change "resolution" to "self.resolution".
when selected and the submit button is pressed, the selected radio button will pass its value to a variable
To do this you need to return the value from the variable, not from the button.
Also, you should put the class definition at the global level.
import tkinter as tk
class ResolutionInputGUI:
def __init__(self, master):
self.master = master
master.title("My GUI")
self.label = tk.Label(master, text="Your Screen Resolution Is: " + screenres + "\n")
self.label.pack()
MODES = [
("500×500", "500×500"),
("1280×800", "1280×800"),
("1280×1024", "1280×1024"),
("1440×900", "1440×900"),
("1680×1050", "1680×1050"),
("1920×1080", "1920×1080"),
("1920×1200", "1920×1200")
]
self.resolution = tk.StringVar(master, value="500×500")
for text, mode in MODES:
self.radiobutton = tk.Radiobutton(master, text=text, variable=self.resolution, value=mode)
self.radiobutton.pack(anchor=tk.W)
self.submit_button = tk.Button(master, text="Submit", command=self.submit)
self.submit_button.pack()
self.cancel_button = tk.Button(master, text="Cancel", command=self.cancelbutton)
self.cancel_button.pack()
def submit(self):
global screenres
screenres = self.resolution.get()
root.quit()
self.master.destroy()
print(screenres)
def cancelbutton(self):
raise SystemExit
if urldata == None:
root = tk.Tk()
my_gui = ResolutionInputGUI(root)
root.mainloop()
Related
Having problems trying to populate a tk form widget
Its a simple question really, and I am a bit loathed to post it here because there must be a really simple explanation, but i cant see it right now.
I am new to python. I have a python3 program to perform some tasks on an excel file. The program uses three files. All i need is a simple interface, which allows the user to select the scriptfile (file1), procedures file (file2) and there will be an third output file. Then use these files to do the main processing.
I have copied the code from some websites, and mangled some together to come up with something (see below). However, I cant see how i am supposed to write back the code into the text widget.
for example, i would expect something in the form of
Window.t_out = "new value"
or self.t_out = "new value"
However, i cant see how to access t_out for example.
it could be i have created more confusion using a class, but i thought this was the preferred way.
Known issues:
1. I have .grid and .place and dont intend to mix the two, but just trying to see which is better
2. Im not entirely sure at this point if I am supposed to keep the button functions with the class or outside of the class, since both seem to work.
class Window(Frame):
def __init__(self, master=None):
# parameters that you want to send through the Frame class.
Frame.__init__(self, master)
# reference to the master widget, which is the tk window
self.master = master
# with that, we want to then run init_window, which doesn't yet exist
self.init_window()
# Creation of init_window
def init_window(self):
self.master.title("Select Files to process") # changing the title of our master widget
# self.pack(fill=BOTH, expand=1) # allowing the widget to take the full space of the root window
TROW = 1
SROW = 2
PROW = 4
COL = 2
COLBTN = 30
# Define Menu
menu = Menu(self.master) # creating a menu instance
self.master.config(menu=menu)
file = Menu(menu) # create the file Menu
file.add_command(label="Exit", # adds a Exit to the menu option
command=self.menu_exit) # bind client_exit to run on event
menu.add_cascade(label="File", menu=file) # bind the function file to Menu "File" Label
edit = Menu(menu) # create the Edit Menu
edit.add_command(label="Undo",
command=self.menu_undo) # adds a command Undo to Edit menu
menu.add_cascade(label="Edit", menu=edit) # bind the function edit to Menu "Edit" Label
# Define Form
# Define the Labels
Label(self.master, text="File1").grid(row=SROW)
Label(self.master, text="File2").grid(row=PROW)
# Define the Widgets
t_out = Text(self.master, height=15, width =100)
t_scr = Text(self.master, height =1, width =90)
t_proc = Text(self.master, height =1, width =90)
t4 = Text(self.master, height=10, width=20)
# Define the positioning
t_out.grid(row=TROW, column=COL)
t_scr.grid(row=SROW, column=COL, sticky = W)
t_proc.grid(row=PROW, column=COL, sticky = W)
t4.place (x=50,y=320)
# Assign the widgets to procedures
Button(self.master, text='script',
command=get_script_file).grid(row=SROW, column=COLBTN, sticky=W, pady=4)
Button(self.master, text='procedure',
command=get_procedure_file).grid(row=PROW, column=COLBTN, sticky=W, pady=4)
Button(self.master, text='getVAL',
command=self.getval).place(x=320,y=310)
def getval(self):
from tkinter import filedialog
filename = filedialog.askopenfilename(initialdir="/", title="Select file",
filetypes=(("Excel Files", "*.xlsx"), ("all files", "*.*")))
# I NEED TO WRITE BACK THE FILENAME INTO THE TEXT WIDGET HERE
def menu_exit(self):
exit()
def menu_undo(self):
print ("Undo not implemented yet")
def get_script_file():
from tkinter import filedialog
root.filename = filedialog.askopenfilename(initialdir="/", title="Select file",
filetypes=(("Excel Files", "*.xlsx"), ("all files", "*.*")))
print("got ", root.filename)
# I NEED TO WRITE BACK THE FILENAME INTO THE TEXT WIDGET HERE ALSO
def get_procedure_file():
print ("Not implemented yet")
# root window created. Here, that would be the only window, but
# you can later have windows within windows.
root = Tk()
root.geometry("1000x400")
# creation of an instance
app = Window(root)
# mainloop
root.mainloop()
You have to use self. to have access to widget in all methods in class.
self.t_out = Text(...)
To insert new text in Text you have to delete() old text and insert() new one
self.t_out.delete("0", "end")
self.t_out.insert("end", "new text")
So I got to change the colors as well as change colors while hovering over menu objects created using the OptionMenu in tkinter.
I even have text printing when I hover over the button, but the second I drop down the menu, it won't print anymore.
What am I doing wrong? How can I print when you click to open the OptionMenu and and move around through the selections?
from tkinter import *
import tkinter as tk
OZoneIzotopeSemiWhite = "#c0c4ca"
buttonBackground = "#303336"
buttonforeground = "#cdd0d7"
BACKGROUND2 = "#1e1f21"
class DropDownButton():
def __init__(self, parent, placement, opTions, **kw):
self.parent = parent
self.options = opTions
self.om_variable = tk.StringVar(self.parent)
self.om_variable.set(self.options[0])
self.om_variable.trace('w', self.option_select)
self.om = tk.OptionMenu(self.parent, self.om_variable, *self.options)
self.om["menu"].config(fg=buttonforeground, bg=buttonBackground, activebackground=OZoneIzotopeSemiWhite, activeforeground=BACKGROUND2, borderwidth = 0)
self.om.config(fg=buttonforeground, bg=buttonBackground, activebackground=OZoneIzotopeSemiWhite, activeforeground=BACKGROUND2, bd =0)
self.om.place(x = placement, y = 2)
self.om.bind("<Enter>", self.on_enter)
self.om.bind("<Leave>", self.on_leave)
def on_enter(self, event):
if self.om == self.options[0]:
print ("Hello")
elif self.om_variable.get() == self.options[1]:
print ("Hello 2!")
else:
print("Hell0 3!")
def on_leave(self, enter):
print ("leave")
def option_select(self, *args):
print (self.om_variable.get())
root = tk.Tk()
DropDownButton(root, 55, ['one', 'two', 'three'])
root.mainloop()
You're not doing anything wrong as such, but you might need to alter what you do or your expectations. When a menu is popped up by clicking on a menubutton widget — as created by the option menu code — the mouse pointer is grabbed by the menu that has popped up, and this state continues until you do an action that selects something (or you click or release outside the menu, which cancels). Your styling, which depends on the state of the menubutton, might be noticing this and going into something slightly unexpected to you.
By the way, if you pop up the menu using key bindings, the differing look is actually useful as the focus will also be on the actual popup menu.
I am using these calendar modules found in this post for my program, with some slight modifications to the imports to make it work for the latest python version.
I'll just show the snippets of my code that I feel does matter to this problem.
So I have this pop up window that I made that I use for alerts:
#class for pop-up windows for alerts, errors etc.
class PopUpAlert():
def __init__(self, alert='Alert!'):
self.root = tk.Tk()
tk.Label(self.root,
text=alert,
font="Verdana 15",
fg='red',
padx=10,
pady=5).pack(side=tk.TOP)
self.root.bind('<Return>', (lambda event: self.ok()))
tk.Button(self.root,
text='ok',
pady=10,
command=self.ok).pack(side=tk.TOP)
def ok(self):
print('ok clicked')
self.root.destroy()
The function ok was made just for me to test if the function is even being called. This window works completely fine in my code, except when I try to implement with the calendar, where the "ok" button of my PopUpAlert (which is supposed to destroy the window) stops working:
class CalendarDialog(tkSimpleDialog.Dialog):
"""Dialog box that displays a calendar and returns the selected date"""
def body(self, master):
self.calendar = ttkcalendar.Calendar(master)
self.calendar.pack()
def apply(self):
self.result = self.calendar.selection
def validate(self):
if self.calendar.selection == None:
PopUpAlert(alert='Please select a date or click cancel!')
return False
return True
The calendar has an "ok" button that is used to confirm selection of the date and close the calendar window. What I was trying to do is make it such that the user cannot click "ok" to close the window if he/she has not picked a date. For that, I used the function validate which is pre-defined in the class tkSimpleDialog.Dialog which my CalendarDialog inherits from. I overwrote the function in my CalendarDialog class to call up PopUpAlert, then returned False to the parent function ok (which is called when the "Ok" button is pressed on the calendar window):
def ok(self, event=None):
if not self.validate():
self.initial_focus.focus_set() # put focus back
return
self.withdraw()
self.update_idletasks()
self.apply()
self.cancel()
def cancel(self, event=None):
# put focus back to the parent window
self.parent.focus_set()
self.destroy()
(The whole thing can be found in the tkSimpleDialog file that's linked in the other SO page that I linked above.)
After commenting out lines one by one I found that the "ok" button on my PopUpAlert only didn't work when self.root.destroy() isn't called on the calendar. Why? How do I fix this?
I already tried changing my PopUpAlert to be a Toplevel window, which also didn't work.
It would be a lot nicer of you to provide a mcve instead of asking us to make it.
The problem is that a dialog by default disables clicks to other windows, including windows it spawns. To fix this you need to use a Toplevel instead of Tk (as mentioned) AND add this line of code to the end of PopUpAlert.__init__:
self.root.grab_set()
It would be a lot neater if you subclassed Toplevel rather than that weird wrapper. Here's a mcve:
try:
import Tkinter as tk
import tkSimpleDialog as sd
except:
import tkinter as tk
from tkinter import simpledialog as sd
#class for pop-up windows for alerts, errors etc.
class PopUpAlert(tk.Toplevel):
def __init__(self, master, alert='Alert!', **kwargs):
tk.Toplevel.__init__(self, master, **kwargs)
tk.Label(self,
text=alert,
font="Verdana 15",
fg='red',
padx=10,
pady=5).pack(side=tk.TOP)
self.bind('<Return>', self.ok)
tk.Button(self,
text='ok',
pady=10,
command=self.ok).pack(side=tk.TOP)
self.grab_set() # this window only gets commands
def ok(self, *args):
print('ok clicked')
self.destroy()
class CalendarDialog(sd.Dialog):
"""Dialog box that displays a calendar and returns the selected date"""
def body(self, master):
self.calendar = tk.Label(master, text="Whatever you do, don't click 'OK'!")
self.calendar.pack()
def validate(self):
PopUpAlert(self, alert='Please select a date or click cancel!')
def display():
CalendarDialog(root)
root = tk.Tk()
tk.Button(root, text='data data data', command=display).pack()
root.mainloop()
Note I also got rid of that useless lambda, which happens to be a pet peeve of mine. lambda is great in some cases, but it's very rarely needed.
I'm trying to return the focus to the first entry. If you move the focus to the next entry or the button and the you click on the button, the focus returns fine to first entry. When I try doing the same thing by using the tab key, the focus_set method fails. I've tried many different ways, but the result is always the same. Anyone knows why? And might be so kind as to showing me how to do it right? Thanks in advance.
This is what I got so far:
from tkinter import *
w = Tk()
def focus():
box1.focus_set()
def check(event):
if str(event.widget) == '.!entry2':
print('focus back to box1')
focus()
box1 = Entry(w, width=15)
box2 = Entry(w, width=15)
box1.focus_set()
box2.bind('<Tab>', check)
box1.pack()
box2.pack()
btn = Button(w, text='Box 1 Focus', command=focus)
btn.pack()
w.mainloop()
If I run your code, str(event.widget) is something like ".36580648", not ".!entry2". You can give your widget a custom name like
box2 = Entry(w, width=15, name='second')
You can then check if str(event.widget) == '.second'.
Alternatively, you can just check if event.widget == box2: which is easier and less prone to error.
If you do one of these things, you will see that 'focus back to box1' is printed, but the focus is still transferred to the button instead of the label. This is because your custom event is triggered before the default event for <Tab>, which is to move focus to the next widget. You can stop the default event handling by returning 'break' in your function.
The complete example would become:
from tkinter import *
w = Tk()
def focus():
box1.focus_set()
def check(event):
if event.widget == box2:
print('focus back to box1')
focus()
return 'break'
box1 = Entry(w, width=15)
box2 = Entry(w, width=15)
box1.focus_set()
box2.bind('<Tab>', check)
box1.pack()
box2.pack()
btn = Button(w, text='Box 1 Focus', command=focus)
btn.pack()
w.mainloop()
I am trying to learn tkinter, but I got a problem and I can`t move forward from this point. I wanted to make just a simple GUI with one button, unfortunately, I am not able to move that button ( being always displayed in the most left upper corner).
This is the code that I used :
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid()
self.master.title('GUI')
quitbttn = Button(self, text='quit')
quitbttn.grid(row=3, column=5)
root = Tk()
app = App(root)
app.mainloop()
Although, I found this snippet of code on the iternet, and it is working perfectly, the only difference being that pack() is used instead of grid() :
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title("GUI")
self.pack(fill=BOTH, expand=1)
quit_button = Button(self, text='quit')
quit_button.pack(side=BOTTOM)
root = Tk()
app = Window(root)
root.mainloop()
I would like to be able to use grid as well.
Any advice is being apreciated. Thank you!
The reason you cannot move the button is because you only have one element.
quitbttn.grid(row=3, column=5)
This part of the script basically says that the button should be placed a third row down and in the fifth space along. Since you have no other elements in the window it does not move the button at all. This is because all the 2 rows and 4 columns are all equal to 0 so the first place it packs is in the top left corner.
Using the .pack() function allows you move the button without the need of any other button in the window.
If you added another button you would then be able to move around the first button in three different places.
Note that you cannot use the .pack() and .grid() functions in the same window.