tkinter python create child window - python

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.

Related

Tkinter: How do I return the text within an Entry widget to a variable upon a Button press

This is what I tried
GUI.py
from tkinter import *
class Search:
def __init__(self, root):
self.query = None
self.root = root
# Create and draw container
self.frame = Frame(self.root)
self.frame.pack()
# Create widgets
self.search_bar = Entry(self.frame)
self.search_button = Button(self.frame, command = self.get_query, text = 'Search')
# Draw widgets
self.search_bar.pack()
self.search_button.pack()
def get_query(self):
self.query = self.search_bar.get()
def create_gui_root(): # Should I be doing this in a function in GUI.py?
root = Tk()
root.geometry('300x300')
return root
main.py
from GUI import *
def use_query_to_do_stuff(query):
print(query)
def main():
# Create GUI root instance
root = create_gui_root()
# Initialize search page
search_page = Search(root)
# I know this part is wrong, just not sure where to go from here
# It runs the search_submit method on start instead of waiting for the button to be pressed
query = search_page.get_query()
use_query_to_do_stuff(query)
root.mainloop() # Also is this in the right place?
main()
I want the program to wait until I press the button, then assign the text inside the Entry widget to a variable.
I started off with a fully working text-based program in main.py and I'm trying to add a GUI.
I'm trying to learn how to split the logic and GUI into separate Python files so I import the Search class into the main.py file and I need GUI.py to return the search query back to main.py when the button is pressed.
I'm also unsure if I'm going about using classes and functions and separating logic from GUI the right way. Should I be using a bunch of functions instead of a class? Or am I using the class wrong?
Are the root = Tk() and root.mainloop() lines even in the right place?
I thought of importing main.py into GUI.py, creating a method in Search() to call the function use_query_to_do_stuff() from main.py while passing self.search_bar.get() as a parameter directly, then binding that method to the self.search_button command. However, I feel like cross-importing your main.py file into your GUI.py is counter-intuitive, I kind of feel like all roads should 'lead back' to the main.py file, if that makes sense. Then again, maybe I'm completely lost.
I made main.py first and it ran as intended, but now that I'm trying to implement a GUI I feel like I'm going about everything slightly incorrectly.
Add Label in GUI.
Add label.configure in get_query(self) function
.
Snippet code:
# Create widgets
self.search_bar = Entry(self.frame)
self.search_button = Button(self.frame, command = self.get_query, text = 'Search')
self.label = Label(self.frame)
# Draw widgets
self.search_bar.pack()
self.search_button.pack()
self.label.pack(padx=15, pady=35)
def get_query(self):
self.query = self.search_bar.get()
self.label.configure(text=self.query)
self.search_bar.delete(0, END)
print(self.query)
Screenshot before:
Screenshot after:

why is my code returning searchBar entry widget is not defined

I am trying to write an application in python tkinter.
the code I wrote is returning that the searchBar variable is not defined.
code:
from Scripts import acquire, widgets
from tkinter import *
from tkinter import messagebox
def run():
print("starting main")
window = Tk()
window.title("PyData Library Database Manager")
window.geometry("1080x760")
searchFrame = Frame(window)
searchBar = Entry(searchFrame).grid(row=0, column=0)
def search():
global searchBar
searchBarStr = searchBar.get()
acquire.search(searchBarStr)
searchGo = Button(searchFrame, text="Go", command=search).grid(row=0, column=1)
searchFrame.grid(row=0, column=0)
window.mainloop()
and some more code:
# acquire.py of PyData
# used to get data and search Data folder
def search(searchStr):
print("searching for: %s" % searchStr)
I am trying to write a database application. the search function is supposed get the search data from the acquire search function, which is supposed to look at a Data directory structure, which contains folders Data and Images. In Data, we have text files for the entry data. I can't search for the right file if I can't get the search results: when I click "go", it returns an searchBar is not defined error.
Any ideas?

Why doesn't Tkinter Entry's get function return anything?

