plt.savefig stops working when called from another file - python

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.

Related

Continue a code after calling a class method which opens a tkinter window

I have created a class "Node" which creates a binary tree. (I know i can use binarytree module but its a project given to me in DSA subject.)
class Node:
def __init__(self, data) -> None:
#initialisation
def addNode(self, data):
# Code to add data.
def traverse(self):
#traverses the tree and returns a dict
def view(
self,
length=500,
width=1000,
yDist=50,
xDistScale=0.25,
title='Tree View',
):
# shows the tree in a tkinter window as in the screenshot attached
tree = self.traverse()
root = tk.Tk()
self.root=root
root.geometry(str(width)+'x'+str(length))
root.title(title)
# ........ some code which places the nodes on the window using the place() method
root.mainloop()
Now, I import the code in an another file, create an instance of the class add some nodes and call the view() method it works fine but the code after the view() methd does not run till I close the tkinter window.
How can I make the code after view() run without the window being closed?
Its ok if the window is not able to update.
Code where I import and use the node class :
t1 = Node(15)
t1.addNode(12)
t1.addNode(27)
t1.addNode(7)
t1.addNode(14)
t1.addNode(20)
t1.addNode(88)
t1.addNode(23)
t1.view()
# The code here does not run until I close the window.
Output of the above code :
Link to image
I tried googling and also viewed following stackoverflow posts:
Calling a tkinter application from a different class in python,
A code needs to be continued...(Python with Tkinter)
How would I continue to run code after importing a Tkinter file?
Few other websites and guides...
But nothing was helpful.
Please help/guide me.
I am new to StackOverflow and Python.
Thanks in Advance :)
Method 1: Don't update window
You could replace the root.mainloop() with root.update(). This will stop showing any changes to the window. The window will only and always close if the program stops running.
Method 2: using threading
You could useimport threading to run t1.view() in another thread: threading.Thread(target=t1.view).start(). This may result in a Tcl_AsyncDelete error.

Is the objects of tkinter.Tk independent of each other?

I write a python script (named script2.py) which includes tkinter. To be convenient, I write a function at the bottom. It looks like:
import tkinter
import pygubu
class GuiApp2:
def __init__(self,master):
self.builder=pygubu.Builder()
self.builder.add_from_file('test.ui')
self.builder.get_object('Frame_main',master)
self.list_box_a=self.builder.get_object('Listbox_a')
self.lba_value_set=tkinter.StringVar()
self.list_box_a['listvariable']=self.lba_value_set
def set_value_set(self,the_value_set):
self.lba_value_set.set(the_value_set)
def run(the_value_set):
master=tkinter.Tk()
app=GuiApp1(master)
app.set_value_set(the_value_set)
master.mainloop()
def main():
run(['1','2','3'])
if __name__ == '__main__':
main()
And then I write another script (named script1.py) which calls the function at the bottom of the script above. It is:
import tkinter
import pygubu
import script2
class GuiApp1:
def __init__(self,master):
self.builder=pygubu.Builder()
self.builder.add_from_file('mainapp.ui')
self.builder.get_object('Frame_main',master)
self.button_show=self.builder.get_object('Button_show')
self.button_show['command']=self.command_for_button_show
def command_for_button_show(self):
script2.run(['1','2','3'])
def main():
master=tkinter.Tk()
app=GuiApp1(master)
master.mainloop()
if __name__ == '__main__':
main()
When I run script2.py, everything is fine. But when I run script1.py which imports script2.py the Listbox in script2.py is empty.
Of course, these two scripts are not the files I use in my project. The files I really use is too long and difficult to read.
In order to find out the problem, I inserted several print functions in my script to show the values of the variable in my scripts. Finally, every print result is fine except the Listbox.
Thus I simplified the real scripts to these scripts which are easy to read.
I guess maybe the master(tkinter.Tk()) in script1.py affect the master in script2.py. Because the logic of GUI Management in tkinter is different from it in dotNet.
Is there anyone who's met a similar problem or have some idea about that?
Yes, they are independent. Each time you create an instance of Tk, you create a new instance of an embedded Tcl interpreter. As a consequence, the widgets in one know nothing about the widgets in another.
A well written tkinter program should never create more than one instance of Tk. If you need additional windows you should create instances of Toplevel. You also should call mainloop() exactly once.

