Get argument of tkinter instance in other module - python

I would like to create a program which does the following thing via a GUI:
1.) Choose a directory in which some new directories will be created
2.) give those new directories names by using an entry widget.
Since the program is rather going to be large, I would like to split up my code into several modules which causes me some problems
What I have done so far is the following thing:
I have a "main"-program using.py
import os, sys
import Tkinter
import tkFileDialog
#import own modules
import Gui
root = Tkinter.Tk()
root.withdraw()
directory='some path'
tempdir = tkFileDialog.askdirectory(parent=root, initialdir=directory,
title='Please choose a location for your directory')
n2=Gui.DirectoryCreator()
n2.mainloop()
The module Gui looks like this
import Tkinter as tk
class DirectoryCreator(tk.Tk):
text=''
def __init__(self):
tk.Tk.__init__(self)
self.label=tk.Label(self, text='Enter the name of the directory')
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Name Of Folder",width=15, command=self.on_button)
self.label.grid(row=0,column=0)
self.button.grid(row=0,column=2)
self.entry.grid(row=0,column=1)
def on_button(self):
text= self.entry.get()
self.quit()
Now, the point is that I somehow need to get the value of the string 'text' in the module using.py. If I try something like n2.text, the streing n2.text is simply empty, e.g. n2.text="". How can I get this string?

In on_button, use self.text = self.entry.get(). Just using text is like declaring a new variable instead of using the text already bound to the DirectoryCreator instance.
def on_button(self):
self.text= self.entry.get()
self.quit()

Related

why does tkinter Entry widget returns empty sting while using multiple windows but seems to work in a single window?

I am building a project in python using tkinter but when using multiple windows in tkinter, I have encountered a problem, In my Code Which you can see below, In my first window ,by pressing Open Entry Box button, I can open my second window from there i want to take value using Entry box to be store in local database but whenever I try to do so each time it returns an empty string. but it works in a single window though From This code I have given a Brief idea what i want to achieve
from tkinter import *
import pymysql
class Sample():
def __init__(self,root):
self.root = root
self.root.geometry("200x200")
S_Button = Button(root,text="Open entry Box",command=self.add).pack()
def add(self):
self.second = Tk()
self.second.geometry("500x200")
self.SRN = StringVar()
S_entry = Entry(self.second,textvariable=self.SRN).pack()
S_Button2 = Button(self.second,text="Store in Database",command=self.data).pack()
self.second.mainloop()
def data(self):
print(self.SRN.get())
#con = pymysql.connect(host="localhost",user="root",password="",database="stm")
# cur = con.cursor()
# cur.execute("insert into StudentsData value (%s),(self.SRN.get()))
# con.commit()
# con.close()'''
Your issue is you are using two instances of Tk(). Doing so means you have two seperate instances of Tk() running that essentially can't interact with each other (a great explanation of this can be found here). Hence why you aren't able to store the text from the Entry box in StringVar and it only returns an empty string. Instead, use Toplevel(). It does what you are wanting to do while only using one Tk(). Working example using Toplevel:
import tkinter as tk
class Sample():
def __init__(self,root):
self.root = root
self.root.geometry("200x200")
S_Button = tk.Button(root,text="Open entry Box",command=self.add)
S_Button.pack()
def add(self):
self.second = tk.Toplevel(self.root)
self.second.geometry("500x200")
# Widgets, StringVar
self.SRN = tk.StringVar()
S_entry = tk.Entry(self.second,textvariable=self.SRN)
S_Button2 = tk.Button(self.second,text="Store in Database",command=self.data)
# Packing widgets
S_entry.pack()
S_Button2.pack()
def data(self):
self.second.destroy()
print(self.SRN.get())
if __name__ == "__main__":
root = tk.Tk()
app = Sample(root)
root.mainloop()
Don't use from tkinter import *, it doesn't follow PEP8. Instead use import tkinter as tk or similar. Don't use button = tk.Button().pack(). Seperate the button creation from the packing, this makes it much easier to order your widgets properly when there are more of them.

How do I get the Tkinter event-listener to work?

