Splitting a TKInter function - python

I am new to programming in all languages and am having a crack at some python. I have a collection of the functions I've written so far which work so I can refer back to them for when I get stuck. In order to collect them together I have used tkinter to do this for me 'AddToCollection.py'. I can get it working when I run the .py file I created it in, however I would like to import it to any function I wish. Whenever I run the code with the AddToCollection imported it runs immediately. I have tried to split it into functions so that the new window will only open when I call a function but then it can't access the Entry to get the file name. Help would be appreciated. TL;DR how do I stop this code from running when it's imported?
from tkinter import *
from tkinter import messagebox as box
#pops up a box to confirm you want to save it
def SaveConf():
var = box.askokcancel('Save', 'Are you sure you want to save?')
if var == 1:
Save(FileName.get())
#Does the actual saving
def Save(Oldfile):
file = open(Oldfile+".py", 'r')
ToAdd = '\n#- '+ Oldfile +' -------------\n' + file.read() + '\n#-----------'
file.close()
newfile = open("/New.py", 'a+')
newfile.write(ToAdd)
newfile.close()
newwind.destroy()
#setting up items in window
#Initialising window
newwind = Tk()
newwind.title('Save a file')
#to enter filename
Frame3 = Frame()
Frame3.pack(padx=5, pady=5)
FileName = Entry(Frame3)
FileName.pack(side = LEFT)
#click button
SaveBtn2 = Button(Frame3, text = 'Save to Testicles.py', command = SaveConf)
SaveBtn2.pack(side=RIGHT,padx=2)

A common way to structure tkinter application is to subclass Tk and create your widget in the constructor. Here is an example of how you could architecture for your code. It pack your application in a class (subclass of Tk) and provide an helper function launch_app to initialise your class and run mainloop on it.
The point with __name__ == "__main__" is to segregate code executed when the script is run #> python foo.py from code executed when the module is imported. If you want to provide a default behavior when used as script, as well as the ability to use that functionality from another module, put it in a function and call this function from if __name__ == "__main__" block.
I also took the liberty to transform your code toward python coding standard (described in PEP 8)
import tkinter as tk
from tkinter import messagebox as box
#Does the actual saving
def save(oldfile):
file_ = open(oldfile+".py", 'r')
#[...]
newfile.close()
#do not destroy window here
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title('Save a file')
frame3 = tk.Frame()
frame3.pack(padx=5, pady=5)
self.filename = tk.Entry(frame3)
self.filename.pack(side=tk.LEFT)
#click button
save_btn2 = tk.Button(frame3, text='Save to Testicles.py', command=self.save_conf)
save_btn2.pack(side=tk.RIGHT, padx=2)
def save_conf(self):
var = box.askokcancel('Save', 'Are you sure you want to save?')
if var == 1:
save(self.FileName.get())
self.destroy() #<-- here come the destroy
def launch_app():
app = App()
app.mainloop()
if __name__ == "__main__":
launch_app()

If I'm understanding this correctly, you just want to import and use the functions you've written? I think part of what you're missing is this:
if __name__ == "__main__":
#setting up items in window
#Initialising window
newwind = Tk()
newwind.title('Save a file')
#to enter filename
Frame3 = Frame()
Frame3.pack(padx=5, pady=5)
FileName = Entry(Frame3)
FileName.pack(side = LEFT)
That will prevent this code from running when the file is imported as a module.

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:

Tkinter reading a file only once in loop when it needs to fo it infinitely many times

I am trying to make a script that reads constantly and updates it on a Tkinter GUI, with a simple button to refresh, but I can't seem to make it work.
I have used the while True to loop to read the file but that doesn't work, it only reads once and then it probably ends with the root.mainloop() line. Any solutions for me?
Code I have written so far:
from tkinter import *
root = Tk()
root.geometry("400x400")
while True:
with open('door1.txt', 'r') as f:
f_contents = f.read()
f.close
def something():
global my_label
my_label.config(text=f_contents)
my_label = Label(root, text="this is my first text")
my_label.pack(pady=10)
my_buttton = Button(root, text="C",command=something)
my_buttton.pack(pady=10)
root.mainloop()
Remove the while loop,
place the reading part in the something function,
remove f.close() because that is what context manager does automatically when exiting it.
Don't use * when importing modules, import what you need or import module (where module is the module name you need to import).
You don't need to use global my_label, it is already a globally accessible name and you are not changing what the name refers to.
Also you may want to put the function outside of the GUI parts so that they are kept separate and the code is more readable.
from tkinter import Tk, Label, Button
def something():
with open('door1.txt', 'r') as f:
f_contents = f.read()
my_label.config(text=f_contents)
root = Tk()
root.geometry("400x400")
my_label = Label(root, text="this is my first text")
my_label.pack(pady=10)
my_buttton = Button(root, text="C", command=something)
my_buttton.pack(pady=10)
root.mainloop()

