Nested functions inside a class in tkinter python [duplicate] - python-3.x

Consider below example:
import tkinter as tk
root = tk.Tk()
root.title("root")
other_window = tk.Tk()
other_window.title("other_window")
root.mainloop()
and also see below example that creates instances of Tk back-to-back instead of at once, so there's exactly one instance of Tk at any given time:
import tkinter as tk
def create_window(window_to_be_closed=None):
if window_to_be_closed:
window_to_be_closed.destroy()
window = tk.Tk()
tk.Button(window, text="Quit", command=lambda arg=window : create_window(arg)).pack()
window.mainloop()
create_window()
Why is it considered bad to have multiple instances of Tk?
Is the second snippet considered a bit better, or does it suffer from
the same conditions the first code does?

Why is it considered bad to have multiple instances of Tk?
Tkinter is just a python wrapper around an embedded Tcl interpreter that imports the Tk library. When you create a root window, you create an instance of a Tcl interpreter.
Each Tcl interpreter is an isolated sandbox. An object in one sandbox cannot interact with objects in another. The most common manifestation of that is that a StringVar created in one interpreter is not visible in another. The same goes for widgets -- you can't create widgets in one interpreter that has as a parent widget in another interpreter. Images are a third case: images created in one cannot be used in another.
From a technical standpoint, there's no reason why you can't have two instances of Tk at the same time. The recommendation against it is because there's rarely an actual need to have two or more distinct Tcl interpreters, and it causes problems that are hard for beginners to grasp.
Is the second snippet considered a bit better, or does it suffer from the same conditions the first code does?
It's impossible to say whether the second example in the question is better or not without knowing what you're trying to achieve. It probably is not any better since, again, there's rarely ever a time when you actually need two instances.
The best solution 99.9% of the time is to create exactly one instance of Tk that you use for the life of your program. If you need a second or subsequent window, create instances of Toplevel. Quite simply, that is how tkinter and the underlying Tcl/Tk interpreter was designed to be used.

I disagree with the tkinter community discouraging the use of multiple tk.Tk windows. You can have multiple tk.Tk windows. Using multiple instances of tk.Tk is the only way to create windows that are truly independent of each other. The only mistake most people make when creating multiple tk.Tk windows is that they forget to pass in master=... when creating PhotoImages/StringVars/IntVars/...
For example look at this code:
import tkinter as tk
root = tk.Tk()
root2 = tk.Tk()
variable = tk.StringVar() # Add `master=root2` to fix the problem
entry = tk.Entry(root2, textvariable=variable)
entry.bind("<Return>", lambda e: print(repr(variable.get())))
entry.pack()
root.mainloop()
The code above doesn't work. If you add master=root2 to the tk.StringVar(), then it will work perfectly fine. This is because tkinter stores the first instance of tk.Tk() in tk._default_root. Then if you don't pass in master=..., it will assume that you wanted the window in tk._default_root.
Another thing people get wrong is how many times should .mainloop() be called. It handles events from all tk.Tk windows that are alive so you only need one .mainloop().
For folks who disagree, I'd be interested in an example of where an actual problem is caused by the multiple tk.Tk windows.

The best reference I've found so far is the Application Windows section of the tkinterbook:
In the simple examples we’ve used this far, there’s only one window on the screen; the root window. This is automatically created when you call the Tk constructor
and
If you need to create additional windows, you can use the Toplevel widget. It simply creates a new window on the screen, a window that looks and behaves pretty much like the original root window
My take on it is that a Tk instance creates a Toplevel widget, plus things like the mainloop, of which there should be only one.

