Multiple application/file/windows instances running on tkinter - python

Here is a sample situation:
I have written a tk GUI that opens and edits e.g. .txt files. I'm currently on a mac, but I might also want to run this on windows. Currently the main Application class creates its own internal tk.Tk() like so
import tkinter as tk
class App:
def __init__(self):
self.root = tk.Tk()
tk.Label(master=self.root,text="content").pack()
if __name__ == '__main__':
a=App()
a.root.mainloop()
I want to add multi file capabilities to the application. In the current system this would result in 2 tk.Tk() instances running which I couldn't get to work and people say is unpredictable.
I want to be able to close any of the multiple files and still have the application running until the last window is closed and then the application quit or stays without a window (like on a mac).
My problem is that if I use tk.Toplevel() for each of my windows I have a ugly default tk.Tk() window which I would need to hide which seams inelegant. It would also be messy to determine which file is currently in focus for actions with the menubar.
I'm also considering each file being it's own application instance like on Windows, but that would fill the dock with repeated icons and be inconsistent with the rest of the system.
Here are the possible way I came up with:
Multiple Tks (Works but more complex examples are unpredictable):
import tkinter as tk
class App:
def __init__(self):
self.root = tk.Tk()
tk.Label(master=self.root,text="content").pack()
tk.Button(master=self.root,text="new_window",command=self.open).pack()
def open(self):
App()
if __name__ == '__main__':
a=App()
a.root.mainloop()
Hidden tk window and multiple Toplevels (Needs hiding the Tk and the osx menubar commands like view->change_colors would need to manaly decide where to direct comands):
import tkinter as tk
class App:
def __init__(self):
self.root = tk.Toplevel()
tk.Label(master=self.root,text="content").pack()
tk.Button(master=self.root,text="new_window",command=self.open).pack()
def open(self):
App()
if __name__ == '__main__':
tk.Tk()
a=App()
a.root.mainloop()
Each Window is its own Application and fully independant (Dosn't fit in with the other mac applications, the implementation is ugly):
#!/usr/local/bin/python3
import tkinter as tk
import sys,os
class App:
def __init__(self):
self.root = tk.Tk()
tk.Label(master=self.root,text="content").pack()
tk.Button(master=self.root,text="new_window",command=self.open).pack()
def open(self):
os.system(sys.argv[0]+"&")
if __name__ == '__main__':
a=App()
a.root.mainloop()
I was alos think of maybe doing it with threads but I think I must be over thinking it so I came here.
Is there a generally accepted way of doing this in tk?

The generally accepted way is just like you describe: use Toplevel for additional windows, and hide the root window if you don't want to see it.
Another choice is to make your main program a non-GYI app, and each window is created by spawning a new process.

Hidden tk window and multyple Toplevels (Needs hiding the Tk and the osx menubar commands like view->change_colors would need to manaly decide where to direct comands):
It is easy to get into "which goes with what" hell with multiple Toplevels. Instead create a separate class for each new Toplevel with built in responses to button clicks, etc. Pass "root" to the class and let it create, update, and destroy as necessary, and keep track of it's own variables. If you want to use variables elsewhere, then store the class instance when the Toplevel classes are called and create the variables within the class as instance attributes.

Related

plt.savefig stops working when called from another file

I have a code that works well for bulk data analysis and plotting. But now i'm trying to incorporate it into a larger data analysis GUI. I find that when i run my code on its own, all goes well. But when i call it from the main code and run it from a tkinter button, it's not the same. Everything looks the same and it runs smoothly, the only difference is that no files are saved.
i think maybe it's a problem with which window is defined with "____init____"? or something with how i create and destroy Tk() windows within the subcode?
**the stackoverflow text editor uses underscores to make text bold/itallic, so for all cases that double underscores are used to wrap "init" or "main" in python, i had to use four on each side here
my code (saved as SubCode.py):
def AnalysisFunction():
*does things*
main = Tk()
os.chdir(OutputFolder)
plt.savefig('image.png')
main.destroy()
if __name__ == '__main__':
AnalysisFuntion()
the code i want to add mine into:
import SubCode
class TopLevel(Frame):
def __init__(self, master):
Frame.__init__(self,master)
*Creates main GUI window*
MyButton = Button(root, command = self.CallSubCode)
def CallSubCode(self):
SubCode.AnalysisFunction()
root = Tk()
main_window = TopLevel(root)
root.mainloop()
Any ideas why the subcode alone can save figures but it cannot when called by the larger GUI? FYI, it still creates all variables correctly when running through the larger GUI.
I think you should just save the image in SubCode.py without creating a tkinter window. i.e
def AnalysisFunction():
*does things*
os.chdir(OutputFolder)
plt.savefig('image.png')
if __name__ == '__main__':
AnalysisFuntion()
i figured it out, I had to put the whole SubCode within a class structure, then call it as its own Toplevel app. I think otherwise the plt.savefig command doesn't know which Tkinter window it is working with, and tries to find data in the "host" window, not the one which is handling the data.

Defined a window as a class, but unable to call mainloop function

I am so trash at coding that I have no idea how to utilise classes to make cool stuff :( --- I'm really new to GUI development, and I'm trying to make a simple maze game with a level selector. I have the maze program squared away, but I am somehow hopeless at Tkinter apparently, since I've been trying constantly for the last hour to find a solution online. As you might have noticed, this is my first post here.
I'm running this in PyCharm, using my decent computer on Windows 10. I'm especially trash at this IDE since I, for some reason, cannot install any libraries/ use any libraries that I see clearly installed in my list of libraries... but that's for another post. As I've mentioned, I've been trying to figure out a simple program for the past hour, but nothing seems to be working.
Nothing I find online is particularly useful, either, and the ones that might be are so hopelessly complex that I cannot understand what they are trying to achieve. I'm looking for a simple solution to a simple problem, and hopefully, this great community can help me out.
import tkinter as tk
class Window():
def __init__(self):
self = tk.Tk()
self.geometry("%dx%d+0+0" % (1920,1080))
root = Window()
root.mainloop()
Expected: Window appears
Observed: Program abruptly ends
Error:
Traceback (most recent call last):
File "C:/Users/(GD) ShadowPlague/PycharmProjects/GameDesign/Main.py", line 12, in <module>
root.mainloop()
AttributeError: 'Window' object has no attribute 'mainloop'
You create class in wrong way. You can't assign Tk() to self to create correctly class. External root will have nothing to do with internal self. First you create instance Window() and assign to variable root but later you create instance Tk() and assign to self but it will not change instance assigned to root.
First method: create Tk() inside class as self.root and then you use win.root
import tkinter as tk
class Window():
def __init__(self):
self.root = tk.Tk()
self.root.geometry("%dx%d+0+0" % (1920,1080))
win = Window()
win.root.mainloop()
Second method: inherit from Tk(). It needs Window(tk.Tk) and super().__init__
import tkinter as tk
class Window(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("%dx%d+0+0" % (1920,1080))
root = Window()
root.mainloop()

Adding Tix Widget to Tkinter Container

I'm working with Tkinter in Python 2.7 on Windows 7, and found the need to create a popup box with a tree-style list of checkboxes. I could not find this in Tkinter, or ttk. I did, however, find it in Tix in the CheckList widget. I got a working standalone example using Tix, but I cannot figure out how to add my Tix.CheckList to my ttk.Frame that controls my main program.
Surely I am not forced to use Tix framework from the ground up?
import Tix
import pandas as pd
import Tkinter as tk
class TreeCheckList(object):
def __init__(self, root):
self.root = root
self.cl = Tix.CheckList(self.root)
self.cl.pack(fill=Tix.BOTH, expand=Tix.YES)
self.cl.hlist.config(bg='white', bd=0, selectmode='none', selectbackground='white', selectforeground='black', drawbranch=True, pady=5)
self.cl.hlist.add('ALL', text='All Messages')
self.cl.hlist.add('ALL.First', text='First')
self.cl.setstatus('ALL.First', "off")
self.cl.hlist.add('ALL.Second', text='Second')
self.cl.setstatus('ALL.Second', "off")
self.cl.autosetmode()
def main():
root = Tix.Tk()
top = Tix.Toplevel(root)
checklist = TreeCheckList(top)
root.update()
top.tkraise()
root.mainloop()
if __name__ == '__main__':
main()
The above code works in a standalone program using all Tix widgets. However, when I try to implement this into my larger program, I receive a TclError: invalid command name "tixCheckList"
To simulate this in the standalone, I changed the lines:
root = Tix.Tk()
top = Tix.Toplevel(root)
to
root = tk.Tk()
top = tk.Toplevel(root)
I was hoping I could just implement a Tix.Toplevel, placing it on a tk.Tk() root, but same issue.
Am I only allowed to use Tix frames when using a Tix widget, or am I misunderstanding something? If anyone has good Tix documentation, I would LOVE whatever I can get. It seems good docs on it are few and far between. Or is this same functionality included in ttk and I've just overlooked it? It seems to be one of the only things left out.
I have just learned that apparently only root needs to be a Tix class. Since Tk, and therefore ttk, classes appear to be added to the Tix root just fine (since most of them extend the Tkinter classes anyway), this appears to be a "fix". So my problem may have been solved by changing just
root = tk.Tk()
to
root = Tix.Tk()
This did require that I pull Tix into a part of my program I wasn't wanting for encapsulation purposes, but I guess there's no other way.

Multithreading with Tkinter

I'm having some issues with a Tkinter-based GUI. Basically the GUI creates lots of threads and run them. When each thread has finished, I'd like it to update a label to inform the user of this specific thread completion.
I know Tkinter widgets are not thread-safe and that it is a bad practice to allow subthreads to update the view. So I'm trying to trigger an event on the main thread so it can update the view itself.
I'm running the simplified code sample below:
from Tkinter import *
from threading import *
def myClass(root):
def __init__(self, root):
self.root = root
# Other stuff populating the GUI
# Other methods creating new 'threading.Thread'
# objects which will call 'trigger_Event'
# Called by child threads
def trigger_Event(self):
self.root.event_generate("<<myEvent>>", when="tail")
# Called by main thread
def processEvent(self):
# Update GUI label here
if __name__ == '__main__':
root = Tk()
root.geometry("500x655+300+300")
app = myClass(root)
root.bind("<<myEvent>>", app.processEvent())
root.mainloop()
Unfortunately, this does not work: processEvent is never called. What am I missing ?
root.bind("<<myEvent>>", app.processEvent())
Here, you're binding myEvent to the return value of app.processEvent, because you're calling the function rather than just referring to it. Try removing the parentheses.
root.bind("<<myEvent>>", app.processEvent)

Make Tkinter window not always on top

I have a simple program as follows:
from Tkinter import *
class Run:
def __init__(self,parent):
parent.overrideredirect(True)
root = Tk()
app = Run(root)
root.mainloop()
When I run this program, the undecorated root window always stays on top.
How can I make it so any other window can be on top of it, whilst having no decorations?
I have tried setting 'topmost' to 'False' as well, but to no avail.
I am running Ubuntu 13.04.
Thanks in advance
This code below, will put your window on the background on every 100ms, no matter what, so everything will be in the front of it all the time. I think this is what you were asking for:
from tkinter import *
class Run:
def __init__(self):
self.root = Tk()
self.root.overrideredirect(True)
def put_back(self):
# this method will put the window to the background
self.root.lower()
# recursive calling of put_back method
self.root.after(100, self.put_back)
app = Run()
# Start the recursion
app.put_back()
app.root.mainloop()
Seems like this should happen automatically if I click a different window.

Categories

Resources