Tkinter add Widgets (a Button) to a new second Frame

I created a Frame, gave it a menubar. Works just fine. The purpose of the entry in the menubar is to open a new frame, in which u can change some settings. The creation of the new Window works also. However I can't create widgets on the new created window. I tried it with a Button and got a
TclError: can't invoke "button" command: application has been destroyed
I tried to google it and found Cannot invoke button command: application has been destroyed which didn't quite helped me.
Further I found a solution were u have to create a parent class (which inherrits from Frame) and than create all other Frames within it, but on the first view it looked pretty complicated. Especially because the creation of the second window seems to work in the first place.
I know this is probably a really basic question, so thanks in advance for your time
def perfSettings():
perfFrame = Tk(className=" Performanz Einstellungen")
perfFrame.configure(bg='#F2F2F2')
perfFrame.geometry("300x300")
perfFrame.mainloop()
btn = Button(master=perfFrame, text='Speichern', command=myPerfSettingValue.getValues, width=37)
btn.pack()
# Button(perfFrame, text='Abbrechen', command=perfFrame.destroy, width=37).grid(row=0 ,column=1 )
class perfSettingsValue:
def __init__(self):
self.bvhSteps = 0
def getValues(self):
pass
#Hauptfenster
root = Tk(className="BoneMapping & SkeletonEstimation")
root.configure(bg='#F2F2F2')
root.geometry("1300x600")
myPerfSettingValue = perfSettingsValue()
menubar = Menu(root)
sdmenu = Menu(menubar, tearoff=0)
sdmenu.add_command(label="Performanz", command=perfSettings)
menubar.add_cascade(label='Einstellungen',menu=sdmenu)
root.config(menu=menubar)
The key problem here is that you are trying to add a button after starting the mainloop which effectively blocks the execution of the program. The error you are getting is because the line that adds the button gets executed after the window has been closed.
Your problem will be solved if you modify your function like this:
def perfSettings():
perfFrame = Tk(className=" Performanz Einstellungen")
perfFrame.configure(bg='#F2F2F2')
perfFrame.geometry("300x300")
btn = Button(master=perfFrame, text='Speichern', command=myPerfSettingValue.getValues, width=37)
btn.pack()
perfFrame.mainloop()
This is not the only problem though. Instead of creating a new instance of Tk, you should create a new Toplevel instance, which will, in your case, act just as a Tk instance, but have a lot less tendency to cause trouble.
Finally, you should consider reading on the object oriented approach to designing tkinter applications. There are far too many variants of that to be appropriately elaborated here but I certainly recommend you take the effort to learn to use one of them. It will make your code more comprehensible and maintainable. My usual approach is to create a class that inherits from Toplevel or Tk for every type of window I am going to use.

Simple Python GUI program won't run, says RESTART

