I wanted to create module based tkinter classes. I want to be able var_text variable to be able to print on Label text given to it.
from tkinter import *
class Maincl(Tk):
def __init__(self,xwidth,yheight):
super().__init__()
self.geometry(f'{xwidth}x{yheight}')
self.var_text=StringVar()
Labelcl(self,'darkblue',30,3).pack()
Labelcl(self, 'white', 30, 3,self.var_text.set('hello tkinter')).pack()
self.mainloop()
class Labelcl(Label):
def __init__(self,master,bg_color,width,height,text_variable=None):
super().__init__(master)
self.master=master
self.configure(bg=bg_color,width=width,height=height,textvariable=text_variable)
app=Maincl('500','300')
But as of matter, for testing purpose I assigned(set) to var_text to "hello tkinter" but cannot see text when code is run.
set() on any tkinter variable will not return anything. So, text_variable is always None as self.var_text.set('hello tkinter') will return None.
self.var_text.set('hello tkinter')
Labelcl(self, 'white', 30, 3,self.var_text).pack()
Here, it will pass the StringVar instance itself.
You can set the initial value when creating the StringVar:
from tkinter import *
class Maincl(Tk):
def __init__(self,xwidth,yheight):
super().__init__()
self.geometry(f'{xwidth}x{yheight}')
# set initial value using `value` option
self.var_text=StringVar(value='hello tkinter')
Labelcl(self,'darkblue',30,3).pack()
# need to pass the variable reference
Labelcl(self, 'white', 30, 3, self.var_text).pack()
self.mainloop()
class Labelcl(Label):
def __init__(self,master,bg_color,width,height,text_variable=None):
super().__init__(master)
self.master=master
self.configure(bg=bg_color,width=width,height=height,textvariable=text_variable)
app=Maincl('500','300')
Related
I make code for displaying two serial read string in two different label using tkinter GUI..
My code is running and window also popped up but values are not displaying..
Help me to solve this issue..
Here is my code
import serial
import tkinter
from tkinter import *
import time
class SerialViewer(Tk):
def _init_(self):
self.win = Tk()
self.ser=serial.Serial('/dev/ttyS0',9600)
def makewindow (self):
frame1 =Frame(self.win)
frame1.pack()
self.v=StringVar()
self.v.set=('default')
label=Label(frame1,textvariable=self.v,relief=RAISED)
label.pack(side=LEFT)
frame2 = Frame(self.win)
frame2.pack()
self.d=StringVar()
self.d.set=('default')
label=Label(frame2,textvariable=self.d,relief=RAISED)
label.pack(side=RIGHT)
def update(self):
print(self.ser.write("*01T%"))
data=self.ser.readline(self.ser.inWaiting())
self.v.set(data)
time.sleep(2)
print(self.ser.write('*00T%'))
data1=self.ser.readline(self.ser.inWaiting())
self.d.set(data1)
self.win.after(100,self.update)
def run (self):
self.makewindow()
self.update()
self.win.mainloop()
app=SerialViewer()
app.mainloop()
As said in comment that the method _init_ needs to be changed to __init__ and the recursion error is coming because the __init__ method of parent class wasn't called. Please add that call like below:
class SerialViewer(Tk):
def _init_(self):
super(SerialViewer, self).__init__() # Call the initializer method of parent class
self.win = Tk()
self.ser=serial.Serial('/dev/ttyS0',9600)
Hope this helps!!
This question already has an answer here:
How to save askdirectory result in a variable I can use using tkinter with OOP?
(1 answer)
Closed 4 years ago.
first of all im pretty new to OOP coding, so sorry for asking stupid questions.
I have a problem returning a value from Class AskDir that gets its value from Class SelectDir to Class MainWindow if that makes sense
Currently the code i have works, in every other way, except I cannot save the actual "self" (which is the path i.e. "/home/etc/") return value from Class AskDir() to another variable no matter what i do.
From what i have read i should be able to get the return value with a = AskDir(self)
print(a), but what I get instead is ".!frame.!functionchangename.!askdir"
So in short, how to save actual return path from a function inside Class AskDir, to be saved to variable a in Class MainWindow()?
To clarify, what i want is for Class MainWindow() to have a variable (a) that gets the return value from a subfunction get() inside Class SelectDir(), and that value should be the path that get() function returns
simplified code:
class MainWindow:
self.controller = controller
# prints the button to this frame!
getdir = AskDir(self)
print(getdir) # this should return actual path (/home/etc/), but it doesnt
class AskDir(tk.Frame):
def __init__(self, container):
tk.Frame.__init__(self, container)
self.selectdir = SelectDir(self, "Select directory",
"/home/user/folder/")
button = tk.Button(frame, text="Select directory",
command=self.select_dir)
self.act_dir = tk.StringVar()
self.act_dir.set("/home/")
def select_dir(self):
self.selectdir.show()
self.act_dir.set(self.selectdir.get())
class SelectDir:
def __init__(self, container, title, initial):
self.master = container
def show(self):
result = filedialog.askdirectory()
if result:
self.selected = result
# THIS RETURNS THE ACTUAL PATH!
def get(self):
return self.selected
Does this work for you?
I can see no need to have SelectDir as a class given that its purpose is just to call the askdirectory method and return the selected folder path so I've change it to a function.
With this example, pressing the "Select directory" button will open the directory dialog and then place the selected directory text in to an Entry widget.
If the MainWindow class wishes to do anything with the path once it has been set then you just need to call the get_dir method of AskDir e.g. print(askDir.get_dir()
import tkinter as tk
from tkinter import filedialog
def doStuffFunction(value):
print(value)
class MainWindow(tk.Frame):
def __init__(self,master=None,**kw):
tk.Frame.__init__(self,master=master,**kw)
self.askDir = AskDir(self)
self.askDir.grid()
doStuffButton = tk.Button(self, text="Do Stuff", command=self.doStuff)
doStuffButton.grid()
def doStuff(self):
doStuffFunction(self.askDir.get_dir())
class AskDir(tk.Frame):
def __init__(self, master, **kw):
tk.Frame.__init__(self, master=master,**kw)
button = tk.Button(self, text="Select directory",
command=self.select_dir)
button.grid(row=0,column=1)
self.act_dir = tk.StringVar()
self.act_dir.set("/home/")
self.pathBox = tk.Entry(self, textvariable=self.act_dir,width=50)
self.pathBox.grid(row=0,column=0)
def select_dir(self):
selectdir = SelectDir("Select directory", "/home/user/folder/")
self.act_dir.set(selectdir)
def get_dir(self):
return self.act_dir.get()
def SelectDir(title,initial):
result = filedialog.askdirectory(initialdir=initial,title=title)
return result
if __name__ == '__main__':
root = tk.Tk()
MainWindow(root).grid()
root.mainloop()
You can now use the AskDir class as many times as you want in the MainWindow (perhaps to select Source and Destination paths). This has been added to my example with the doStuff method inside MainWindow.
How can I call a function change_label whenever global variable a changes its value? With change_variable I am trying to simulate actual changing of the variable (the variable changes on button click).
from tkinter import *
a = 3
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.master = master
self.button = Button(self.master, text='Change Variable', command=self.change_variable)
self.button.grid(row=0)
self.label = Label(self.master, text='Test')
self.label.grid(row=1)
def change_label(self):
self.label.config(bg='Red', fg='Yellow')
def change_variable(self):
global a
a = 1
def main():
root = Tk()
Application(root)
root.mainloop()
if __name__ == '__main__':
main()
If you use one of tkinters special variables (StringVar, etc) you can add a "trace" that will trigger a callback whenever the variable is set or unset.
For example:
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master)
...
self.a = tk.IntVar(value=3)
self.a.trace("w", self.change_label)
...
def change_label(self, *args):
self.label.config(bg='Red', fg='Yellow')
def change_variable(self):
self.a.set(1)
With that, whenever you set the value of self.a via the set method, the function bound with the trace will be called.
Any widget that uses that variable also will be updated. For example, change your label to this:
self.label = tk.Label(self.master, textvariable=self.a)
When you click the button, notice that the label changes to reflect the change.
For a description of what arguments are passed to the trace function, see What are the arguments to Tkinter variable trace method callbacks?
These variables have a good description here: The Variable Classes (BooleanVar, DoubleVar, IntVar, StringVar)
If you are using Tk, this might be worth looking into: Tk's "Variable" classes
If that doesn't work for you (because you want to store your own type, or something like that), Shiva's comment is the way to go, and if you want to store multiple variables, this might be a good idea:
class Storage(dict):
def __getattribute__(self, name):
return self[name]
def __setattr__(self, name, value):
print(name, value) # react to the change of name
self[name] = value
storage = Storage({a: 3, b: 2})
storage.a = 4
print(storage.a)
If you don't want to be able to set the variable without triggering some code you put there, good luck. You can override __setitem__ too, but you can always call dict.__setitem__ with the Storage variable as the first argument.
Try running this code it will give you the idea of what you want.
import tkinter
count = 5
def change_text():
global count
if count != 2:
button.config(text='edit')
frame = tkinter.Frame(height=500,width=500)
button = tkinter.Button(frame,text='save',command=change_text)
button.place(x=0,y=0)
frame.pack()
frame.mainloop()
The code was supposed be like this.
from tkinter import *
a = 3
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.master = master
self.button = Button(self.master, text='Change Variable', command=self.change_label)
self.button.grid(row=0)
self.label = Label(self.master, text='Test')
self.label.grid(row=1)
def change_label(self):
global a
a = 1
if a != 3:
self.label.config(bg='Red', fg='Yellow')
def main():
root = Tk()
Application(root)
root.mainloop()
hope that is what you wanted.
I have a Window wich opens another window askig for the settings. But the BooleanVar I use to get the Checkbutton's state doesn't change. It does however when I call the settingswindow strait from the code without the other window.
This is the minimal code to get the
from tkinter import *
class MainWindow():
def __init__(self, master):
self.root = master
SettingsWindow()
self.root.mainloop()
class SettingsWindow():
def __init__(self):
rootSettings = Tk()
self.rebuild = BooleanVar()
chkRebuild = Checkbutton(rootSettings, text="rebuild", variable=self.rebuild, command=self.testFunc)
chkRebuild.pack()
rootSettings.mainloop()
def testFunc(self):
print(self.rebuild.get())
root = Tk()
mainWindow = MainWindow(root)
The output is always 0 when clicking on the checkbutton, evenso the output of BooleanVar is True or False.
What is the difference in calling SettingsWindow() from insite a class or outside? At least I think that is the reason it is not working.
I am using Pyhton3 in case there is a difference.
You can't have two instances of Tk. For your second window you need to create a Toplevel. You also should never call mainloop more than once in your entire program.
This is a sample example for how to solve the checkbutton's variable not changing issue.
Key: for the second window, should use Toplevel, not use Tk.
from tkinter import *
class MainWindow:
def __init__(self):
self.master = Tk()
self.fun = dict()
def set_ui(self):
Button(self.master, text='SecondWindow', command=self.fun).pack(side=LEFT)
self.master.mainloop()
class SecondWindow:
def __init__(self):
self.root = Toplevel()
self.var = BooleanVar()
self.set_ui()
def printf(self):
print(self.var.get())
def set_ui(self):
Checkbutton(self.root, text='press', variable=self.var, command=self.printf).pack(side=LEFT)
def call_second_window():
second_window = SecondWindow()
main_window = MainWindow()
main_window.fun = call_second_window
main_window.set_ui()
Python 3.1, tkinter/ttk
I wrote something very simple to try and understand how tkinter's variables linked to widgets can be stored in a different class to the widget. Code below.
Questions:
1) why doesn't pressing the button change the label?
2) do I need quite so many selfs? Can the variables within each method manage without self. on the start?
Hopefully the answer will be a useful learning exercise for other tkinter newbies...
from tkinter import *
from tkinter.ttk import *
root = Tk()
class Store:
def __init__(self):
self.v = IntVar()
self.v.set(0)
def set(self, v):
self.v.set(v)
class Main:
def __init__(self):
self.counter = 0
self.label = Label(root, textvariable = a.v)
self.label.pack()
self.button = Button(root, command = self.counter, text = '+1')
self.button.pack()
def counter(self):
self.counter = self.counter + 1
a.set(self.counter)
a = Store()
b = Main()
root.mainloop()
Your problem is that you have both a method and a variable named counter. When you click the button, your function isn't being called so the variable isn't being set. At the time you create the button, tkinter thinks that self.counter is a variable rather than a command.
The solution to this particular problem is to rename either the function or the variable. It then should work as you expect.
To answer the question about "too many selfs": you need to use self so that python knows that the object you are referring to should be available everywhere within a specific instance of the object. So, yes, you need all those self's, if you want to refer to variables outside of the function they are defined in.
As for self in the definition of a method, those too are necessary. When you do object.method(), python will automatically send a reference to the object as the first argument to a method, so that the method knows specifically which method is being acted upon.
If you want to know more about the use of "self", there's a specific question related to self here: What is the purpose of self?
If I were you, I will do this:
import tkinter
root = tkinter.Tk()
var = tkinter.IntVar()
label = tkinter.Label(root, textvariable=var)
button = tkinter.Button(root, command=lambda: var.set(var.get() + 1), text='+1')
label.pack()
button.pack()
root.mainloop()
UPDATE:
But, if you insist doing this with two classes (where the first class is only a somewhat pointless interface), then I would do this:
import tkinter
class Store:
def __init__(self):
self.variable = tkinter.IntVar()
def add(self, value):
var = self.variable
var.set(var.get() + value)
return var.get()
class Main(tkinter.Tk):
def __init__(self, *args, **kwargs):
tkinter.Tk.__init__(self, *args, **kwargs)
var = Store()
self.label = tkinter.Label(self, textvariable=var.variable)
self.button = tkinter.Button(self, command=lambda: var.add(1), text='+1')
self.label.pack()
self.button.pack()
root = Main()
root.mainloop()
As you may notice, I used in both times the get() and set() methods of the IntVar (in your version I do this thru the Store interface we made), therefore you don't need to use a new variable (like self.counter) because the variable we instantiated is storing the data.
The other thing I used is a lambda expression instead of a full-blown function definition, since we only want to get the current value, add 1 to it, and store it as the new value.
And in my version, the Main class is a subclass of the original tkinter.Tk class -- that's why I call it's __init__ method inside the Main's __init__ method.