Below you can see the code I have for a method in my GUI Class. I have been trying to create an option menu from a list however I am getting an error. It says that tkinter has no module 'apply'. In all the examples I can find people use Tkinter instead of tkinter, so has there been a change in the apply method from python 2.x to 3.x?
I have tried writing all of the following:
tk.apply, tk.Apply, apply. But nothing seems to work.
import tkinter as tk
class GUI:
def UploadHomeworkScreen(self):
self.masternew = tk.Tk()
self.framenew = tk.Frame(self.masternew)
self.HomeworkFileEntry = tk.Entry(self.framenew)
self.ClassVariable = tk.StringVar(self.masternew)
self.ClassVariable.set(Client.ListOfClasses[0])
self.ClassChoice = tk.apply(tk.OptionMenu, (self.framenew, self.ClassVariable) + tuple(Client.ListOfClasses))
self.SubmitButton = tk.Button(self.framenew, text = "Submit", command = self.SubmitHomework)
self.HomeworkFileEntry.pack(pady = 30, padx = 10)
self.ClassChoice.pack()
self.SubmitButton.pack()
self.framenew.pack()
self.masternew.mainloop()
I am open to creating the option menu another way if it is possible.
Thanks.
Note: apply() doesn't exist in Python 3. Any guide that uses it(notably, the tkinterbook on effbot.org) is horribly out of date.
Per tkinter's definition for class OptionMenu(Menubutton):
The OptionMenu is initialized as such:
def __init__(self, master, variable, value, *values, **kwargs):
"""Construct an optionmenu widget with the parent MASTER, with
the resource textvariable set to VARIABLE, the initially selected
value VALUE, the other menu values VALUES and an additional
keyword argument command."""
Taking that into account, your code line:
self.ClassChoice = tk.apply(tk.OptionMenu, (self.framenew, self.ClassVariable) + tuple(Client.ListOfClasses))
Should be changed to:
self.ClassChoice = tk.OptionMenu(self.framenew, self.ClassVariable, *Client.ListOfClasses)
Note the asterisk before Client.ListOfClasses. This is to pass in the menu VALUES needed by the OptionMenu as a list, per https://docs.python.org/3.7/tutorial/controlflow.html#unpacking-argument-lists.
The apply function has been removed in Python 3.[1] It was not a tkinter specific function. To fix this, use the first parameter of the apply function as the function name, like so:
Your code:
self.ClassChoice = tk.apply(tk.OptionMenu, (self.framenew, self.ClassVariable) + tuple(Client.ListOfClasses))
New:
self.ClassChoice = tk.OptionMenu((self.framenew, self.ClassVariable) + tuple(Client.ListOfClasses))
Or as the documentation for 2to3 says, "apply(function, *args, **kwargs) is converted to function(*args, **kwargs)."
Related
I'm developing an application what have its functions set in different files.
The main file have a tkinter interface and the buttons, entrys and labels are in other file, like this:
Mainfile.py
from tkinter import *
class Program:
def __init__(self, root):
root.geometry('200x200')
self.main_frame = Frame(root)
self.main_frame.pack()
import Buttons
self.branch = Buttons.New_Button(self.main_frame)
#Here i wuold like to verify the hipotetic variable after the main_frame were destroyed
if self.branch.hipotetic_variable:
root.mainloop()
app = Program(Tk())
Buttons.py
from tkinter import *
import functools
class New_Button:
def __init__(self, using_frame):
self.button_1 = Button(using_frame, text = 'Button 1', command=functools.partial(self.Func, using_frame))
self.button_1.pack()
def Func(self, to_destroy):
to_destroy.destroy()
#Here is the hipotetic variable what i would like to verify with if statment
self.hipotetic_variable = True
The problem is that I want to keep managing the program in the main file calling the other functions and implementing it, but I cannot verify if it's time to update the screen because mainloop makes impossible to verify it using a while loop and an hipotetic variable that's created after user pressed button.
I wold like to know if there is an way to update an variable contained in the Buttons.py file on Mainfile.py to keep implementing all other canvas in this file.
Your if self.branch.hipotetic_variable: check in the Program.__init__() method is only going to be executed when the Program class instance gets created initially, which is before the button that could change the value of the variable could have been pressed. You also don't want to make the hipotetic_variable an attribute of the Button because that will be destroyed along with the Frame it is in when that's destroyed in the button callback function.
Tkinter applications are user-event driven, meaning that they're "run" by responding to events (that's what mainloop is all about). This type of programming paradigm is different from the procedural or imperative one you're probably used to.
Therefore to do what you want requires setting things up so an event that the program can respond to will be generated, which in this case to when the frame is destroyed. One way to do that is by taking advantage of tkinter Variable classes to hold this hipotetic variable you're interested in. It looks like a boolean, so I used a tkinter BooleanVar to hold its value. One interesting thing about Variables is that you can have changes to their values "traced" by defining functions to be called whenever that happens. That's what I have done in the code below, and the callback function in this case — check_hipotetic_variable() — updates a Label to display the new value of the variable when it's called.
Below is your code with the modifications necessary to use a tkinter BooleanVar and trace changes to its value.
Mainfile.py
from tkinter import *
import Buttons
class Program:
def __init__(self, root):
root.geometry('200x200')
self.main_frame = Frame(root)
self.main_frame.pack()
self.notice_lbl = Label(root, text='')
self.notice_lbl.pack(side=BOTTOM)
self.hipotetic_variable = BooleanVar(value=False)
# Set up a trace "write" callback for whenever its contents are changed.
self.hipotetic_variable.trace('w', self.check_hipotetic_variable)
self.branch = Buttons.New_Button(self.main_frame, self.hipotetic_variable)
root.mainloop()
def check_hipotetic_variable(self, *args):
"""Display value of the hipotetic variable."""
value = self.hipotetic_variable.get()
self.notice_lbl.config(text=f'hipotetic variable is: {value}')
app = Program(Tk())
Buttons.py
from tkinter import *
import functools
class New_Button:
def __init__(self, using_frame, variable):
self.button_1 = Button(using_frame, text = 'Button 1',
command=functools.partial(self.Func, using_frame))
self.button_1.pack()
self.variable = variable # Save for use in callback.
def Func(self, to_destroy):
to_destroy.destroy()
self.variable.set(True) # # Change value of the variable.
P.S. I noticed you're not following the PEP 8 - Style Guide for Python Code, which makes reading your code harder to read and follow that if you're were following them — for that reason I strongly suggest you read the guide and start following the suggestions, especially the Naming Conventions which apply to functions and variable names, as well as the names of script files.
New to GUI. Not quite getting there. I used page and get can get buttons to do something (click on a button and get a response). With Combobox, I can't pass a value. Searched here, tried many things, watched a few hours of youtube tutorials.
What am I doing wrong below? This is the code page generates (basically) then I added what I think I need to do to use the Combobox.
I am just trying to have 1,2,3 in a combo box and print out the value that is chosen. Once I figure that out I think I can actually make a simple GUI that passes variables I can then program what I want to do with these variables being selected.
class New_Toplevel_1:
def __init__(self, top):
self.box_value = StringVar()
self.TCombobox1 = ttk.Combobox(textvariable=self.box_value)
self.TCombobox1.place(relx=0.52, rely=0.38, relheight=0.05, relwidth=0.24)
self.TCombobox1['values']=['1','2','3']
self.TCombobox1.configure(background="#ffff80")
self.TCombobox1.configure(takefocus="")
self.TCombobox1.bind('<<ComboboxSelected>>',func=select_combobox)
def select_combobox(self,top=None):
print 'test combo ' # this prints so the bind works
self.value_of_combo = self.ttk.Combobox.get() # this plus many other attempts does not work
It's hard to know what you're actually asking about, since there is more than one thing wrong with your code. Since you say the print statement is working, I'm assuming the only problem you have with your actual code is with the last line.
To get the value of the combobox, get the value of the associated variable:
self.value_of_combo = self.box_value.get()
Here's a working version where I fixed the other things that were wrong with the program:
from tkinter import *
from tkinter import ttk
class New_Toplevel_1:
def __init__(self, top):
self.box_value = StringVar()
self.TCombobox1 = ttk.Combobox(textvariable=self.box_value)
self.TCombobox1.place(relx=0.52, rely=0.38, relheight=0.05, relwidth=0.24)
self.TCombobox1['values']=['1','2','3']
self.TCombobox1.configure(background="#ffff80")
self.TCombobox1.configure(takefocus="")
self.TCombobox1.bind('<<ComboboxSelected>>',func=self.select_combobox)
def select_combobox(self,top=None):
print('test combo ') # this prints so the bind works
self.value_of_combo = self.box_value.get()
print(self.value_of_combo)
root = Tk()
top = New_Toplevel_1(root)
root.mainloop()
Note: I strongly advise you not to start with place. You should try to learn pack and place first. I know place seems easier, but to get the most responsive and flexible GUI you should leverage the power of pack and grid.
I have a GUI in Tkinter that is becoming more complicated. I would like to separate it into some modules to make it easier to manage. Is there a way to separate my GUI into modules if I didn't use the object oriented approach?
Here is a simple example of some code I would like to move to a separate module:
def create_main_nav_buttons(strat_folder_list):
global dynamic_info_nav_items
temp_count = 0
for item in strat_folder_list:
main_nav = tk.Canvas(Nav_Frame_Top, width=175, height=grid_box_size/1.5, highlightthickness=1, bg='slategray1')
main_nav.grid(row = temp_count, column = 1)
main_nav.bind("<Button-1>", lambda event, x=item: create_navigation2(x))
temp_count += 1
dynamic_info_nav_items.append(main_nav)
-Side note:
I wrote a GUI using the object oriented approach before but decided to not use it this time because I didn't fully understand parts of it such as:
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
So when something went wrong it was a nightmare to fix and I couldn't find much support.
Using OO techniques is the proper way to divide code into modules, but it's not the only way. I think it's a bit more work to do it without classes, but there's nothing preventing you from doing what you want without classes.
The main thing you need to change about the way you are coding is to stop relying on global variables. That means you need to pass in information from the main program to the functions, and the functions need to return information back to the main program.
Example
I've tried to keep the code in this example as close to your original as possible, though I've made a few small changes.
First, create a file named "widgets.py" with the following contents:
import tkinter as tk
def create_main_nav_buttons(parent, grid_box_size, items):
temp_count = 0
widgets = []
for item in items:
main_nav = tk.Canvas(parent, width=175, height=grid_box_size/1.5, highlightthickness=1, bg='slategray1')
main_nav.grid(row = temp_count, column = 1)
main_nav.bind("<Button-1>", lambda event, x=item: create_navigation2(x))
temp_count += 1
widgets.append(main_nav)
return widgets
def create_navigation2(x):
print("create_navigation2...", x)
Next, create your main program in a file named "main.py":
import tkinter as tk
from widgets import create_main_nav_buttons
root = tk.Tk()
Nav_Frame_Top = tk.Frame(root)
Nav_Frame_Top.pack(side="top", fill="x")
dynamic_info_nav_items = []
strat_folder_list = ["one", "two", "three"]
grid_box_size=10
widgets = create_main_nav_buttons(Nav_Frame_Top, grid_box_size, strat_folder_list)
dynamic_info_nav_items += widgets
root.mainloop()
Notice that the parent and the other items that you were expecting to be in the global namespace are passed in to the function, and then the function returns values which the main program can use.
In essence you are creating a contract between the function and the main program. The contract is "you tell me the information I need to construct the widgets, and I'll create the widgets and return them to you in a list".
have spent a while looking for an answer. I'm new to Python but not to coding in general. Finding the various versions quite challenging!
Anyway I'm very Gui orientated and have managed to get tkinter working with Python 3.5.1
Just playing with basics and have the following code but cannot set focus to the first entry box. Have tried mEntry1.focus() and mEntry1.focus_set() but always get object has no attribute error. Any help?
from tkinter import *
def calc(*args):
try:
value1 = float(V1.get())
value2 = float(V2.get())
result.set(value1 * value2)
except ValueError:
pass
mGui = Tk()
mGui.geometry('450x450+200+200')
mGui.title('Test Gui')
V1 = StringVar()
V2 = StringVar()
result = StringVar()
mEntry1 = Entry(textvariable=V1,width=10).grid(row=0,column=0,sticky=W)
mEntry2 = Entry(textvariable=V2).grid(row=1,column=0)
mButton = Button(text='Calculate',command=calc).grid(row=3,column=0)
mlabel = Label(textvariable=result).grid(row=4,column=2)
Every Tkinter widget has the focus_set method.
The problem with your code is that the .grid method returns None. Thus
mEntry1 = Entry(textvariable=V1,width=10).grid(row=0,column=0,sticky=W)
sets mEntry1 to None, not the widget. So you need to create the widget and put it in the grid in two steps:
mEntry1 = Entry(textvariable=V1,width=10)
mEntry1.grid(row=0,column=0,sticky=W)
Of course, if you don't actually need a reference to the widget object then it's ok to do it in one step. So something like
Entry(textvariable=V1,width=10).grid(row=0,column=0,sticky=W)
would be fine.
BTW, it's not a good idea to use from tkinter import *. It imports over 130 names into your namespace, which can lead to name collisions with your own names, or with those of other modules if you also import them with a "star" import statement. It also makes the code harder to read. Instead, you can do
import tkinter as tk
and then refer to Tkinter names using the tk. prefix, eg tk.Entry instead of Entry.
Basically my problem is in configuring a combobox in a tablelist written in python (not tcl directly). I have prepared an example that could demonstrate the problem, but before that lets see the required steps to run it:
Copy the tablelist wrapper from here and save it as 'tablelist.py', then put it at the example code directory.
Download "tklib-0.5" from here and copy "tablelist" directory from "modules" directory to the directory of example code.
Here it is the code:
from tkinter import *
from tkinter import ttk
from tablelist import TableList
class Window (Frame):
def __init__(self):
# frame
Frame.__init__(self)
self.grid()
# tablelist
self.tableList = TableList(self,
columns = (0, "Parity"),
editstartcommand=self.editStartCmd
)
self.tableList.grid()
# configure column #0
self.tableList.columnconfigure(0, editable="yes", editwindow="ttk::combobox")
# insert an item
self.tableList.insert(END,('Even'))
def editStartCmd(self, table, row, col, text):
#
# must configure "values" option of Combobox here!
#
return
def main():
Window().mainloop()
if __name__ == '__main__':
main()
As you would see it results in a single column/cell window with an initial value (Even). By clicking on the cell the combobox will appear (because of using "editstartcommand") and it doesn't have any values (None). I know for editing cell's widgets one has to use "editwinpath" command to get pathname of temporary widget, but the method just returns a string of the address referring to combobox widget that is not callable.
I'd appreciate any help or possible solutions.
I know it's a bit weird that I'm responding to my own question, but I'm hopeful that it could be useful to others in the future. After reading tablelist scripts and examples in TCL, I was able to find my answer. The answer has two different parts, one is related to the wrapper (tablelist wrapper in python that you can find it here), and the other one resides in my example code. First of all the wrapper needs to be modified to include widgets' pathnames in its "configure" method of "TableList" class. Here it is the modified version:
def configure(self, pathname, cnf={}, **kw):
"""Queries or modifies the configuration options of the
widget.
If no option is specified, the command returns a list
describing all of the available options for def (see
Tk_ConfigureInfo for information on the format of this list).
If option is specified with no value, then the command
returns a list describing the one named option (this list
will be identical to the corresponding sublist of the value
returned if no option is specified). If one or more
option-value pairs are specified, then the command modifies
the given widget option(s) to have the given value(s); in
this case the return value is an empty string. option may
have any of the values accepted by the tablelist::tablelist
command.
"""
return self.tk.call((pathname, "configure") +
self._options(cnf, kw))
Second and last part is the obligation that the "editstartcommand" (in case of my example code "editStartCmd" method) has to return its "text" variable at the end of it. This trick would prevent entries from turning to "None" as you click on them. The correct form of example code is:
from tkinter import *
from tkinter import ttk
from tablelist import TableList
class Window (Frame):
def __init__(self):
# frame
Frame.__init__(self)
self.grid()
# tablelist
self.tableList = TableList(self,
columns = (0, "Parity"),
editstartcommand=self.editStartCmd
)
self.tableList.grid()
# configure column #0
self.tableList.columnconfigure(0, editable="yes", editwindow="ttk::combobox")
# insert an item
self.tableList.insert(END,('Even'))
def editStartCmd(self, table, row, col, text):
#
# must configure "values" option of Combobox here!
#
pathname = self.tableList.editwinpath()
self.tableList.configure(pathname, values=('Even','Odd'))
return text
def main():
Window().mainloop()
if __name__ == '__main__':
main()
That's it and I hope it was clear enough.