Linking tkinter GUI with functions in a different module - python

I want to link GUI (1. modul) with the functions that are in a different module. Basically I need to link GUI with the program.
I created a very easy example:
modul1:
from modul2 import *
from tkinter import *
window = Tk()
window.title('Program')
window.geometry("300x300")
text_input= StringVar()
#result
result=Entry(window, textvariable=text_input)
result.place(x=6,y=15)
#Button
button=Button(window, text='X')
button.config(width=5, height=2, command=lambda: test())
button.place(x=10,y=70)
window.mainloop()
modul2:
import modul1
def test():
global text_input
text_input.set('hello')
EXPECTED:
This program should write "hello" into Entry window after clicking on a button.
RESULT: ERROR:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Ondrej\AppData\Local\Programs\Python\Python37-32\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:/Users/Ondrej/Desktop/VUT FIT/IVS\modul1.py", line 17, in <lambda>
button.config(width=5, height=2, command=lambda: test())
NameError: name 'test' is not defined
Does anybody know where the problem is?
Thank you in advance for the help.

Yes, the problem you actually have is to do with a circular import. Modul1 imports Modul2 and Modul2 imports Modul1. a way you could solve this is to give the textinput as a parameter to the test function in modul2 like this:
modul1.py:
import modul2
from tkinter import *
window = Tk()
window.title('Program')
window.geometry("300x300")
text_input = StringVar()
# result
result = Entry(window, textvariable=text_input)
result.place(x=6, y=15)
# Button
button = Button(window, text='X')
button.config(width=5, height=2, command=lambda: modul2.test(text_input))
button.place(x=10, y=70)
window.mainloop()
modul2.py:
def test(text_input):
text_input.set('hello')
Unfortunately python really doesn't like you to do circular imports and this is good as this would actually mean an infinite loop. When would it stop importing the other file?
Edit: there would be a very convoluted way to make a class in a third module and set attributes on that, and then import this third module in both modul1 and modul2 to share variables between them but please don't...
Edit2: an example of this:
Modul1.py:
from shared import SharedClass
import modul2
from tkinter import *
window = Tk()
window.title('Program')
window.geometry("300x300")
SharedClass.text_input = StringVar()
# result
result = Entry(window, textvariable=SharedClass.text_input)
result.place(x=6, y=15)
# Button
button = Button(window, text='X')
button.config(width=5, height=2, command=lambda: modul2.test())
button.place(x=10, y=70)
window.mainloop()
Modul2.py:
from shared import SharedClass
def test():
SharedClass.text_input.set('hello')
shared.py
class SharedClass:
pass
This works due to the way python loads imported classes. They all are the same. This makes it so if you set properties on the class (not the instances of it) you can share the properties.

Related

How to use an entry widget from file 1 for a class in file 2, then use that class back in file 1 in a different frame without circular reference

this may sound confusing but I am trying to understand how I can get the input used in an entry widget in my main.py file and import what the user typed into another file. In the second file there would be a class that takes that data and stores it for docxtpl. Then once that is done in the main.py file there is a button that will start and write it to the docx.
I'm thinking this is more of a logic issue as I'm not really sure how to make it work without making the function all in one file. The reasoning for two separate files is I'm trying to improve my coding without relying on dumping everything into a single file.
The code looks like this
main.py
from tkinter import *
from gui_class import *
root = Tk()
frame1 = LabelFrame(root).pack
username = Label(frame1, text="Enter your name").pack()
username_entry = Entry(frame1)
username_entry.pack()
button_Frame = LabelFrame(root).pack
mybtn = Button(button_Frame, text="Enter", command=lambda: start(button_Frame)).pack
main.loop
gui_class.py
from docxtpl import DocxTemplate
doc = DocxTemplate("test.docx")
def start(button_Frame)
context = {
"NAME" : username_entry.get()
}
doc.render(context)
doc.save("test1.docx")
I get the error "username_entry is not defined" which makes sense after looking into it, it seems that gui_class.py is loaded up first before username_entry is ever created. I've only found that you can delay it by x seconds which wouldn't be a fix. I also tried importing the specific entry widget but that causes circular reference error.
I'm not entirely sure how else to solve this issue without adding the function into main.py
In main.py.
Missing brace bracket for pack
Added StringVar(). And put this textvariable=var in Entry
In line 14, Added keyword command button var.get() instead of
button_Frame.
Typo main.loop should be mainloop()
Code:
from tkinter import *
from gui_class import *
root = Tk()
var = StringVar()
frame1 = LabelFrame(root).pack()
username = Label(frame1, text="Enter your name").pack()
username_entry = Entry(frame1, textvariable=var)
username_entry.pack()
button_Frame = LabelFrame(root).pack()
mybtn = Button(button_Frame, text="Enter", command=lambda : start(var.get()))
mybtn.pack()
mainloop()
And gui_class.py, you can't called username_entry.get().
Replace username_entry.get() to button_Frame
Code:
from docxtpl import DocxTemplate
doc = DocxTemplate("test.docx")
def start(button_Frame):
context = {
"NAME" : button_Frame
}
print(context)
doc.render(context)
doc.save("test1.docx")
Btw, I do not used docxtpl library. I can do workaround.
Screenshot:
Screenshot after clicking. And you can see in debug:

I have a weird problem with tkinter when using python