I'm trying to create a python program that pulls up a simple window that displays the text "Hello World?" I've imported tkinter and have created a class called MyGUI that should create a simple window. Then I create an instance of the MyGUI class. When I hit "F5" or run the programming after saving it, I get an error:
RESTART: C:....my filepath.....
>>>
Here is the code:
import tkinter
class MyGUI:
def init (self):
# Create the main window widget.
self.main_window = tkinter.tk()
# Create a Label widget containing the
# text 'Hello World!'
self.label = tkinter.Label(self.main_window, text="Hello World!")
# Call the Label widget's pack method.
self.label.pack()
# Enter the tkinter main loop.
tkinter.mainloop()
# Create an instance of the MyGUI class
my_gui = MyGUI()
What causes the "RESTART" error? Does where I save my .py file matter for this program?
Any help would be greatly appreciated. Thanks
The good news:
Your code works (in that it doesn't crash in python3, as is)!
The bad news:
Your code doesn't do anything :( Your only function would raise an exception if called
You have a code-unrelated problem
To resolve problem #1, change init to __init__ and tkinter.tk to tkinter.Tk()
__init__ is the function called by default on instance construction. The underscores are important if you want to override it. The other issue is just a typo.
You're broader problem is... broader. yes it matters where you save your file. If you don't save it in the place you are running python from, you need to supply an absolute path to it, or a relative path from the place you are running from. This is a broad topic, but pretty important and not too challenging. Maybe try here, or any python tutorial.
I don't know what type F5 does on your computer. I would not in general expect it to run python code. Are you in an IDE, then maybe it does run python code? Are you playing call of duty, because then it's more likely to lob a virtual grenade? F5 is app-dependent, probably not a universal binding on your machine

Tkinter Keyboard Binds

I'm working on an interface using Tkinter and the canvas widget, and so far have found answers to issues I have had from others questions and the answers posted, but I am stumped on this one.
I have several keyboard binds in the class where my GUI elements are created, and they all work fine when the program is started. The binds looks something like this:
self.canvas.get_tk_widget().bind("<Control-o>",self.flash_open)
and are within the __init__ function of the class. As of yesterday, I initialized this class
to start the program, then waited for the user to select open from a menu, which then opened (among other things) a tkmessagebox
self.specfilename =askopenfilename(filetypes=[("spec", "")],initialdir= self.pathname)
With this filename I am able to retrieve my required variable names from a certain filetype (inconsequential to the problem). Today I modified the __init__ function to call the open function when the program starts. Since nothing else can be done until this file is opened, it would make sense to open it first thing. Once the file is selected and the Tkmessagebox is closed, the root window is active, but none of the keyboard binds work. My functions still work using the menu/buttons assigned to them, just not the binds. I have tried binding the shortcuts to the root, with the same result, and am now thinking it may be an issue with the order I am calling them
def __init__(self):
...
self.openfile() #calls the tkmessagebox
self.root.mainloop() #starts gui
I had actually run into this issue before, where a toplevel() instance was closed/destroyed and disabled the binds of the parent window. There isn't any error message to speak of, the binds just don't do anything. I should also mention I have tried to focus on the root window again using
self.openfile()
self.root.mainloop()
self.root.focus_set()
I got around it before by using the wm_withdraw() and wm_deiconify() functions to simply hide the child window, then close it after the program is complete. This fix is a little more difficult to apply in this case however. If anyone can shed some light on the cause of the problem I'd appreciate it.
Edit:
I've written up a runable code segment to show exactly what my issue is.
import os
from tkFileDialog import askopenfilename
from Tkinter import *
class Start:
def __init__(self):
self.root = Tk()
self.root.title('Binding Troubles')
menubar = Menu(self.root)
#add items and their commands to the menubar
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Do work", command=self.do_work)
filemenu.add_command(label="Open File",command=self.openfile)
menubar.add_cascade(label="File", menu=filemenu)
#bind control-o to perform the do work function
self.root.bind("<Control-o>",self.flash_do_work)
self.root.bind("<Control-O>",self.flash_do_work)
#add the menubar to the GUI
self.root.config(menu=menubar)
#initially open a tkdialog to open a file
self.openfile()#comment out this line to make the bind work
self.root.focus()#also tried self.root.focus_set()
self.root.mainloop()
def flash_do_work(self,event):
#indirect tie to the do_work() function, I'm don't know a
#proper way to make functions handle calls from both events and non-events
self.do_work()
def openfile(self):
#gets current path
self.pathname = os.getcwd()
#Requests filename using a tkdialog
self.filename =askopenfilename(initialdir= self.pathname)
print self.filename
def do_work(self):
#placeholder for actual function; shows whether the bind is working or not
print "work"
Start()
The bind will work if self.openfile() is removed from __init__, and used only from the menu
Another Edit: I've updated the example again, giving a menu option to run the openfile() function. I noticed that if openfile() is called in __init__, the bind will not work. But if next the openfile function is called again, this time manually from the menu, the bind will start working again. Not exactly sure what to take from this. Also, my apologies for the post getting so long.
Change
self.openfile()
to
self.root.after(1, self.openfile)
This moves the call to askopenfilename into the main event loop. Having it outside the main event loop is somehow clobbering your event bindings.
I had this kind of problem a couple of times and it took quite a while until I found a solution I was comfortable with. As #Steven Rumbalski suggests I tried with delaying the application, which works but seems shaky.
Then I found the functions for waiting until something is complete, in this case wait_visibility(widget). This will delay execution until the widget is visible, which seems to be the thing to be waiting for. Try this:
self.root.wait_visibility(self.root) # Wait for root to be displayed
self.openfile()
Now; I'm not sure why this is so, and it seems that there may be differences depending on platform: Tkinter window event . This has nevertheless worked for me on Windows10 and Python 3.10.5.

Categories

Resources