global functions dont work with tkinter - python

I wrote a basic script in Python3 to create a basic function that would display then close a window. I would like to have a function to close the window, so I could import the script into a different file I have without cluttering it up too much. I tried this, however the compiler returns with name tkdel() is not defined.
def tkdisp(t):
import tkinter as tk
class disp(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.distk = tk.Label(self)
self.distk["text"] = t
self.distk.pack(side="top")
root = tk.Tk()
disp(master=root).mainloop()
def tkdel():
global tkdel
root.destroy
tkdisp('nice')
tkdel()

well I had to change the code quite a bit but this works (Hope it helps):
import tkinter as tk
root = tk
class disp(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.distk = tk.Label(self)
self.distk["text"] = t
self.distk.pack(side="top")
root = tk.Tk()
disp(master=root).mainloop()
def tkdel():
#global tkdel
app = None
exit()
disp.tkdel()
I try to not use globals too much as it can get complicated in a big program. :) it's better to send data between places directly. :)
I just reread the question. If you want to use another FILE - like file.py you can import the file on startup just by saying: import file
If you want to close the window from anywhere just use disp.tkdel() as this is a direct path. Really hope this helps. :)

Related

Having trouble with Tkinter

I just need help in why this code does not work. I am unfamiliar with tkinter and this is the code that I was given and was told it should work. I see it says root is not defined but not sure what it should be defined as because I am assuming root is from tkinter? The only thing is that line 2 is grayed out. If you could help it would be greatly appreciated!
import tkinter as tk
from tkinter import *
class MyFirstGUI:
def __init__(self):
root.gui = tk.TK()
root.gui.title("A simple GUI")
root.gui.label = tk.Label(text="This is our first GUI!")
root.label.pack()
root.greet_button = tk.Button(text="Greet", command=self.greet)
root.greet_button.pack()
root.close_button = tk.Button(text="Close", command=self.gui.destroy)
root.close_button.pack()
def greet(root):
print("Greetings!")
root.mainloop()
my_gui = MyFirstGUI()
The identifier root is often assigned from tk.Tk(). If that's the case, then root will be the Tk application and the root of the Tk display graph.
You do all of your setup in the MyFirstGUI class's constructor, but you only use a local variable to keep track of the results. You therefore lose access to all of it when you exit the constructor.
I expect that you want to create everything as a root attribute on self so that your setup is encapsulated and preserved by the class.
Here's how I would rework your code to get it to do what I think you intend:
import tkinter as tk
from tkinter import *
class MyFirstGUI:
def __init__(self):
self.root = Tk()
self.root.title("A simple GUI")
self.root.label = tk.Label(text="This is our first GUI!")
self.root.label.pack()
self.root.greet_button = tk.Button(text="Greet", command=self.greet)
self.root.greet_button.pack()
self.root.close_button = tk.Button(text="Close", command=self.root.destroy)
self.root.close_button.pack()
def greet(root):
print("Greetings!")
my_gui = MyFirstGUI()
my_gui.root.mainloop()

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 opens a second Window when openening a Dialog or creating a window