I am trying to learn how to use Tkinter, but whenever I want to execute my code I always get this problem: (NameError: name 'label' is not defined) or (NameError: name 'button' is not defined).
As a beginner, it seems to me that the problem is with my code editor. (BTW I am using VScode)
this is my code:
from tkinter import *
root = Tk()
mylabel = label(root, text='Hello World')
mylabel.pack()
root.mainloop()
And as I said, this also happens with this one:
from tkinter import *
root = Tk()
mybutton = button(root, text='Hello World')
mybutton.pack()
root.mainloop()
you have caps error its Button and Label not button and label

Properly assigning a function with arguments to a button in Tkinter

I have two files:
functions.py
import tkinter as tk
class funcs():
def func1(entry):
entry.delete(0, tk.END)
main.py
import tkinter as tk
import functions as f
root = tk.Tk()
entrybox = tk.Entry(master=root).grid()
button = tk.Button(master=root, command=f.funcs.func1(entrybox)).grid()
root.mainloop()
In main.py, I have assigned the command func1 with the argument entrybox to the widget button.
My intent is to have the entry argument represent an entry widget I want to manipulate.
This line of code is broken:
button = tk.Button(master=root, command=f.funcs.func1(entrybox)).grid()
The problem is that when I run the program, the function is called immediately and does not get assigned to the button.
I am looking for a way to assign a function with arguments to a button in tkinter.
You can use an anonymous function:
tk.Button(
master=root,
command=lambda: f.funcs.func1(entrybox)
)
Python recognizes the broken line of code as an immediate call, so you have to do lambda:
button = tk.Button(master=root, command=lambda: f.funcs.func1(entrybox)).grid()
and as far as I know that will get rid of the problem.

Close button tkinter after pressing button?

I created a button that works perfectly (not entire code here) but I want that once you press the 'Save' button, the window disappear. Someone knows how to do it?
root2 = tk.Tk()
root2.geometry('200x100')
save_button = tk.Button(root2)
save_button.configure(text='Save', command=lambda: ask_parameter(ents1))
save_button.pack()
root2.mainloop()
Based on the extremely limited snippet of code in your question: I would suggest it doing by defining a function to call that does something like this:
import tkinter as tk
def ask_and_close(root, ents):
ask_parameter(ents)
root.destroy()
ents1 = "something"
root2 = tk.Tk()
root2.geometry('200x100')
save_button = tk.Button(root2)
save_button.configure(text='Save', command=lambda: ask_and_close(root2, ents1))
save_button.pack()
root2.mainloop()
Note: If you're creating multiple windows, I wouild suggest using tk.Toplevel() instead of calling tk.TK() more than once.
Just use the master.quit() method!
Example Code:
from tkinter import *
class Test:
def __init__(self):
self.master = Tk()
self.button = Button(self.master, text="Push me!", command=self.closeScreen)
self.button.pack()
def closeScreen(self):
# In your case, you need "root2.quit"
self.master.quit()
test = Test()
mainloop()
I would suggest using destroy() method as used here https://docs.python.org/3.8/library/tkinter.html#a-simple-hello-world-program.
One of the easy ways to invoke the destroy method in your code is this;
def ask_parameter_and_destroy(ents1):
ask_parameter(ents1)
root2.destroy()
root2 = tk.Tk()
root2.geometry('200x100')
save_button = tk.Button(root2)
save_button.configure(text='Save', command=lambda: ask_parameter_and_destroy(ents1))
save_button.pack()
root2.mainloop()
You can read about differences between destroy() and previously proposed quit() methods on the following page: What is the difference between root.destroy() and root.quit()?.
If your goal is to create a dialog for saving a file, you might be interested in tkinter.filedialog library, which has ready made dialog boxes for handling file saving.

TypeError in __init__(), unexpected argument python

I have the following code:
from tkinter import *
class Button:
def __init__(self, master):
frame = Frame( master )
frame.pack()
self.printButton = Button(frame, text = "Print Message", command=self.printMessage)
self.printButton.pack(side = LEFT)
self.quitButton = Button(frame, text = "Quit", command = frame.quit)
self.quitButton.pack(side = LEFT)
def printMessage(self):
print(" WORKING!! " )
root = Tk()
b = Button(root)
root.mainloop()
Which does not seem to be wrong in anyway... But when I run it, terminal says:
Traceback (most recent call last):
File "class.py", line 23, in <module>
b = Button(root)
File "class.py", line 10, in __init__
self.printButton = Button(frame, text = "Print Message", command=self.printMessage)
TypeError: __init__() got an unexpected keyword argument 'command'
I wrote all these codes according to a tkinter tutorial. And in the tutorial, the code works well. Any help would be appreciated. Thanks in advance!
Tkinter already has a Buttonclass and when you create your class you now have overwritten the tkinter class named Button. So, when you try to create a tkinter button like this:
self.printButton = Button(frame, text = "Print Message", command=self.printMessage)
You are now referencing your button because you overwrote the tkinter button previously. And since your button only takes one argument and you give it three, It will throw you an error. The way to fix this would be to change your import line to this:
import tkinter as tk
And then reference any tkinter functions with tk.*. For example:
root = Tk()
would become:
root = tk.Tk()
Then your button would be referenced by Button while the tkinter button would be referenced by tk.Button. This way you could easily distingush between the two. However you could also just call your button something like myButton which would also fix the problem.

Categories