From what I read, it is best practice to wrap code for a GUI in a class. How do I do this? I have come up with the following code based on some examples I have looked at, but this does not work as apparently DropdownMenu isn't defined? I have used this function successfully without the class wrapping.
import Tkinter as tk
import tkFileDialog
import os
class Window(tk.Frame):
def DropdownMenu(options,status,name):
optionFrame = tk.Frame(root)
optionLabel = tk.Label(optionFrame)
optionLabel["text"] = name
optionLabel.pack(side=LEFT)
var = StringVar(root)
var.set(status)
w = tk.OptionMenu(optionFrame, var, *options)
w.pack(side=LEFT)
optionFrame.pack()
return w
def __init__(self,parent):
tk.Frame.__init__(self,parent)
ndim_options = DropdownMenu(("1","2","3"),'-',"Number of dimensions")
if __name__ == "__main__":
root = tk.Tk()
Window(root).pack()
root.mainloop()
When you call DropdownMenu (inside the class), use self ...
ndim_options = self.DropdownMenu((...
Related
i'm a new programmer and there are certainly several errors but this shouldn't be difficult to spot. I need to create a simple window with a field named "Concorrente 1:" and an entry field displayed by function named lacopertina(). I don't understand where is the error:
import tkinter as tk
from tkinter import *
from tkinter.ttk import *
from tkinter import ttk
class schermoiniziale(tk.Frame):
def lacopertina():
print(gio1)
#return (tot1)
def __init__(self):
global gio1
#tot1=0
#schermo1=Tk()
self.gio1=tk.StringVar()
lab1=ttk.Label(self, text="Concorrente 1:")
lab1.pack()
ent1=ttk.Entry(self, textvariable=self.gio1)
ent1.pack()
pulsante = ttk.Button(self, text="Inizio", textvariable=self.gio1, command=self.lacopertina)
pulsante.pack()
def main():
schermoiniziale().mainloop()
if __name__== "__main__":
main()
I would suggest you to go through some tutorials on Python OOP.
I have modified your code as below with some comment:
# avoid using wildcard import
import tkinter as tk
from tkinter import ttk
class schermoiniziale(tk.Frame):
def __init__(self, master, **kw):
# need to call __init__() of inherited class
super().__init__(master, **kw)
self.gio1 = tk.StringVar()
lab1 = ttk.Label(self, text="Concorrente 1:")
lab1.pack()
ent1 = ttk.Entry(self, textvariable=self.gio1)
ent1.pack()
# removed textvariable=self.gio1 as I think you actually don't need it
pulsante = ttk.Button(self, text="Inizio", command=self.lacopertina)
pulsante.pack()
def lacopertina(self):
# use .get() to get the content of a StringVar
print(self.gio1.get())
def main():
# need to create the root window before creating other widget
root = tk.Tk()
# pass root window as the parent of the widget
frame = schermoiniziale(root)
frame.pack()
# start the tkinter mainloop
root.mainloop()
if __name__== "__main__":
main()
This attempt is try to get the value from a function in class One which is retrieve when the function is call. Then the value [string1] would be passed as a parameter for class Two's method call which's within tkinter, any idea on how can it be attain
from tkinter import Tk, Label, Button
from tkinter import *
from tkinter.ttk import *
class One:
def __init__(self, master):
self.master = master
master.title("Test")
self.greet_button = Button(master, text="Test", command=self.do)
self.greet_button.pack()
def do(self):
...some action...
string1='test'
return string1
class Two:
def write(self, str):
...some action...
#object instantiate within tkinter
root = Tk()
p0 = One(root)
p0.do()
p1 = Two()
p1.write(string1)
root.mainloop()
##from tkinter import Tk, Label, Button
##
##from tkinter import *
##from tkinter.ttk import *
####why are you importing some and then everything?
import tkinter as tk
class Reader_Class(tk.Frame):
def __init__(self, master, writer_class):
tk.Frame.__init__(self,master)
self.master = master
master.title("Test")
self.writer = writer_class
self.greet_button = tk.Button(self, text="Test", command=self.do)
self.greet_button.pack()
def do(self):
string1='test'
self.writer.write(string1)
class Writer_Class(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
def write(self, test):
print(test)
root = tk.Tk()
writer = Writer_Class(root)
writer.pack()
reader = Reader_Class(root, writer)
reader.pack()
root.mainloop()
Explaination:
So first of all, it isnt necessary to import stuff twice.
Second you need to pass through the interface a reference, therefor you need to have reference. Thats why it makes sense to use the construktor first on the writer_class.
Next you give the reference as a parameter to the reader_class.
reader = Reader_Class(root, writer)
In the reader_class you keep a reference in the local_scope by using the syntax self..
self.writer = writer_class
Finally you use the reference of that class and calling a method of this class, where you can pass through a parameter of your choice.
self.writer.write(string1)
I am new to python. working on python 3.7, windows os. suppose that i have created a file named
Class1.py in which
import tkinter as tk
import Class2
class main_window:
def openanotherwin():
Class2.this.now()
def create():
root = tk.Tk()
button1 = tk.Button(root, text="Open another window", command = openanotherwin )
button1.pack()
root.mainloop()
Now my Class2.py contains:
import tkinter as tk
class this():
def now():
new = tk.Toplevel(root) #Error displayed: root is not defined
lb = tk.Label(new, text = "Hello")
lb.pack()
new.mainloop()
and my Main.py contains:
import Class1
Class1.main_window.create()
Error displayed is: root is not defined in Class2.py. I have tried root = Class1.main_window.root to bring the value of root but it showed error that function has no attribute root.
Please help me solving my problem.
I think function need to get root
def now(root):
new = tk.Toplevel(root) #Error displayed: root is not defined
Then in class1:
def openanotherwin(root):
Class2.this.now(root)
And third:
button1 = tk.Button(root, text="Open another window", command=lambda: main_window.openanotherwin(root) )
===
Class1.py
import tkinter as tk
import Class2
class main_window:
def openanotherwin(root):
Class2.this.now(root)
def create():
root = tk.Tk()
button1 = tk.Button(root, text="Open another window", command=lambda: main_window.openanotherwin(root) )
button1.pack()
root.mainloop()
Class2.py
import tkinter as tk
class this():
def now(root):
new = tk.Toplevel(root) #Error displayed: root is not defined
lb = tk.Label(new, text = "Hello")
lb.pack()
new.mainloop()
First, an error might be in the name "this" of your class in Class2.
I guess that "this" is a reserved name for the current object instance.
You should change that to something else, e.g. "class2"
Then you should instantiate class2 in your class1 and pass root to the constructor as an argument. Only then, you can use root in class2.
Here's an example for passing arguments to a class constructor:
class DemoClass:
num = 101
# parameterized constructor
def __init__(self, data):
self.num = data
# a method
def read_number(self):
print(self.num)
# creating object of the class
# this will invoke parameterized constructor
obj = DemoClass(55)
# calling the instance method using the object obj
obj.read_number()
# creating another object of the class
obj2 = DemoClass(66)
# calling the instance method using the object obj
obj2.read_number()
I'm having an issue trying to call a class's method from a command on tkinters button. I have tried:
command = Alarm_clock.save_alarm()
command = self.save_alarm()
command = Alarm_clock.save_alarm(self.hour_count, self.min_count)
command = Alarm_clock.save_alarm(self)
I think I'm missing something obvious because I have managed to 'command' methods within the same class.
Here is a snippet of my code: It's for an alarm clock:
import tkinter as tk
import time
import vlc
import pygame
from mutagen.mp3 import MP3
class Alarm_clock():
def __init__(self):
alarm_list = []
def save_alarm(self):
print(init_alarm_gui.hour_count.get())
print(init_alarm_gui.min_count.get())
#get the hour and minute and append it to the alarm_list to be checked on real time
class init_alarm_gui():
def __init__(self, root):
self.root = root
self.hour_count = 0
self.min_count = 0
def make_widgets(self, root):
self.hour_count = tk.IntVar()
self.min_count = tk.IntVar()
self.time_label = tk.Label(text="")
self.time_label.pack()
self.Save_but = tk.Button(root, text = "Save Alarm", command=Alarm_clock.save_alarm)
self.Save_but.pack()
def main():
root = tk.Tk()
gui = init_alarm_gui(root)
gui.make_widgets(root)
root.mainloop()
if __name__ == "__main__":
main()
TyperError: save_alarm() missing 1 required positional argument: 'self'
You must create an instance of the class, and then call the method of the instance. This will automatically fill in the self parameter. This isn't unique to tkinter, it's just how python works.
alarm = Alarm_clock()
alarm.save_alarm()
I'm trying to use a trace callback to validate inputs to a set of combo boxes but the arguments I get in the callback are just a string representation of the internal name. Is there a way to get a reference to the actual variable triggering the trace callback or a way to get at the variable using the internal name like PY_VAR1?
from Tkinter import *
import ttk
def validate(self, *args):
print(self)
print(self.get())
master = Tk()
cb1_var = StringVar()
cb2_var = StringVar()
cb1_var.trace('w', validate)
cb2_var.trace('w', validate)
cb1 = ttk.Combobox(master, textvariable=cb1_var)
cb2 = ttk.Combobox(master, textvariable=cb2_var)
cb1.grid(row=0, column=0, sticky='NW')
cb2.grid(row=1, column=0, sticky='NW')
mainloop()
Fails on trying to call self.get() since self is just that string representation, not the actual StringVar. I don't want to have a specific callback for each StringVar since the actual interface has around 30 boxes I all want validated by the same criteria.
You could simply pass the arguments you want by making use of lambda statement for anonymous functions. Replace:
def validate(self, *args):
print(self)
print(self.get())
...
cb1_var.trace('w', validate)
cb2_var.trace('w', validate)
with:
def validate(var):
print(var)
print(var.get())
...
cb1_var.trace('w', lambda *_, var=cb1_var: validate(var))
cb2_var.trace('w', lambda *_, var=cb2_var: validate(var))
If you use multiple objects that are related, simply use collection types. For the example in the question, I see a list should be a good fit.
See the example below:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
import tkinter.ttk as ttk
except ImportError:
import Tkinter as tk
import ttk
def upon_var_change(var):
print(var.get())
if __name__ == '__main__':
root = tk.Tk()
cbs = list()
for i in range(3):
cbs.append(ttk.Combobox(root))
cbs[i].var = tk.StringVar()
cbs[i].var.trace_add('write', lambda *_,
var=cbs[i].var:upon_var_change(var))
cbs[i]['textvariable'] = cbs[i].var
cbs[i].grid(row=i // 2, column=i % 2, sticky='nw')
tk.mainloop()
If such is required you could identify the Variable class from its internal reference as well:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
import tkinter.ttk as ttk
except ImportError:
import Tkinter as tk
import ttk
def upon_var_change(var_name):
value = root.tk.globalgetvar(var_name)
print(var_name, value)
if __name__ == '__main__':
root = tk.Tk()
cbs = list()
for i in range(3):
cbs.append(ttk.Combobox(root))
cbs[i].var = tk.StringVar()
cbs[i].var.trace_add('write', lambda var_name,
*_: upon_var_change(var_name))
cbs[i]['textvariable'] = cbs[i].var
cbs[i].grid(row=i // 2, column=i % 2, sticky='nw')
tk.mainloop()
I know this is an old post, gut I found a way to use the internal variable name. Modify your validate function like this:
def validate(self, *args):
var_name = args[0]
var = IntVar(name=var_name)
print(var.get())
master = Tk()
cb1_var = StringVar()
cb2_var = StringVar()
cb1_var.trace('w', validate)
cb2_var.trace('w', validate)