I'm using Tkinter for the GUI of a little tool I wrote with Python. Basically I just want a callback-method to be executed as soon as the contents of an entry widget have changed. This can be done with Tkinter's own variable classes (StringVar, BooleanVar, etc. - see documentation for details: http://effbot.org/tkinterbook/variable.htm).
So I couldn't get the mechanism to work and I found a snippet online, where it works perfectly fine. Now I'm trying to figure out why my version does not work.
As you can see in the two code examples the only difference is, that I'm using the event-listening functionality inside a class, whereas the snippet I found online only demonstrates it in a straight top-to-bottom manner.
Here's what I've already tried:
I instantiated the Tk instance directly in the constructor of my GUI class - same behaviour.
I inherited directly from the Tk class (instead of Frame) - same behaviour.
I placed the callback outside of the class - same behaviour.
The only idea I have is that the problem might be scope related, which I tried to verify.
Working code snippet:
from tkinter import *
import tkinter as tk
def text_changed(*args):
print("Text changed.")
top = tk.Tk()
string_listener = StringVar()
string_listener.set("Init Text")
string_listener.trace("w", text_changed)
entry_widget = tk.Entry(top, textvariable = string_listener)
entry_widget.pack()
top.mainloop()
Not working code snippet
from tkinter import *
import tkinter as tk
root = tk.Tk()
class GUI(tk.Frame):
def __init__(self, master=root):
super(GUI, self).__init__(master)
string_listener = StringVar()
string_listener.set("Init Text")
string_listener.trace("w", self.text_changed_callback)
entry_widget = tk.Entry(master, textvariable=string_listener)
entry_widget.pack()
def text_changed_callback(self, *args):
print("Text changed.")
gui = GUI()
gui.mainloop()
Like in the working example, my code ought to print Text changed., everytime a character is either deleted from or appended to the string in the extry-widget.
The problem is that string_listener is a local variable, and python is destroying the variable when __init__ finishes running. This doesn't happen in your original code since the variable is created in the global scope.
A simple solution is to save a reference as an attribute of the class:
import tkinter as tk
root = tk.Tk()
class GUI(tk.Frame):
def __init__(self, master=root):
super(GUI, self).__init__(master)
self.string_listener = tk.StringVar()
self.string_listener.set("Init Text")
self.string_listener.trace("w", self.text_changed_callback)
entry_widget = tk.Entry(master, textvariable=self.string_listener)
entry_widget.pack()
def text_changed_callback(self, *args):
print("Text changed.")
gui = GUI()
gui.mainloop()
note: I also changed StringVar to tk.StringVar so that I could remove the redundant wildcard import of tkinter.

tkinter use same widget multple times

Since no one in the Spanish StakOverFlow hasn't answered me yet, I'm asking here. I'm from ARG. I am working on an mass document upload automation. The first widget I made with tkinter asks the user what type of document wants to upload to the web. Once this process is done, I want to throw another widget to ask the same question. The thing is I don't know how to write that code. I have not learned yet how to handle classes. And the code for my widget is a copy from an example of the web, and formatted to fit my specs.
from Tkinter import Tk, Label, Button
class DocumentTypeOption:
def __init__(self, master):
self.master = master
master.iconbitmap("bigpython.ico")
master.minsize(280,150)
master.geometry("280x150")
master.title("DOCUMENT TYPE")
self.label = Label(master, text="SELECT THE DOCUMENT TYPE")
self.label.pack()
self.tipo1_button = Button(master, text="Tipo1", command=self.opcion_tipo1)
self.tipo1_button.pack()
self.tipo2_button = Button(master, text="Tipo2", command=self.opcion_tipo2)
self.tipo2_button.pack()
def funciontipo1(self):
def subirtipo1():
"things to upload doc type1"
time.sleep(0.5)
root.destroy()
time.sleep(1)
subirtipo1()
"SHOULD THE WIDGET BE CALLED HERE?"
def funciontipo2(self):
def subirtipo1():
"things to upload doc type2"
time.sleep(0.5)
root.destroy()
time.sleep(1)
subirtipo2()
"SHOULD THE WIDGET BE CALLED HERE?""
root = Tk()
my_gui = OpcionTipoDeDocumento(root)
root.mainloop()
When one type of document was uploaded I need to throw the widget once more in order to ask the user if he wants to continue with the other type of document.
There are a few options. You could simply keep the Tkinter window open and ask the user if they want to load another file. You also are using sleep() inside a tkinter instance. You cannot use sleep() within Tkinter. There is another method called after() that is for setting up timed events to replace the use of sleep(). In this case I don't think you need a delay anyway.
Here is a simple example using a tkinter class and 1 function for the doc and 1 function to ask if you want to load another one.
# import tkinter as tk # for python 3.X
# from tkinter import messagebox # for python 3.X
import Tkinter as tk
import tkMessageBox as messagebox
class DocumentTypeOption(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.iconbitmap("bigpython.ico")
self.minsize(280,150)
self.geometry("280x150")
self.title("DOCUMENT TYPE")
self.label = tk.Label(self, text="SELECT THE DOCUMENT TYPE")
self.label.pack()
self.tipo1_button = tk.Button(self, text="Tipo1", command=lambda: self.do_stuff("Tipo1"))
self.tipo1_button.pack()
self.tipo2_button = tk.Button(self, text="Tipo2", command=lambda: self.do_stuff("Tipo2"))
self.tipo2_button.pack()
def do_stuff(self, doc_type):
# things to upload doc type1
# you can do this part with a single function as long as you check the doc type first.
print(doc_type) # just to verify buttons are working.
self.check_next(doc_type)
def check_next(self, doc_type):
x = messagebox.askyesno("DOCUMENT OPTION", "Would you like to load another {}?".format(doc_type))
if x != True:
self.destroy()
my_gui = DocumentTypeOption()
my_gui.mainloop()

Attribute error in opening file dialog in python GUI using tkinter

I am starting to learn Python and the tkinter package and I am writing a program to load a text file on the GUI window. To open the file browser, I installed the button and its necessary function as shown in the below code. The program runs but when I click on the "browse" button, I am getting an attribute error saying : "'assign_1' object has no attribute 'var_filename'". It would be great if anyone could help me with this.
from tkinter import *
from tkinter import messagebox
from tkinter import simpledialog
from tkinter import filedialog
from math import *
from numpy import *
import string
root = Tk()
def close_window_callback(root):
if messagebox.askokcancel("Quit", "Do you really wish to quit?"):
root.destroy()
class assign_1:
def __init__(self,master):
self.master = master
frame = Frame(master)
frame.pack()
self.canvas = Canvas(master,width=1000,height=1000, bg="yellow")
self.button_browse = Button(frame, text="Browse",
command=self.browse_file)
self.button_browse.pack()
self.button_load = Button(frame, text="Load")
self.button_load.pack(side = LEFT)
self.canvas.pack(expand=YES, fill=BOTH)
def browse_file(self):
self.var_filename.set(filedialog.askopenfilename(filetypes=[("allfiles","*"),("pythonfiles","*.txt")]))
filename = self.var_filename.get()
print(filename)
root.protocol("WM_DELETE_WINDOW", lambda root_window=root: close_window_callback(root_window))
assign_1(root)
root.mainloop()
Although, as Rinzler pointed out, your indentation is wrong in the code you posted, that would lead to another error (AttributeError: assign_1 instance has no attribute 'browse_file'). So I'm guessing the indentation in the code you actually use is correct.
The problem is that you try to use self.var_filename.set(...) without having defined what self.var_filename is. If you want it to be a StringVar, which seems to be the case since you use set and get, you have to initialize it. To do this you should put self.var_filename = StringVar(master) in the class' __init__ function. A small example demonstrating this:
root = Tk()
class assign_1:
def __init__(self, master):
self.master = master
self.var_filename = StringVar(master)
self.button_browse = Button(master, text="Browse", command=self.browse_file)
self.button_browse.pack()
def browse_file(self):
self.var_filename.set(filedialog.askopenfilename(filetypes=[("allfiles","*"),("pythonfiles","*.txt")]))
filename = self.var_filename.get()
print(filename)
assign_1(root)
root.mainloop()
However, from the looks of it, in your case there is no need to use a tkinter StringVar, just use a normal string variable:
root = Tk()
class assign_1:
def __init__(self, master):
self.master = master
self.button_browse = Button(master, text="Browse", command=self.browse_file)
self.button_browse.pack()
def browse_file(self):
self.filename = filedialog.askopenfilename(filetypes=[("allfiles","*"),("pythonfiles","*.txt")])
print(self.filename)
assign_1(root)
root.mainloop()
The indentation is wrong. The function browse_file you wanted to define as method of the class assign_1 (use capitalise letters to declare name of classes) is a global function as you defined it.
You have also not defined self.var_filename anywhere, so it will then give you the error:
AttributeError: 'assign_1' object has no attribute 'var_filename'
Under the function close_window_callback, you have also wrong indentation.

tkinter python create child window

Hi I am trying to create a tool that browses a time machine image with Tkinter in python. I plan on using the code from here: http://code.google.com/p/python-ttk/source/browse/trunk/pyttk-samples/dirbrowser.py?r=21 for the directory browser. I have written a start menu and upon clicking on the 'browse' button I want the directory browser to open where the user can select a file and the path is then passed back to the Label (I need to add this as its not in the directory browser code yet). Below is the code for my start menu:
#!/usr/bin/python
from Tkinter import *
import ttk
class App:
def __init__(self,master):
frame = Frame(master)
frame.pack()
self.label = Label(frame, text="Please enter file path or browse to a file")
self.label.pack(side=TOP)
self.button = Button(frame, text="OK", command=messageWindow)
self.button.pack(side=BOTTOM)
self.hi_there = Button(frame, text="Browse")
self.hi_there.pack(side=BOTTOM)
self.entry = Entry(frame, width = 30)
self.entry.pack(side=LEFT)
root = Tk()
app = App(root)
root.mainloop()
I have read that you cannot have two root frames at once with Tkinter but I am struggling to find an alternative as the directory browser also has a root frame. I am not sure if what I am doing is correct but on the button for browse I have added:
self.hi_there = Button(frame, text="Browse", command=dir)
I have put the directory browser code inside of a class and called it dir. So my thinking is that I should be calling the entire class? But then I get an error stating that the name dir is not defined. What ways can I go about getting this right?
I don't quite understand what you mean by "a time machine image", but I've got a few things that might help you: don't use the variable name dir, as that's a builtin keyword and you're bound to run into problems. If you're having trouble finding the method called dir which is inside a class, make sure you're telling it to look inside the class.
def sayHello():
print "Hello!"
class Person:
def sayHello():
print "Hello from Person"
a_person = Person()
sayHello()
##"Hello"
a_person.sayHello()
## "Hello from Person"
Calling printHello and class_instance.printHello are two different functions, and you'll want to pass class_instance.dir to the button.
I'm sure you know about them, but there are premade file dialogs to help with getting filepaths, filenames etc.
Another thing is you don't want a new root instance, you're looking for a new TopLevel instance, which is essentially the same thing as a new root, but not quite.

Categories