How to store a value from Tkinter.Button on a var - python

I'm novice with python.
So i'm trying to make a GUI, when i click the button will open a directory selector(see on code 1),and i need storage the path from selection that i expected receive on "command" in a var, so i can use the directory path for another things.
Code 1
css.py
...
def selectFolder():
path = customtkinter.filedialog.askdirectory()
return path
Code 2
css.py
def button(root,txt,event = any):
button = customtkinter.CTkButton(master = root,text=txt,command = event)
return button;
...
Main code
main.py
root = css.root()
frame = css.frame(root)
frame.pack(pady=20, padx=60, fill='both',expand=True)
label = css.label(frame,'Tecverde - Engenharia S.A','Roboto',10)
label.pack(pady=12, padx=10)
path = css.button(frame,"Selecione a pasta",css.selectFolder)
path.pack()
root.mainloop()

You are using custontkinter, so all your widgets are 'CTk...'
Your button widgets can contain 'commands', my suggestion is, inside the function passed to the 'ask' command you can use tkinter's filedialog module, and use the output as needed.
This is an example of using Stringvar, a tkinter dynamic variable, whenever you use the command set('') in it you will modify its value for the entire code, and to get the value you can use .get () anywhere in the code.
Note that the debug label text changes after you dynamically select a directory.
from tkinter import filedialog
import customtkinter as css
root = css.CTk()
frame = css.CTkFrame(root)
frame.pack(pady=20, padx=60, fill='both', expand=True)
label = css.CTkLabel(frame, text='Tecverde - Engenharia S.A', font=('Roboto', 10))
label.pack(pady=12, padx=10)
def ask():
dir = filedialog.askdirectory()
path_var.set(dir)
path_var = css.StringVar(value='Empty Path')
path_button = css.CTkButton(frame, text="Selecione a pasta",
command=ask)
path_button.pack()
debug_label = css.CTkLabel(root, textvariable=path_var)
debug_label.pack()
root.mainloop()
In the second example, now using classes, self.path_var can be used anywhere in your code, even with a Stringvar as in the previous example, but now it has more options.
from tkinter import filedialog
import customtkinter as css
class App(css.CTk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
frame = css.CTkFrame(self)
frame.pack(pady=20, padx=60, fill='both', expand=True)
label = css.CTkLabel(frame, text='Tecverde - Engenharia S.A', font=('Roboto', 10))
label.pack(pady=12, padx=10)
self.path_var = 'Empty path'
path_button = css.CTkButton(frame, text="Selecione a pasta",
command=self.ask)
path_button.pack()
self.debug_label = css.CTkLabel(self, text=self.path_var)
self.debug_label.pack()
def ask(self):
dir = filedialog.askdirectory()
self.path_var = dir
self.debug_label.configure(text=dir)
if __name__ == '__main__':
app = App()
app.mainloop()

Related

Store the directory address using filedialog.askdirectory() in tkinter, Python

In a part of my code, I am going to get the directory of files from user. To do this there is an Open Button and Entry bar which shows the address like the picture below:
Open Button is bound to filedialog.askdirectory() and user could navigate through directories. The class below do this task:
class Open_File:
def __init__(self, master):
self.master = master
self.file_path = ''
self.open_button = tk.Button(master,
text = 'Open',
command = self.open_file)
self.v = tk.StringVar(master)#, value = self.file_path)
self.path_entry = tk.Entry(master, width=40, bg = 'white', textvariable=self.v)
self.path_entry.xview_moveto(1)
def open_file(self):
self.file_path =filedialog.askdirectory()
if self.file_path: #check if file path is not None or empty
self.v.set(self.file_path)
self.path_entry.xview_moveto(1)
def return_path(self):
return self.v.get()
Although the address displayed correctly in the Entry bar, I could not store it in a variable so I can use it in other parts of the code. For example, this code just returns an empty string for the address:
import tkinter as tk
root = tk.Tk()
open_file = Open_File(root)
open_button = open_file.open_button
path_entry = open_file.path_entry
slices_dir = open_file.return_path()
open_button.pack(side=tk.LEFT)
path_entry.pack(side=tk.LEFT)
root.mainloop()
print(slices_dir)
I am on windows 10, my Python version is 3.8.3 (Anaconda), and the Tkinter version is 8.6
try using Global variable, below simple example :
import tkinter as tk
from tkinter import filedialog as fd
def callback():
global name
name= fd.askopenfilename()
print(name)
errmsg = 'Error!'
tk.Button(text='Click to Open File',
command=callback).pack(fill=tk.X)
tk.mainloop()

How to save askdirectory result in a variable I can use using tkinter with OOP?

I have ran into some trouble.
I'm quite new to OOP and working with tkinter and GUI's in general.
I have managed to find some code on the Internet and meshed it all together to create something and I'm nearly where I want to be.
So what I want is some help figuring this out.
How can I assign results of askdirectory to a variable I can use elsewhere?
# coding=utf-8
import tkinter as tk
from tkinter import font as tkfont
from tkinter import filedialog
class MainApp(tk.Tk):
....
class SelectFunction(tk.Frame):
....
class FunctionChangeName(tk.Frame):
....
a = Gui(self)
# this gets me the askdirectory but how to add it to a variable?
Above is the call to run askdirectory code, and it works, just need to find out how to save it to a variable so I can use it, I have tried to print it in several ways, but all I get is something along the lines .!frame.!functionchangename.!gui.
class SelectDir:
def __init__(self, container, title, initial):
self.master = container
self.initial = initial
self.selected = initial
self.options = {'parent': container,'title': title,'initialdir':initial,}
def show(self):
result = filedialog.askdirectory()
if result:
self.selected = result
def get(self):
return self.selected
class Gui(tk.Frame):
def __init__(self, container):
tk.Frame.__init__(self, container)
frame = tk.Frame(container)
frame.pack()
self.seldir = SelectDir(self, "Select directory", "D:\\MyPgm\\Python\\Tiles_8")
button = tk.Button(frame, text="Select directory", command=self.select_dir)
button.grid(column=0, row=0)
self.act_dir = tk.StringVar()
self.act_dir.set("D:\\MyPgm\\Python\\Tiles_8")
entry = tk.Entry(frame, textvariable=self.act_dir, width=30)
entry.grid(column=0, row=1)
def select_dir(self):
self.seldir.show()
self.act_dir.set(self.seldir.get())
# or
# result = seldir.show()
# self.act_dir.set(result)
if __name__ == "__main__":
app = MainApp()
app.mainloop()
I had an idea:
example, if you have f inside a function, you can make it global to have access as variable
def print_path():
# select working directory
global f #make f global to access the path
f = filedialog.askdirectory(parent=root, initialdir="/", title='Select Dir')

Tkinter Button - How to get the values and display it using a single module for different Buttons

I am creating a GUI, where I have placed three buttons for selecting a file. When I click the button it browse for a file and display the selected file path and I am trying to do it with a single browse function since the operation is same. I am bit confused to how I should proceed . Can anyone please help me out.
Thanks in advance.
Here is my code:
Browse_Files.py
from tkinter import filedialog
def Browse_File():
global Bfilepath
Bfilepath = filedialog.askopenfilename(filetypes = (("Please select the required file", "*"), ("All files", "*")))
return Bfilepath
Main.py
from tkinter import *
import sys
import fileinput
import Browse_Files
root = Tk()
root.geometry('1400x800')
root.title('Dr Configuration')
Heading = Label(root, font=('Times New Roman',50,'bold'),text = "Arxml
Configuration Tool").place(x=300,y=25)
BasepathLabel = Label(root,font=('Times New Roman',20,'bold'),text = " Base
arxml").place(x=200,y=150)
NewpathLabel= Label(root,font=('Times New Roman',20,'bold'),text = "
New/Unedited arxml").place(x=200,y=250)
InterfaceLabel = Label(root,font=('Times New Roman',20,'bold'),text = "
Interface_File").place(x=200,y=350)
BpathtoDisp = StringVar(None)
BpathEntry = Entry(root,font=('Times New Roman',18),textvariable=
BpathtoDisp,justify='left',width=48).place(x=500,y=150)
NpathtoDisp = StringVar(None)
NpathEntry = Entry(root,font=('Times New Roman',18),textvariable=
NpathtoDisp,justify='left',width=48).place(x=500,y=250)
InterPathtoDisp = StringVar(None)
InterPathEntry = Entry(root,font=('Times New Roman',18),textvariable=
NpathtoDisp,justify='left',width=48).place(x=500,y=350)
button1 = Button(root,text="...",height=1,width=3,command=lambda:Browse_Files.Browse_File()).place(x=1100,y=150)
button2 = Button(root,text="...",height=1,width=3,command=lambda:Browse_Files.Browse_File()).place(x=1100,y=250)
button3 = Button(root,text="...",height=1,width=3,command=lambda:Browse_Files.Browse_File()).place(x=1100,y=350)
root.mainloop()
You could create a class that contains the functionality to have a button, open a file dialog, store it and make it available to other parts of the program.
I have a similar widget that I use in many different programs
from tkinter import *
from tkinter import filedialog
class FileSelect(Frame):
def __init__(self,master,label="",**kw):
Frame.__init__(self,master)
self.configure(**kw)
self.file = StringVar()
self.Label = Label(self, text=label)
self.Label.config(width=10,anchor=E)
self.filenamebox = Entry(self,text=self.file)
self.filenamebox.config(width=50)
self.btnBrowse = Button(self,text='Browse',command=self.browse_file)
self.btnBrowse.config(width=10)
self.Label.grid(row=0,column=0,pady=5,sticky=E)
self.filenamebox.grid(row=0,column=1,pady=5)
self.btnBrowse.grid(row=0,column=2,pady=5,padx=5)
def browse_file(self):
filename = filedialog.askopenfilename(filetypes=[('All File','*.*')])
self.file.set(filename)
def get_filename(self):
return self.file.get()
def main():
root = Tk()
root.title("Example Widget")
fileSelect1 = FileSelect(root,label="My File 1")
fileSelect1.grid()
fileSelect2 = FileSelect(root,label="My File 2")
fileSelect2.grid()
root.mainloop()
if __name__ == '__main__':
main()
In your code if you want the value of the file selected use
fileSelect1.get_filename()
EDIT: I've created a new version that is only a Button 'with memory' to remember which item was selected by the file dialog. This will allow you to place using the place geometry manager (which I don't recommend). Its not complete but you should get the idea.
from tkinter import *
from tkinter import filedialog
class FileSelectButton(Button):
def __init__(self,master,**kw):
Button.__init__(self,master,text='Browse',command=self.browse_file,width=10,**kw)
self.file = StringVar()
def browse_file(self):
filename = filedialog.askopenfilename(filetypes=[('All File','*.*')])
self.file.set(filename)
def get_filename(self):
return self.file.get()
def main():
root = Tk()
root.geometry('1400x800')
root.title("Example Widget")
Label(root, font=('Times New Roman',50,'bold'),text = "Label1").place(x=200,y=150)
fileSelect1 = FileSelectButton(root)
fileSelect1.place(x=500,y=150)
Label(root, font=('Times New Roman',50,'bold'),text = "Label2").place(x=200,y=250)
fileSelect2 = FileSelectButton(root)
fileSelect2.place(x=500,y=250)
root.mainloop()
if __name__ == '__main__':
main()

import user input from TKinter button to a different .py module

On ticket.py module i have a Tkinter frame; value = Entry(frame), and on the same module I have a button where command=exoutput;
def exoutput():
print value.get()
I would like to import the value to othermodule.py on the button command/ when I hit the button.
Currently when I import, the print is generated from the exoutput() function, rather than from the othermodule.py file.
Suggestions on how to print the value on othermodule.py?
# ticket.py
from Tkinter import*
window = Tk()
window.title("Entry")
frame = Frame(window)
value = Entry(frame)
def exoutput():
print value.get()
btnStage = Button(frame, text='ACTION', command=exoutput)
btnStage.pack(side=RIGHT, padx=2)
value.pack(side=LEFT)
frame.pack(padx=10, pady=10)
window.resizable(0, 0)
window.mainloop()
The other file, I've tried something like this;
# othermodule.py
import ticket
usersinput = ticket.value.get()
print usersinput
I think you either need multi-threading, or swap your file contents. Nothing runs after mainloop until the Tk instance is destroyed.
Alternatively, you could structure your ticket.py in OOP, and fetch GUI object from it by your othermodule to use it as you please. Below is an example:
ticket.py:
#import tkinter as tk
import Tkinter as tk
class Window(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Entry")
self.resizable(False, False)
# create widgets
self.frame = tk.Frame(self)
self.value = tk.Entry(self.frame)
self.btn_stage = tk.Button(self.frame, text="ACTION")
# widgets layout
self.frame.pack(padx=10, pady=10)
self.btn_stage.pack(side='right', padx=2)
self.value.pack(side='left')
if __name__ == "__main__":
root = Window()
root.mainloop()
and othermodule.py:
import ticket
def put():
global my_var_in_othermodule
my_var_in_othermodule = ticket_GUI.value.get()
ticket_GUI.destroy()
my_var_in_othermodule = ""
ticket_GUI = ticket.Window()
ticket_GUI.btn_stage['command'] = put
ticket_GUI.mainloop()
print(my_var_in_othermodule)
input()

How to run a code whenever a Tkinter widget value changes?

I'm using Python and Tkinter, and I want the equivalent of onchange event from other toolkits/languages. I want to run code whenever the user updates the state of some widgets.
In my case, I have many Entry, Checkbutton, Spinbox and Radiobutton widgets. Whenever any one of these changes, I want to run my code (in this case, update a text box on the other panel).
(just remember that user may interact with those widgets using either mouse or keyboard, and even using Ctrl+V to paste text)
I think the correct method is to use trace on a tkinter variable that has been assigned to a widget.
For example...
import tkinter
root = tkinter.Tk()
myvar = tkinter.StringVar()
myvar.set('')
mywidget = tkinter.Entry(root,textvariable=myvar,width=10)
mywidget.pack()
def oddblue(a,b,c):
if len(myvar.get())%2 == 0:
mywidget.config(bg='red')
else:
mywidget.config(bg='blue')
mywidget.update_idletasks()
myvar.trace('w',oddblue)
root.mainloop()
The w in trace tells tkinter whenever somebody writes (updates) the variable, which would happen every time someone wrote something in the Entry widget, do oddblue. The trace always passes three values to whatever function you've listed, so you'll need to expect them in your function, hence a,b,c. I usually do nothing with them as everything I need is defined locally anyway. From what I can tell a is the variable object, b is blank (not sure why), and c is the trace mode (i.e.w).
For more info on tkinter variables check this out.
How I would solve this in Tcl would be to make sure that the checkbutton, spinbox and radiobutton widgets are all associated with an array variable. I would then put a trace on the array which would cause a function to be called each time that variable is written. Tcl makes this trivial.
Unfortunately Tkinter doesn't support working with Tcl arrays. Fortunately, it's fairly easy to hack in. If you're adventurous, try the following code.
From the full disclosure department: I threw this together this morning in about half an hour. I haven't actually used this technique in any real code. I couldn't resist the challenge, though, to figure out how to use arrays with Tkinter.
import Tkinter as tk
class MyApp(tk.Tk):
'''Example app that uses Tcl arrays'''
def __init__(self):
tk.Tk.__init__(self)
self.arrayvar = ArrayVar()
self.labelvar = tk.StringVar()
rb1 = tk.Radiobutton(text="one", variable=self.arrayvar("radiobutton"), value=1)
rb2 = tk.Radiobutton(text="two", variable=self.arrayvar("radiobutton"), value=2)
cb = tk.Checkbutton(text="checked?", variable=self.arrayvar("checkbutton"),
onvalue="on", offvalue="off")
entry = tk.Entry(textvariable=self.arrayvar("entry"))
label = tk.Label(textvariable=self.labelvar)
spinbox = tk.Spinbox(from_=1, to=11, textvariable=self.arrayvar("spinbox"))
button = tk.Button(text="click to print contents of array", command=self.OnDump)
for widget in (cb, rb1, rb2, spinbox, entry, button, label):
widget.pack(anchor="w", padx=10)
self.labelvar.set("Click on a widget to see this message change")
self.arrayvar["entry"] = "something witty"
self.arrayvar["radiobutton"] = 2
self.arrayvar["checkbutton"] = "on"
self.arrayvar["spinbox"] = 11
self.arrayvar.trace(mode="w", callback=self.OnTrace)
def OnDump(self):
'''Print the contents of the array'''
print self.arrayvar.get()
def OnTrace(self, varname, elementname, mode):
'''Show the new value in a label'''
self.labelvar.set("%s changed; new value='%s'" % (elementname, self.arrayvar[elementname]))
class ArrayVar(tk.Variable):
'''A variable that works as a Tcl array variable'''
_default = {}
_elementvars = {}
def __del__(self):
self._tk.globalunsetvar(self._name)
for elementvar in self._elementvars:
del elementvar
def __setitem__(self, elementname, value):
if elementname not in self._elementvars:
v = ArrayElementVar(varname=self._name, elementname=elementname, master=self._master)
self._elementvars[elementname] = v
self._elementvars[elementname].set(value)
def __getitem__(self, name):
if name in self._elementvars:
return self._elementvars[name].get()
return None
def __call__(self, elementname):
'''Create a new StringVar as an element in the array'''
if elementname not in self._elementvars:
v = ArrayElementVar(varname=self._name, elementname=elementname, master=self._master)
self._elementvars[elementname] = v
return self._elementvars[elementname]
def set(self, dictvalue):
# this establishes the variable as an array
# as far as the Tcl interpreter is concerned
self._master.eval("array set {%s} {}" % self._name)
for (k, v) in dictvalue.iteritems():
self._tk.call("array","set",self._name, k, v)
def get(self):
'''Return a dictionary that represents the Tcl array'''
value = {}
for (elementname, elementvar) in self._elementvars.iteritems():
value[elementname] = elementvar.get()
return value
class ArrayElementVar(tk.StringVar):
'''A StringVar that represents an element of an array'''
_default = ""
def __init__(self, varname, elementname, master):
self._master = master
self._tk = master.tk
self._name = "%s(%s)" % (varname, elementname)
self.set(self._default)
def __del__(self):
"""Unset the variable in Tcl."""
self._tk.globalunsetvar(self._name)
if __name__ == "__main__":
app=MyApp()
app.wm_geometry("400x200")
app.mainloop()
You have three different ways of doing the same:
1) Use the built-in "command" configuration, like the one you use on buttons
import tkinter as tk
from tkinter import messagebox as tk_messagebox
def spinbox1_callback():
tk_messagebox.showinfo("Spinbox callback", "You changed the spinbox.")
if __name__ == "__main__":
master = tk.Tk()
spinbox1 = tk.Spinbox(master, from_=0, to=10, command=spinbox1_callback)
spinbox1.pack()
tk.mainloop()
2) Use the event bindings to capture specific events:
http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
import tkinter as tk
from tkinter import messagebox as tk_messagebox
root = tk.Tk()
def callback(event):
tk_messagebox.showinfo("clicked at", event.x, event.y)
frame = tk.Frame(root, width=100, height=100)
frame.bind("<Button-1>", callback)
frame.pack()
root.mainloop()
3) "trace" changes on a tkinter variable classes, so if your widget uses a StringVar, BooleanVar, IntVar, or DoubleVar in the textvariable parameter, you will get a callback once it gets updated. https://effbot.org/tkinterbook/variable.htm
import tkinter as tk
from tkinter import messagebox as tk_messagebox
if __name__ == "__main__":
master = tk.Tk()
widget_contents = tk.StringVar()
widget_contents.set('')
some_entry = tk.Entry(master,textvariable=widget_contents,width=10)
some_entry.pack()
def entry1_callback(*args):
tk_messagebox.showinfo("entry callback", "You changed the entry %s" % str(args))
some_entry.update_idletasks()
widget_contents.trace('w',entry1_callback)
tk.mainloop()
It's quite late, but yet, somebody found something that might be useful.
The whole idea comes from #bryan Oakley's post
If I understand well, the main problem is to detech Entry widget's . To detect it in spinbox, Checkbutton and Radiobutton you can use command options when creating widget.
To catch the <onChange> in Entry widget you can use Bryan`s approach using Tcl, which generates this event. As I said, this is not my solution, I've only changed it slightly for this case.
For example:
import tkinter as tk
from tkinter import ttk
def generateOnChange(obj):
obj.tk.eval('''
proc widget_proxy {widget widget_command args} {
# call the real tk widget command with the real args
set result [uplevel [linsert $args 0 $widget_command]]
# generate the event for certain types of commands
if {([lindex $args 0] in {insert replace delete}) ||
([lrange $args 0 2] == {mark set insert}) ||
([lrange $args 0 1] == {xview moveto}) ||
([lrange $args 0 1] == {xview scroll}) ||
([lrange $args 0 1] == {yview moveto}) ||
([lrange $args 0 1] == {yview scroll})} {
event generate $widget <<Change>> -when tail
}
# return the result from the real widget command
return $result
}
''')
obj.tk.eval('''
rename {widget} _{widget}
interp alias {{}} ::{widget} {{}} widget_proxy {widget} _{widget}
'''.format(widget=str(obj)))
def onEntryChanged(event = None):
print("Entry changed")
def onCheckChanged(event = None):
print("Check button changed")
def onSpinboxChanged(event = None):
print("Spinbox changed")
def onRadioChanged(event = None):
print("Radio changed")
if __name__ == '__main__':
root = tk.Tk()
frame = tk.Frame(root, width=400, height=400)
entry = tk.Entry(frame, width=30)
entry.grid(row=0, column=0)
generateOnChange(entry)
entry.bind('<<Change>>', onEntryChanged)
checkbutton = tk.Checkbutton(frame, command=onCheckChanged)
checkbutton.grid(row=1, column=0)
spinbox = tk.Spinbox(frame, width=100, from_=1.0, to=100.0, command=onSpinboxChanged)
spinbox.grid(row=2, column=0)
phone = tk.StringVar()
home = ttk.Radiobutton(frame, text='Home', variable=phone, value='home', command=onRadioChanged)
home.grid(row=3, column=0, sticky=tk.W)
office = ttk.Radiobutton(frame, text='Office', variable=phone, value='office', command=onRadioChanged)
office.grid(row=3, column=0, sticky=tk.E)
frame.pack()
root.mainloop()
Of course modify it to create different callback for plenty of instances (as you mentioned in the question) is easy now.
I hope somebody will find it useful.
So far, I have not encountered any thing equivalent of onChange in Tkinter.
Widgets can be bound to the various events and I have done that explicitly.
http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm

Categories