I have the following problem. I created a gui with Tkinter and when I run it in my IDE (Spyder) everything works perfectly fine, but when I save the file as and want to start it by just executing the .py, everytime a window is created or a dialog opens, a second Tkinter window is poping up. Same problem appears when I save the code as .pyw .
I posted a short example that lasts in the same Problem.
import tkinter as tk
from tkinter import messagebox
class test_GUI(tk.Frame):
def __init__(self,master=None):
super().__init__(master)
self._initializeWindow()
self._window.protocol("WM_DELETE_WINDOW", self.__on_closing)
self._window.mainloop()
def _initializeWindow(self):
self._window=tk.Tk()
self._window.title("The window I initzialized")
def __on_closing(self):
if(messagebox.askokcancel("Quit", "Quit program?")):
self._window.destroy()
self._window.quit()
app=test_GUI()
You define your class as
class test_GUI(tk.Frame):
so your class inherits from tk.Frame, which means that your class basically is a Frame with extra features.
When you do
super().__init__(master)
You initialize the class from which you are inheriting, which is tk.Frame. At this time, there is no tk.Tk object (and master=None). Because a Frame (or any other tkinter widget) cannot exist without an instance of tk.Tk, tkinter silently makes one for you. This is your first window.
After that you call
self._window = tk.Tk()
to make a tk.Tk instance yourself. This is your second window. Besides that you don't want two windows, you should never have more than one instance of tk.Tk (or more accurately the associated Tcl interpreter) running at the same time because this leads to unexpected behavior.
So how can you fix this?
You basically have two options: remove inheritance or initiate tk.Tk before initiating your class.
Without inheritance your app can be structured like
import tkinter as tk
class test_GUI():
def __init__(self):
self._window=tk.Tk()
self._window.title("The window I initzialized")
self.button = tk.Button(self._window, text='Test button')
self.button.pack()
...
self._window.mainloop()
With inheritance you can do it like this
import tkinter as tk
class test_GUI(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.master.title("The window I initzialized")
self.button = tk.Button(self, text='Test button')
self.button.pack()
root = tk.Tk()
app=test_GUI(root)
app.pack(fill='both', expand=True)
root.mainloop()
Both ways work fine. I personally like the version with inheritance. Also check out Bryan Oakley's post on structuring a tkinter application here.
def _initializeWindow(self):
self._window=tk.Tk()
self._window.title("The window I initzialized")
self._window.withdraw()
self._window.withdraw() will remove the second window.
super().__init__(master) is actually responsible for the first window. Comment it out. In that case you won't need to withdraw the window you created in _initializeWindow.
import tkinter as tk
from tkinter import messagebox
class test_GUI(tk.Frame):
def __init__(self,master=None):
#super().__init__(master)
self._initializeWindow()
self._window.protocol("WM_DELETE_WINDOW", self.__on_closing)
self._window.mainloop()
def _initializeWindow(self):
self._window=tk.Tk()
self._window.title("The window I initzialized")
#self._window.withdraw()
def __on_closing(self):
if(messagebox.askokcancel("Quit", "Quit program?")):
self._window.destroy()
self._window.quit()
app=test_GUI()
For those attempting to unit test your GUI and trying to insert the root tk dependency via dataclasses, you can fix the multi window problem by putting tk initialization in the __post_init__ method:
from dataclasses import dataclass
import tkinter as tk
#dataclass
class App():
tk_root: tk.Tk = None
def __post_init__(self):
if self.tk_root is None:
self.tk_root = tk.Tk()
# ...
Then, if you utilize tkinter classes (like StringVars) that need a root Tk to be initialized, you'll need to patch tk in your pytest fixture:
import pytest
from unittest.mock import patch, MagicMock
from GUI import App
#pytest.fixture
def app():
with patch('GUI.tk'):
return GUI(tk_root=MagicMock())

Python 2.7 tkSimpleDialog.Dialog crashes when pressing Enter

I inherited a class from tkSimpleDialog.Dialog. From a tkInter application, I open it, it has some Entry fields, an OK and a Cancel button.
When this dialog is open and I press the Enter button the dialog and also the main application freezes completely and won't do anything further.
I tried to bind <"Return"> event to it, to catch, but nothing else happened, just the freeze.
Sadly I can't attach code, because it is my work in a company.
Does anyone have an idea how could I resolve the problem and maybe set the Enter button to activate th OK?
I use Python 2.7 and cannot use other versions.
from Tkinter import *
import ttk
from tkFileDialog import askopenfilename, asksaveasfilename
import tkSimpleDialog
from mem_data import *
from collections import namedtuple
class OpenFileDialog(tkSimpleDialog.Dialog):
def body(self, master):
self.master = master
def apply(self):
self.result = "OK"enter code here
And I call it like this:
d = OpenFileDialog(self.master)
mcu = d.result
The problem is that you are setting self.master. Unfortunately, Tkinter uses self.master internally. To fix this you need to choose a different variable name.
class OpenFileDialog(tkSimpleDialog.Dialog):
def body(self, master):
self._master = master
...

Safetkinter RuntimeError: Recursion Limit

So I've been trying to get a threadsafe adapter for tkinter (I'm using Python 3.3) to help with some of my projects. I stumbled upon a recipe called "safetkinter" (recipe 578153), which promises to route all gui calls to the main thread. I tried it out, but I am having problems with a RuntimeError that keeps sticking its head up and ruining my endeavors. I've tried various ways of fixing it with no luck. So... Does anyone here know the trick to getting this to work? (also, safetkinter makes use of the threadbox recipe, and the affinity recipe by dependence. I also believe that the affinity module is the culprit of this problem.)
The script I was using to test the module:
import safetkinter as tkinter
class Main(tkinter.Tk):
def __init__(self):
self._setup()
self.mainloop()
def _setup(self):
tkinter.Button(self, text='Push me!').pack()
if __name__ == '__main__':
Main()
Also, here is a link to the exception traceback taken from the console:
Traceback
As I see at the example of safetkinter usage, the initialization should be done in another way. Here is a short working code based on your code and safetkinter example (Frame with a Button). You can extend and improve it:
import safetkinter as tkinter
class Main(tkinter.Frame):
def __init__(self, master, **kw):
super().__init__(master, **kw)
#classmethod
def main(cls):
root = cls.create_application_root()
self = cls.setup_class_instance(root)
root.mainloop()
#staticmethod
def create_application_root():
root = tkinter.Tk()
root.minsize(400, 340)
root.title('Title')
return root
#classmethod
def setup_class_instance(cls, root):
instance = cls(root)
instance.button = tkinter.Button(root, text='Push me!').pack()
return instance
if __name__ == '__main__':
Main.main()

Categories