I am relatively new to programming and I noticed that coding everything in 2 files (settings and main) gets very messy as your code grows.
However, when I split my code into many files, I run into issues where I cannot import fileB.py intro FileA.py and use variables or widgets from file A inside my file B (I get undefined names error).
I am using tkinter for the UI, so my main file is the tk loop (main.py). Each button refers to functions in different files. It works well, until my function includes button states or entry text.
This example is with tkinter but I run into this problem on many occasions, because of my code structure I believe.
File A (main.py)
import FileB
import tkinter
from tkinter import Checkbutton, Tk, Entry, BooleanVar
root = Tk() # initialize blank window
root.geometry("500x500")
# text entry
E1 = Entry(root, bd=5, width = 8)
E1.grid(row=0, column=1)
# Checkbox
CB_var = BooleanVar()
CB = Checkbutton(root, text="Get text", variable=CB_var, command=FileB.get_text() )
CB.grid(row=0, column=2)
root.mainloop()
FileB (FileB.py)
def get_text():
if CB.var == True:
entry_text = E1.get()
E1.config(state=DISABLED)
print(entry_text)
E1.delete(0, END)
elif CB.var == False:
E1.config(state=NORMAL)
print("Checkbox not selected")
Since E1 is defined before my function is called, I would expect my function to be able to change the state of E1, get its text and empty the text; as if the function was in my main.py.
The actual output is undefined name error since E1 is not a variable in my FileB.
As FileB is imported by main.py, FileB cannot access objects in main.py. You need to pass the objects in main.py via function parameters.
Suggest to put your widgets into a class and pass the instance of the class to FileB.get_text() function.
File A (main.py)
from tkinter import Checkbutton, Tk, Entry, BooleanVar
import FileB
class GUI:
def __init__(self, root=None):
# text entry
self.E1 = Entry(root, bd=5, width = 8)
self.E1.grid(row=0, column=1)
# Checkbox
self.CB_var = BooleanVar()
self.CB = Checkbutton(root, text="Get text", variable=self.CB_var, command=lambda: FileB.get_text(self))
self.CB.grid(row=0, column=2)
root = Tk() # initialize blank window
root.geometry("500x500")
GUI(root)
root.mainloop()
File B (FileB.py)
from tkinter import DISABLED, NORMAL, END
def get_text(gui):
if gui.CB_var.get() == True:
entry_text = gui.E1.get()
gui.E1.config(state=DISABLED)
print(entry_text)
gui.E1.delete(0, END)
else:
gui.E1.config(state=NORMAL)
print("Checkbox not selected")
Related
def entering(a):
value=entry1.get() #entry1.get()is used to get values which entered in entry box
label1=Label(root, text =value, height=10)
label1.pack()
entry1.delete(0, END) # used to clear entry box
root.bind('<Return>',entering)
how do I remove the widget that I created in the function call entering?
I know about the destroy function. I don't want to destroy it after a particular time.
I want to destroy it or overwrite it into the widget when I call the function again
I think this is what you expect:
import tkinter as tk
root = tk.Tk()
myentry = tk.Entry(root)
myentry.pack()
var = tk.StringVar(root)
mylabel = tk.Label(root, textvariable= var)
mylabel.pack()
def entering(event):
text = myentry.get()
var.set(text)
myentry.bind('<Return>', entering)
root.mainloop()
Here is my code. It works how I want it to but I have always been told it is poor coding practice to use global variables and that they can cause problems, although I cannot figure out how to get the labels to change without using them. Any help is appreciated.
import tkinter as tk
from tkinter import filedialog, Text
import os
status = 'Start'
startStopBG = 'light green'
def main():
root = configure_screen()
root.mainloop()
def configure_screen():
root = tk.Tk()
root.title('APP')
root.config(bg='snow3')
root.minsize(700, 700)
browse_button = tk.Button(root, text='Browse', width='10', command=browse)
browse_button.place(x=605, y=10)
global text
text = tk.Text(root, height=1.3, width=73)
text.insert(tk.END, 'Enter Path to Storage HERE')
text.place(x=10, y=13)
global start_stop
start_stop = tk.Button(root, height=1, width=12, text=status, bg=startStopBG,
font=('Helvetica', '40'), command=start_scanning)
start_stop.pack(pady=50)
return root
def browse():
path = filedialog.askdirectory(initialdir='/', title='Select where you want to save your file')
text.delete('1.0', tk.END)
text.insert(tk.END, path)
def start_scanning():
global status
global startStopBG
global start_stop
if status == 'Start':
status = 'Stop'
startStopBG = 'red'
else:
status = 'Start'
startStopBG = 'light green'
start_stop.config(text=status, bg=startStopBG)
if __name__ == '__main__':
main()
First of all you can use a class to your main window and instead of global variables you use class variables. Second I would recommend you to use tkinter variables to store important data from widget since the path and the status. For example, if you use text=tk.StringVar() you can set or get the value from text with text.set('value') or text.get(). Tkinter variables are object and if you define an object in your main you can access it as a global variable inside functions without the need of using global. However, in your code, to use text as a StringVar you should change the Text widget for an Entry widget, which is more appropriated since path is a single entry value and not a text. The same way you can change your start_stop button to a Checkutton and it will make the color change unnecessary since you can define colors for background and selectcolor.
The code bellow includes all changes I suggest here:
import tkinter as tk
from tkinter import filedialog, Text
import os
class Main(tk.Tk):
def __init__(self):
super(Main, self).__init__()
self.title('APP')
self.config(bg='snow3')
self.minsize(700, 700)
self.status = tk.IntVar()
self.text = tk.StringVar(self, value='Enter Path to Storage HERE')
browse_button = tk.Button(self, text='Browse', width='10',
command=self.browse)
browse_button.place(x=605, y=10)
tk.Entry(self, width=73, textvariable=self.text).place(x=10, y=13)
self.start_stop = tk.Checkbutton(self, height=1, width=12, text="start",
font=('Helvetica', '40'), indicator=False,
bg='light green', selectcolor='red',
variable=self.status, command=self.start_scanning)
self.start_stop.pack(pady=50)
def browse(self):
path = filedialog.askdirectory(initialdir='/', title='Select where you want to save your file')
self.text.set(path)
def start_scanning(self):
if self.status.get():
self.start_stop.config(text='stop')
else:
self.start_stop.config(text='start')
if __name__ == '__main__':
Main().mainloop()
As I understood you want to change label
Try This:
import tkinter as tk
def main():
def change_label_text():
mylabel.config(text="yeee My Text has Been changed")
def change_button_text():
mybutton.config(text="yee Button text has been changed")
root = tk.Tk()
root.title('Change Label')
root.config(bg='snow3')
root.geometry('400x300')
mybutton = tk.Button(root, text='Press Me To Change Button Text', command=change_button_text)
mybutton.pack(side='top')
mylabel = tk.Label(root, text='Press The Button Above To Change My text')
mylabel.pack(side='bottom')
mybutton2 = tk.Button(root, text="Press me", command=change_label_text)
mybutton2.pack(side='bottom')
root.mainloop()
main()
By making functions inside the mainloop you dont need to do global stuff and all
I'm new to programming and I'm trying to run a program that asks a user for a directory which which they will move their files to.
import tkinter as tk
from tkinter import filedialog, ttk
class Unzip():
def __init__(self):
# initialising the screen name, size title
root = tk.Tk()
root.geometry('650x550')
root.title("Move Files")
root.resizable(0, 0)
root.configure(bg='#002060')
# Initialising the frame to insert our widget
top_frame = tk.Frame(root, bg='#002060')
button_top = tk.Frame(root, bg='#002060')
button_bottom = tk.Frame(root, bg='#002060')
footer_frame = tk.Frame(root, bg='#002060')
top_frame.pack(side='top', fill='both')
button_top.pack(side='top', fill='both')
button_bottom.pack(side='top', fill='both')
footer_frame.pack(side='bottom', fill='both')
# Setting the title name
label_title = tk.Label(top_frame, text='Move Files', font='Arial 36 bold', fg='#948a54',
bg='#002060', pady=60)
label_title.pack(side='top')
# call button to get output file
output_file = ttk.Button(button_bottom, text='Choose location to save files', width=25, command=self.output_path)
output_file.pack(side='left', padx=(120, 10), pady=10)
root.mainloop()
# Get output directory
def output_path(self):
self.output_file_dir = filedialog.askdirectory()
if __name__ == '__main__':
Unzip()
The problem is that when you run the program and try to run the output_file button, the program will not be responding. I decided to use the self because I wanted it to be accessible to other instance methods that I want to create that will use the directory output_path.
So what exactly are you expecting? Your program does respond, but it is not supposed to do anything with the information.
Try
def output_path(self):
self.output_file_dir = filedialog.askdirectory()
print(self.output_file_dir)
Do you see what happens?
Maybe example 2 from this link can help you: https://www.programcreek.com/python/example/95888/tkinter.filedialog.askdirectory
My question is about Python 2.7 tkinter. The difficulty lies in getting all the buttons in one window, right now it opens 2 windows, one window with the size I defined, and one with all the button without a defined size.
from Tkinter import *
master = Tk()
class Calculator:
def __init__(self,master):
self.var = IntVar()
frame = Frame(master)
frame.grid()
f2 = Frame(master,width=500,height=500)
f2.grid(row=0,column=1)
def callback():
print("Hello World")
b = Button(master, text="Calc", command=callback)
b.grid()
e = Entry(master, width=10)
e.grid()
e.focus_set()
b1 = Button(master, text = "Ok", width=10, command = callback)
b1.grid(row=20, column=30)
top = Tk()
app = Calculator(top)
mainloop()
Tkinter requires exactly one instance of Tk() to start the tkinter app and one instance of mainloop() to manage the update loop.
If you call on Tk() more than once you will run into the issue of having multiple windows and things not working as you want.
If you do happen to need a 2nd or more windows then you will need to use Toplevel() to create them.
If you delete:
top = Tk()
and change:
app = Calculator(top)
to:
app = Calculator(master)
You will have everything in one window as intended.
I'm trying to use an Entry field to get manual input, and then work with that data.
All sources I've found claim I should use the get() function, but I haven't found a simple working mini example yet, and I can't get it to work.
I hope someone can tel me what I'm doing wrong. Here's a mini file:
from tkinter import *
master = Tk()
Label(master, text="Input: ").grid(row=0, sticky=W)
entry = Entry(master)
entry.grid(row=0, column=1)
content = entry.get()
print(content) # does not work
mainloop()
This gives me an Entry field I can type in, but I can't do anything with the data once it's typed in.
I suspect my code doesn't work because initially, entry is empty. But then how do I access input data once it has been typed in?
It looks like you may be confused as to when commands are run. In your example, you are calling the get method before the GUI has a chance to be displayed on the screen (which happens after you call mainloop.
Try adding a button that calls the get method. This is much easier if you write your application as a class. For example:
import tkinter as tk
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Get", command=self.on_button)
self.button.pack()
self.entry.pack()
def on_button(self):
print(self.entry.get())
app = SampleApp()
app.mainloop()
Run the program, type into the entry widget, then click on the button.
You could also use a StringVar variable, even if it's not strictly necessary:
v = StringVar()
e = Entry(master, textvariable=v)
e.pack()
v.set("a default value")
s = v.get()
For more information, see this page on effbot.org.
A simple example without classes:
from tkinter import *
master = Tk()
# Create this method before you create the entry
def return_entry(en):
"""Gets and prints the content of the entry"""
content = entry.get()
print(content)
Label(master, text="Input: ").grid(row=0, sticky=W)
entry = Entry(master)
entry.grid(row=0, column=1)
# Connect the entry with the return button
entry.bind('<Return>', return_entry)
mainloop()
*
master = Tk()
entryb1 = StringVar
Label(master, text="Input: ").grid(row=0, sticky=W)
Entry(master, textvariable=entryb1).grid(row=1, column=1)
b1 = Button(master, text="continue", command=print_content)
b1.grid(row=2, column=1)
def print_content():
global entryb1
content = entryb1.get()
print(content)
master.mainloop()
What you did wrong was not put it inside a Define function then you hadn't used the .get function with the textvariable you had set.
you need to put a textvariable in it, so you can use set() and get() method :
var=StringVar()
x= Entry (root,textvariable=var)
Most of the answers I found only showed how to do it with tkinter as tk. This was a problem for me as my program was 300 lines long with tons of other labels and buttons, and I would have had to change a lot of it.
Here's a way to do it without importing tkinter as tk or using StringVars. I modified the original mini program by:
making it a class
adding a button and an extra method.
This program opens up a tkinter window with an entry box and an "Enter" button. Clicking the Enter button prints whatever is in the entry box.
from tkinter import *
class mini():
def __init__(self):
master = Tk()
Label(master, text="Input: ").grid(row=0, sticky=W)
Button(master, text='Enter', command=self.get_content).grid(row=1)
self.entry = Entry(master)
self.entry.grid(row=0, column=1)
master.mainloop()
def get_content(self):
content = self.entry.get()
print(content)
m = mini()