I'm trying to use two dialogs to get manual input, and then work with that data.
All source I've found claim I should use the get() function, but I wrote a simple mini program yet, and I can't make the second dialog work.
I hope someone can tell me what I'm doing wrong. Here's a file:
from tkinter import *
from tkinter.filedialog import askdirectory
from tkinter import messagebox
def getpath():
def selectPath():
path_ = askdirectory()
path.set(path_)
root = Tk()
root.title('select path')
path = StringVar()
def close():
if(path.get()==""):
messagebox.showinfo("","nothing")
else:
root.withdraw()
root.quit()
Label(root,text="path:").grid(row=0,column=0)
Entry(root,textvariable = path).grid(row=0,column=1)
Button(root,text="select",command=selectPath).grid(row=0,column=2)
Button(root,text="enter",command=close).grid(row=0,column=3)
root.mainloop()
return path.get()
def getname():
def get_need_name():
name = need_name.get()
print('hereherehere'+name) #does not work
root = Tk()
root.title('select name')
need_name = StringVar()
Label(root,text="name:").grid(row=0,column=0)
entry = Entry(root,bd=10,textvariable=need_name)
entry.grid(row=0,column=1)
Button(root,text="enter", font=16, bg="silver", relief='groove', command=get_need_name).grid(row=0,column=2)
root.mainloop()
return name.get()
def main():
path = getpath()
print("mypath:"+path)
print('******************')
print('done!')
name = getname()
print("myname:"+name)
if __name__ == '__main__':
main()
This give me two dialogs I can type in, but only the first dialog works.
The reason is that you are creating multiple instances of Tk, and you don't destroy the instances when you are done with them. This causes two problems. First is a memory leak. Each time you call one of these functions you create a new window and a new tcl interpreter.
The second problem is that the first root window becomes the default window when a root isn't specified. When you create a StringVar in the second function, because you didn't specify which root window it belongs to it will be assigned to the first root window. When you use it as the target of textvariable in a second instance of Tk, tkinter thinks the variable doesn't exist so it creates a new one for the second window. However, your reference is still to the one created in the first root window and is never updated by user input in the second window.
Confusing? Yes, which is why you typically shouldn't be creating more than one instance of Tk.
To make your code work with as few changes as possible and to remove the memory leak caused by not destroying the windows, you can change the last couple of lines in your method to look like the following. This destroys the root window when you are done with it, removing the memory leak and the side effect of having more than one root window.
root = Tk()
...
root.mainloop()
value = path.get()
root.destroy()
return value
The second dialog should look similar:
root = Tk()
...
root.mainloop()
value = name.get()
root.destroy()
return value
This retrieves the value after mainloop exits but before the underlying tcl interpreter is deleted, and then destroys the window and its tcl interpreter.
The next time you create an instance of Tk, that instance will become the new default root, and any new instance of StringVar will go to that root.
Another solution would be to specify the master for the StringVar instance, but that leaves the memory leak in place so it's only half of a solution.
Arguably a better solution is to create a single root window, and either reuse it or create instances of Toplevel rather than Tk. Effbot has some decent documentation on how to create a modal window with wait_window.
After some testing, and googling the root.quit() is the problem
here is a working example for you to look at and mess with.
from tkinter import *
from tkinter.filedialog import askdirectory
from tkinter import messagebox
root = Tk()
path = StringVar()
def select_path():
#uses the return value to set no need to create an additional variable
path.set(askdirectory())
def close():
if path.get() == "":
messagebox.showinfo("","Please select path")
else:
get_name_frame.tkraise()
def get_name():
print("hereherehere", name.get())
get_path_frame = Frame(root)
get_path_frame.grid(row=0, column=0, sticky="nsew")
Label(get_path_frame,text="path:").grid(row=0,column=0)
Entry(get_path_frame,textvariable = path).grid(row=0,column=1)
Button(get_path_frame,text="select",command=select_path).grid(row=0,column=2)
Button(get_path_frame,text="enter",command=close).grid(row=0,column=3)
get_name_frame = Frame(root)
get_name_frame.grid(row=0, column=0,sticky="nsew")
Label(get_name_frame, text="name: ").grid(row=0, column=0)
name = StringVar()
entry = Entry(get_name_frame, bd=10, textvariable = name)
entry.grid(row=0, column=1)
Button(get_name_frame,text="enter", font=16, bg="silver", relief='groove', command=get_name).grid(row=0,column=2)
get_path_frame.tkraise()
root.mainloop()

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()

Get argument of tkinter instance in other module

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()

Categories