How to call a function from a class that needs "root"

I am trying to run a script in python that is using multiprocessing. Everything works ok, but I want it to make a bit more complex. In the example below, I don't know how to call a function from the class that needs the root as an argument as the root is defined in another function. I would appreciate some help or advice.
The code that I am using is the following one:
import tkinter as tk, time
from multiprocessing import Process
from tkinter import filedialog, ttk
class Example():
def __init__ (self, parent)
self.parent = parent
canvas = tk.Canvas(self.parent)
self.button = tk.Button(self.parent, command = self.onStart)
self.pbar = ttk.Progressbar(self.parent, mode = 'indeterminate', length = 100)
def pbar_start(self):
self.pbar.pack()
self.pbar.start()
def files(self):
self.filena = filedialog.askopenfilenames(initialdir="/", title = "Select file", filetypes = (("comma separated file","*.csv"), ("all files", "*.*")))
return filena
def onStart():
self.p1 = Process(target = work)
self.p1.start()
def work():
time.sleep(5) # I put time just to show that I don't want the progressbar to start immediately
app = Example(root) # here if I call root is giving me an error as expected, but I don't know how I can do it
app.pbar_start()
file = app.filena()
#do some work
def main():
root = tk.Tk()
app = Example(root)
root.mainloop()
if __name__ == "__main__":
main()
I have tried to place work under the main but it didn't work because when I tried to click the button another window popped up.
Thank you for your help.

method automatically calls when starting the program

I'm working on a program that takes data from a spreadsheet and enters it into a contract template, then converts it into a PDF. I've created a simple GUI using tkinter, and I'd like to have a button that opens a new window, allowing users to select the .xlsx file they'd like to work from. I can't seem to figure out how to get my program to run without calling the importSpread method right when the program starts.
Here are the relevant portions of my code:
import os
from tkinter import *
from tkinter import filedialog
class GUI:
def __init__(self, master):
self.master = master
self.root = os.path.dirname(os.path.abspath(__file__)) + '\\'
self.importButton = Button(master, text = 'Import Spreadsheet',
command = self.importSpread,
height = 1, width = 20)
self.importButton.grid(row = 1, column = 2, sticky = W)
def importSpread(self):
my_filetypes = [('excel files', '.xlsx')]
fileName = filedialog.askopenfilename(parent = self.master,
initialdir = self.root,
title = "Please select a file:",
filetypes = my_filetypes)
return fileName
window = Tk()
myGUI = GUI(window)
fileName = self.importSpread()
window.mainloop()
It seems as though as soon as I instantiate the myGUI object by saying myGUI = GUI(window), the importSpread method is automatically called. I'd also like importSpread to return the fileName to be used later. How do I retain the value the method returns without calling the method itself? I want the program to start and remain static until the user presses the "Import Spreadsheet" button. I'm fairly new to python and GUIs in general, so I could be missing something basic. Any guidance would be much appreciated.

Simplest way to open a file in tkinter

trying to make a GUI with an 'open file' button. When I run the code shown below, the open file dialog opens straight away, and not when I press the button. Why? Is there a simple way to fix this that doesn't involve using classes? (I don't currently know anything about classes and am working on a time-pressured project)
from tkinter import *
interface = Tk()
def openfile():
return filedialog.askopenfilename()
button = ttk.Button(interface, text = "Open", command = openfile())
button.grid(column = 1, row = 1)
interface.mainloop()
The code is passing the return value of the openfile function call, not the function itself. Pass the function itself by removing trailing () which cause a call.
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
interface = Tk()
def openfile():
return filedialog.askopenfilename()
button = ttk.Button(interface, text="Open", command=openfile) # <------
button.grid(column=1, row=1)
interface.mainloop()

Categories