Tk() initializes the hidden tcl interpreter so that the code can run, as Tkinter is just a wrapper around tcl/tk. It also automatically creates a new window. Toplevel() just creates a new window, and wont work if Tk() hasn't been instantiated, as it requires the tcl interpreter that Tk() initializes. You cannot create any Tkinter widgets without instantiating Tk(), and Toplevel is merely a widget. In the question, you use Tk() to create a second window. You should instead create another file, because initializing the tcl interpreter multiple times can get confusing, as #Bryan Oakley explains so well. Then you should do:
from os import startfile
startfile(nameOfTheOtherFile)
, because, as Toplevel() is just a widget, it closes when the Tk() window is closed. Having the other window in a separate file makes it less confusing.

Related

`new_root.mainloop()` doesn't make main window unresponsive

Let's say that I have this tkinter script:
import tkinter as tk
def callback():
new_root = tk.Tk()
new_root.mainloop()
print("Done")
root = tk.Tk()
button = tk.Button(root, text="Click me", command=callback)
button.pack()
root.mainloop()
From my understanding of tkinter, when I press the button, a new window and tcl interpreter should be created. While running callback, the main window (root) shouldn't be updated so it should be unresponsive. new_root.mainloop() is a while True loop that runs until the second window is closed. Therefore, when I press the button it should create a new window, call .mainloop() on it and that should make the main window unresponsive. The problem is that that doesn't happen. The main window is responsive even though code execution is stuck inside new_root.mainloop().
Also closing the second window doesn't print "Done" until the rest of the tkinter windows are closed. Why does that happen?
I looked at the source code for tkinter and _tkinter but I couldn't find anything useful. I have Python 3.7.9, tcl 8.6
Though you're running a subsidiary event loop (really don't do that!) it still shares the same registry of event handlers as the outer loop, so events coming in are handled in the inner loop just as in the outer one. (There's a common piece of low-level event handling code that reaches deep into the OS to do the event processing efficiently. That code, the notifier, is stuff that very few people should ever touch; it's tricky because it merges some really weird and disparate event sources while also working around a bunch of strange bugs on some platforms.) The event_loop method returns once all windows are deleted. It literally calls the low level event processing engine (the API call is Tcl_DoOneEvent()) with appropriate flags, and does that in a while loop (until the number of existing windows drops below 1; that's exactly what it is waiting for). This is why you probably shouldn't count on it terminating and absolutely shouldn't nest it in a GUI callback.

Taking User Input. Python GUI

I'm in the process of making a python GUI calculator. I've been coding for no more than 3-4 week so my knowledge is limited. Anyway i want to make a pop up window that takes input from the user(Enter number, press a button to save that number in a variable).
That should be done twice(in order to add, subtract,... 2 numbers). Then i'll make another pop-up window saying: "The result is:(result)"
I know how to make an entry widget so my question is how do i make a button to save the user's input to a variable?
I highly recommend using a module called tkinter for new coders wanting to learn GUI programming in python. A complete tutorial can be found here:
http://zetcode.com/gui/tkinter/
However, creating a calculator with tkinter is pretty easy. Before you start you should think of what type of calculator you want to make, one with buttons or user input. Since you are a beginner let's do the user input method.
First if you cannot import tkinter without striking and error, head over to command prompt and write:
pip install tkinter
First things first we need to create the popup window:
from tkinter import *
window = Tk()
window.mainloop()
Now we need to create and Entry widget:
from tkinter import *
window = Tk()
User_input = Entry()
User_input.pack()
window.mainloop()
Now you will get an Entry where you will write your math problem.
Many people get confused at this stage because when they call the .get() function it doesn't work. This is because .get() makes a string. So in order to get an int you use
user_problem = int(User_input.get())
Then you use the int (numbers) the user wrote and solve them.
When using the button method assign a command callback to each button.

TkInter Tk() not working

I have much experience with Python, but I'm just now learning Tkinter and the following code isn't working:
root = Tk()
root.mainloop()
It spits out the error message
"NameError: name 'Tk' is not defined"
It seems you are simply not importing the tkinter library.
The quick solution is to add from tkinter import * to the top of your file.
However, global imports are generally a bad idea. I know lots of tkinter tutorials start out this way, but they shouldn't. I recommend doing it this way:
import tkinter as tk
root = tk.Tk()
root.mainloop()
It requires that you prefix every tkinter command with tk., but it makes your code easier to understand, and easier to maintain over time. If, for example, you decide to import ttk (some modern looking tkinter widgets), it is impossible to know if Button(...) refers to the ttk button or the tk button if you use global imports. However, tk.Button(...) and ttk.Button(...) are crystal clear.
The error occurred because the file was named as tkinter.py and caused the tkinter library import to fail.
Make sure your file name differs.

Using tk to create a text editor

Is it possible to use tk to create a text editor that can support syntax highlighting, autocomplete and even can be later extended to be an IDE for a specific language?
I found tkinter widget, but not sure if it can support that or not?
I think if a widget can do some processing on the text while the user is writing it, then it can used for this purpose.
Absolutely. The tkinter text widget is remarkably powerful. Everything you mentioned can be done, and more. You can also implement code folding, embedded images, hyperlinks, undo, etc.
Here are some answers to related questions you might find helpful:
you can use pygments for syntax highlighting. This answer has a tiny example: https://stackoverflow.com/a/11551277
line numbers and a custom event that fires whenever the widget changes is mentioned in this answer: https://stackoverflow.com/a/16375233
adding a search function is partially covered in this answer: https://stackoverflow.com/a/3781773
Not to be repetitive, since Bryan Oakley already answered this, but yes. And he's right. The text widget is extremely powerful. I've never seen another text widget that matches its breadth of features.
Tkinter is the most convenient GUI library I've ever found for making a text editor. Actually, I've been making one, myself, for a while. Are you interested in making one or are you looking for one already made with Tkinter?
For me, syntax highlighting and autocomplete aren't the most important features in a text editor. I care more about quick/convenient file/project access, launching/compiling/running programs, Unicode or special character input, widget color schemes, navigation key-bindings, tabs, and having all kinds of shortcuts for various wild tools that I like to make my life easier (and more fun). So, I'm actually just starting on syntax highlighting now (after already having done most of the other stuff). But, yes, syntax highlighting can be done in Tkinter.
Really, though, a lot of the features you might want in a text editor don't even require much to do with the GUI library. For instance, making it recognize what kind of file you're editing, so you can run your file with the correct program (or compile it with the correct compiler). That really has little to do with Tkinter.
Let me know if you want to chat in real-time about text editors.
The IDE that comes with Python, IDLE, seems to be written with Tkinter, too. Looking at the source code is sometimes helpful, but they tend to do things in a different way than I would personally recommend, although I'm sure some people like it more than the way I like things.
There are a few drawbacks to Tkinter, but they're better than the drawbacks I've seen with other GUI libraries, for my purposes. Some things require more work, but most also seem to offer more flexibility. Some things require less work, too. Impressively, Tkinter doesn't seem to have a lot of bugs, and most of the ones that exist can be programmed around. It's quite robust. It gets a bad rap, sometimes, for not having native widgets and not rendering fonts a certain way, but don't listen to people. The widgets are fine, whether or not they're native, and the fonts look great. If there ever was a problem with the fonts, I think they fixed it. Tell me if I'm wrong. Mine look perfect (exactly as they do in LibreOffice, if not better). Besides, the perks of Tkinter shouldn't be ignored. Tkinter is awesome.
Python's help() method will be your friend. Use it on classes and modules.
One of the best things about Tkinter is that it pretty much comes with Python. So, unlike something like WxPython (which isn't even on 3.x, although they're making something for it with another name), Tkinter will likely automatically be supported for all future releases of Python (until if/when they decide to replace it with something else—and I don't know that they will, unless they find something totally awesome). Plus, it's not a huge download.
Anyway, to get started, you'll want to learn about marks (especially the built-in ones) and tags. You'll likely use those a lot for a lot of stuff. These are important for accessing specific areas of text in your widget and doing things to them.
INSERT is a string, 'insert'. But, it's also a mark. Marks are variable indexes to mark a position. The 'insert' mark represents the index in your text widget where your text insert is. The beginning of your document is the index "1.0" not "0.0". 1 is row 1. 0 is character 0. I don't believe there's a mark for the beginning. You just use the index. The mark for the end is END (which is a string, 'end'). 'sel.first' and 'sel.last' represent the indexes at the beginning and end of selected text. SEL (or 'sel') is a tag. Tags are kind of like HTML tags in that they mark the beginning and end of things (and all the text in between). You can make your own tags to add text styles (like bold and italics, or just highlights, which could be useful for syntax highlighting). You can check a range of text to see if it has a certain tag. For instance,
SEL in self.myTextWidget.tag_names(INSERT)
Will return True if the character directly after (that is, at) the text insert is selected.
For marks, you can use some handy notation to get some handy index locations. For instance "insert wordstart" will give you the index for the beginning of whatever word the 'insert' mark is on. "insert+2c" will give you the position two characters after the insert. Anyway, there's lots of stuff like that.
If you're making a cross-platform application, you'll want to know that a few of the events are different on different platforms. So, check and make sure they all work.
You'll also want to realize that although some of the standard keyboard shortcuts in the text widget don't work (like Control-a doesn't select all) there actually are shortcuts. They're just different shortcuts. However, you can override them and make your own pretty easily:
…
#We're doing both a and A to make sure it works properly when caps-lock is on.
#Control-Shift-a is different from Control-A despite how it may seem.
self.myTextWidget.bind("<Control-a>", self.select_all)
self.myTextWidget.bind("<Control-A>", self.select_all)
def select_all(self, event):
self.myTextWidget.tag_add("sel", "1.0", "end-1c") #END actually goes beyond the end of the text, adding a new line (so, I subtract a character).
return "break" #If you want to override the default binding, you need to return "break"
Tkinter is fairly maliable, it can be used to accomplish a number of tasks on its own. I've actually made my own mini text editor using tkinter in python 2.7. It doesn't have the advanced functionality yet though. But I think this would be a good basis for your idea. Make sure you have python2.7 installed.
from Tkinter import *
import tkFileDialog
class Files(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("File dialog")
self.pack(fill=BOTH, expand=1)
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
fileMenu.add_command(label="Save", command = self.save_command)
fileMenu.add_command(label="Open", command = self.onOpen)
menubar.add_cascade(label="File", menu=fileMenu)
self.txt = Text(self)
self.txt.pack(fill=BOTH, expand=1)
def onOpen(self):
ftypes = [('Python files', '*.py'), ('All files', '*')]
dlg = tkFileDialog.Open(self, filetypes = ftypes)
fl = dlg.show()
if fl != '':
text = self.readFile(fl)
self.txt.insert(END, text)
def readFile(self, filename):
f = open(filename, "r")
text = f.read()
return text
def save_command(self):
file = tkFileDialog.asksaveasfile(mode='w')
if file != None:
data = self.txt.get('1.0', END+'-1c')
file.write(data)
file.close()
root = Tk()
top = Frame(root)
top.pack(fill=BOTH, expand=1)
def build():
l = Label(top, text="test phrase")
l.pack(side="left")
ent = Entry(top)
ent.pack(side="left")
bottom = Frame(root)
bottom.pack()
ex = Files(root)
ex.pack(side="bottom")
root.geometry("300x250+300+300")
root.mainloop()

Perl Tk update(redraw) widgets in sub(in thread)

I have some widgets, checkboxes actually. And I have variables that contains their value. I have shared this variables and called a sub with creating new thread.
Variables change into this sub but look of widgets stays unchanged. So that is the question: how colud I redraw widgets or update their look somehow... or how could I rebuild my programm so they will update automatically or something.
Thank you!
Using threads and Tk is not easy. See Perl Tk and Threads on PerlMonks for tips and tricks.
Do you really need threads? Using Tk::after or $widget->update you can usually update widgets to changing values.

Resources