Repeated dialog window with Tkinter and Matplotlib on Mac OS X - python

I'm newbie in Tkinter. I try to use next code to open a file using tkFileDialog.askopenfilename and then plot something with Matplotlib:
import matplotlib.pyplot as plt
import Tkinter, tkFileDialog
root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
x = range(10)
plt.plot(x)
plt.show()
After running the above script I get dialog window to open my file. After file selection I get repeated dialog window to open the file and a new window at the bottom of my screen. I know that the problem is because of plt.show(). What happens and how to avoid dialog window reopening? Should I set a Matplotlib backend for my task?
My versions:
Tcl/Tk 8.5.9
Matplotlib 1.3.1
Tkinter $Revision: 81008 $
OS X 10.9.4
I have found two related stackoverflow questions:
pyplot-show-reopens-old-tkinter-dialog and
matplotlib-figures-not-working-after-tkinter-file-dialog
but no answers. It seems that root.destroy() is not working for me.

When run with python test.py, the following seems to work for me:
import matplotlib.pyplot as plt
import Tkinter, tkFileDialog
root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
root.destroy()
print file_path
x = range(10)
plt.plot(x)
plt.show()
I think it works because the Tk instance for the file dialog is destroyed before matplotlib fires up its own. Interestingly, it also works for me when run from
ipython --pylab=tk
where I would have expected a problem with starting the event loop twice. The canonical solution in this case would be to check if Tk is already running before firing it up (again).
I'm on MacOSX 10.7.5, custom built matplotlib (shouldn't matter).
The only thing I noticed was that after experimenting with this, the touchpad swipe gestures on my Mac no longer work... Looking into this.
Edit
Here is a breakdown of the Tk commands executed by tkFileDialog.askopenfilename():
# breakdown of tkFileDialog.askopenfilename()
import Tkinter as Tk
window = Tk.Tk()
window.withdraw()
w = Tk.Frame(window)
s = w.tk.call('tk_getOpenFile', *w._options({}))
print s
w.destroy()
window.destroy()
When I run this (with python test.py), I get the file open dialog, where I can choose a file. Upon "OK" it prints the file name and exits. This works every time on my system. However, sometimes the 3-finger gestures on my touchpad stop working while running this program! And they don't come back after the program exits!! Not even after I kill the terminal the program was running in!!!
The only way I found to bring them back is to add the following matplotlib code to test.py:
import matplotlib
matplotlib.use('tkagg')
import matplotlib.pyplot as plt
plt.figure() # simplified from plt.plot(range(10))
plt.show()
and then click on the title bar of "Figure 1". This instantly brings back the 3-finger gestures. I suspect this is simply a bug in Tkinter. (I'm on Tcl/Tk 8.5, by the way)
I cannot reproduce the behavior that the file open dialog is constantly relaunched on my system.
Could you please describe what happens on your system if you launch test.py, without any matplotlib commands?
Alternatively, since Tkinter is old and apparently buggy, may I suggest to use Qt instead? Not only does it look much nicer, it is also snappier and I didn't have any problems with bugs.
Edit 2
I have broken down the Tk actions that matplotlib takes when executing the above commands in a non-interactive environment (i.e. with python test.py and not from iPython). These are the essential backend calls:
import matplotlib.backends.backend_tkagg as backend
figManager = backend.new_figure_manager(1)
figManager.show()
backend.show.mainloop()
These are still backend independent. I.e., for a Qt figure, simply use:
import matplotlib.backends.backend_qt4agg as backend
If we break this down further to the backend-specific layer, we have:
import matplotlib.backends.backend_tkagg as backend
import Tkinter as Tk
window = Tk.Tk()
window.withdraw()
# uncomment this to use the same Tk instance for tkFileDialog and matplotlib
#import tkFileDialog
#fname = tkFileDialog.askopenfilename(master=window)
#print fname
# figManager = backend.new_figure_manager(1)
from matplotlib.figure import Figure
figure = Figure()
canvas = backend.FigureCanvasTkAgg(figure, master=window)
figManager = backend.FigureManagerTkAgg(canvas, 1, window)
# figManager.show()
window.deiconify()
# backend.show.mainloop()
Tk.mainloop()
First, run with the tkFileDialog calls commented out and check if the matplotlib figure appears and behaves correctly. Then uncomment the tkFileDialog calls and see if you finally get the expected behavior.
If not, one has to continue breaking down FigureCanvasTkAgg and FigureManagerTkAgg to understand what is going on...
Edit 3
OK, since the problem persisted, let's break down matplotlib's Tk calls even further. The following code completely isolates all of matplotlib's Tk actions that I consider essential (so it is no longer necessary to import anything from matplotlib!). Note that I left out generating the toolbar and assigning lots of callbacks and keypress events. If the code below works now, then the problem lies with these. If it doesn't work, we can conclude that it is purely a Tk problem, and most likely a bug that should be reported. Here is the code:
import Tkinter as Tk
window = Tk.Tk()
window.withdraw()
# uncomment this to use the same Tk instance for tkFileDialog and matplotlib
#w = Tk.Frame(window)
#fname = w.tk.call('tk_getOpenFile', *w._options({}))
#print fname
#w.destroy()
# canvas = backend.FigureCanvasTkAgg(figure, master=window)
_tkcanvas = Tk.Canvas(master=window, width=640, height=480, borderwidth=4)
_tkphoto = Tk.PhotoImage(master=_tkcanvas, width=640, height=480)
_tkcanvas.create_image(320, 240, image=_tkphoto)
_tkcanvas.focus_set()
# figManager = backend.FigureManagerTkAgg(canvas, 1, window)
window.wm_title("Figure 1")
window.minsize(480, 360)
_tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
# figManager.show()
window.deiconify()
# backend.show.mainloop()
Tk.mainloop()
Please play around with commenting out some lines and see if you can get it working correctly. If not, I would conclude that this is a bug in Tkinter, which should be reported.

Related

Tkinter not working the first time it runs with withdraw()

I am trying to make a Tkinter script to select files through Windows File Explorer. I don't need the Tkinter window to show, just the File Explorer interface.
import tkinter
from tkinter import filedialog
import os
window = tkinter.Tk()
#window.geometry("1x1")
window.withdraw()
def open_files():
files = filedialog.askopenfiles(mode='r')
global filenames
filenames = [os.path.abspath(file.name) for file in files]
window.destroy() # analysis:ignore #says "window" is undefined becasue of "del window" below
window.after(0, open_files)
window.mainloop()
del window
The first time I run this in Spyder, if window.withdraw() is not commented out, the console just shows runfile(*my_file_name*) and the code does... something... in the background, but nothing seems to actually happen. Nothing changes on-screen, but I cannot type in the console so I know the code is running.
If I open a new console tab and run the code with window.withdraw() commented out, everything works, and the Tkinter GUI is visible. If I then run this code again in the same tab, with window.withdraw() not commented out, then the code works as intended, with only the File Explorer window opening up, and the Tkinter GUI staying hidden. This is the case even if I click the "Remove all variables" button in Spyder, so as far as I understand the code is not saving any variables that allow it to run properly after the first time.
My question is, why does this code work the 2nd, 3rd, 4th, etc. time I run it, but not the first time?
I kept playing around, and changed -alpha to alpha and got this error:
TclError: wrong # args: should be "wm attributes window ?-alpha ?double?? ?-transparentcolor ?color?? ?-disabled ?bool?? ?-fullscreen ?bool?? ?-toolwindow ?bool?? ?-topmost ?bool??"
So I ended up changing window.attributes('-alpha',0) to window.attributes('-topmost',True, '-alpha',0), and this works! It brings up File Explorer on the first run without showing the Tkinter window. Thank you #Thingamabobs for your help.
My final code is:
import tkinter
from tkinter import filedialog
import os
window = tkinter.Tk()
window.attributes('-topmost',True, '-alpha',0)
filenames = [os.path.abspath(file.name) for file in filedialog.askopenfiles(mode='r')]
window.destroy()
del window

tk windows in Jupyter

I'm trying to get a tk window to display inline in Jupyter -- similar to the effect that %matplotlib inline has, but for a non-matplotlib tk window. Specifically, I'm trying to get graphics.py windows to display inline. Here would be a minimal working example:
# Some kind of replacement for %matplotlib inline:
%gui tk # <--- doesn't solve the problem, only connects the event loops?
# minimal graphics.py working example:
from graphics import *
c = Circle(Point(50,50), 10)
c.draw(win)
win.getMouse() # Pause to view result
win.close() # Close window when done
Is this possible/feasible? If it's not already implemented, is this something I could attempt to make myself?

Python Tkinter not working in a .py file

My problem is that my python code is not working when I run it as a .py file. Here is the code:
import tkinter
tk=tkinter.Tk()
canvas=tkinter.Canvas(tk, width=500, height=500)
canvas.pack()
There is more code to it than that, but that is the relevant stuff. It works fine when I use the python shell or type it directly into the python console, but when I run it as a .py file, it seems to skip this code and go on to the rest, without displaying a canvas. I am using windows, but I am not sure what version of python I'm using.
I was also using
from * import tkinter
before, with relevant changes to the code and i changed it to try and help fix it. It didn't work :(
You are missing the eventloop at the end:
import tkinter
tk=tkinter.Tk()
canvas=tkinter.Canvas(tk, width=500, height=500)
canvas.pack()
# Enter into eventloop <- this will keep
# running your application, until you exit
tk.mainloop()
Only a personal recommendation: don't use tk as a variable name, use app or root or even win/window

Python Tkinter Flashing Standard Window

I am building an Interface with TKinter and I have the problem, that whenever create a new window the standardized tkinter window of 200x200 pixel with nothing in it flashes up for a fraction of a second and AFTER that all my modifications (widgets ect.) are made. This happens before AND after calling the mainloop.
The Main-Interface is created.
Mainloop stats
Flashing window
Main-Interface appears
Also after Mainloop was called this happens with newly created windows.
Main-Interface appears--> Push a button, that creates a new window
Flashing window
new window appears
Sadly I cannot give you a sample code... If I try to do a minimal example, this doesn't happen. Maybe the standard window is created, but it is changed so fast, that it doesn't appear on screen. I don't even know what to look up in this case... searching "tkinter flashing window" yields nothing.
EDIT: I found the source of the problem. It seems to be caused by wm_iconbitmap, FigureCanvasTkAgg and tkinter.Toplevel. If you remove the the icon out of the code, it works fine, no flashing. But if I use it together with one of the other, the window flashes when created. Try it out with the code below. You have to put the icon in the working directory of course.
Here is a code sample and the link to the icon I am using, but I suppose any icon will do.
# coding=utf-8
import numpy as np
import matplotlib as mpl
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
import os
class INTERFACE(object):
def __init__(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.EXIT)
self.root.wm_iconbitmap( os.path.abspath("icon.ico")) #<---- !!!!!!
self.root.geometry("1024x768")
canvas = FigureCanvasTkAgg(self.testfigure(), master=self.root) #<---- !!!!!!
canvas.get_tk_widget().grid(sticky=tk.N+tk.W+tk.E+tk.S)
self.root.rowconfigure(0, weight=1)
self.root.columnconfigure(0, weight=1)
def testfigure(self):
x=np.linspace(0, 2*np.pi,100)
y=np.sin(x)
fig = mpl.figure.Figure()
sub = fig.add_subplot(111)
sub.plot(x,y)
return fig
def EXIT(self):
Top = tk.Toplevel(master=self.root)
Top.wm_iconbitmap( os.path.abspath("icon.ico")) #<---- !!!!!!
Top.transient(self.root)
Top.resizable(width=False, height=False)
Top.title("Exit")
tk.Message(Top,text="Do you really want to quit?", justify=tk.CENTER, width=300).grid(row=0,columnspan=3)
tk.Button(Top,text="YES",command=self.root.destroy).grid(row=1,column=0)
tk.Button(Top,text="No",command=self.root.destroy).grid(row=1,column=1)
tk.Button(Top,text="Maybe",command=self.root.destroy).grid(row=1,column=2)
def start(self):
self.root.mainloop()
if __name__ == '__main__':
INTERFACE().start()
I know this is an old question, but I've experienced a similar situation and have found a solution.
In my case, I've isolated the issue to the use of iconbitmap. I've managed to solve it by calling iconbitmap with the after method just before calling root.mainloop().
Example:
from tkinter import *
root = Tk()
w = Label(root, text="Hello, world!")
w.pack()
root.geometry('300x300+500+500')
root.after(50, root.iconbitmap('icon.ico'))
root.mainloop()
This method has worked on Toplevel() windows with icons as well.
Tested on Win 8.1 with Python 3.5.0.
Edit: Upon further inspection I've noticed that the behavior changes relative to root.geometry's presence as well. My initial example didn't have it and I only noticed after a few tries that it still had the same issue. The time delay in the after method doesn't seem to change anything.
Moving root.geometry below the after method yields the same issue for some reason.
Most likely, somewhere in your initialization code you're calling update or update_idletasks, which causes the current state of the GUI to be drawn on the screen.
Another possible source of the problem is if you're creating multiple instances of Tk rather than Toplevel.
Without seeing your code, though, all we can do is guess.
Your best route to solving this problem is to create a small example that has the same behavior. Not because we need it to help you, but because the effort you put into recreating the bug will likely teach you what is causing the bug.
This should work but it requires win32gui
import win32gui
def FlashMyWindow(title):
ID = win32gui.FindWindow(None, title)
win32gui.FlashWindow(ID,True)

Matplotlib figures not working after Tkinter file dialog

I'm using the following function which I found as a reply to this question to show a dialog window for file selection.
[ Edit: Turns out the distro differences here are merely because Matplotlib is using gtk3agg on Fedora and TkAgg for drawing windows on each system ]
On Fedora 18: Everything works fine.
On Ubuntu 12.10: Matplotlib hangs after closing any figure displayed after the file dialog. For example, in the code below, on Ubuntu I can never get to the "made it" line. I am still able to type in the terminal, though nothing happens. If I remove the file dialog, Matplotlib figures work as expected.
import Tkinter, tkFileDialog
import pylab
def ask_for_config_file():
print "Please provide location of configuration file."
root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
return file_path
def main():
config_file_path = ask_for_config_file()
pylab.figure()
pylab.show()
print "Made it."
Any suggestions?
I just needed a root.destroy() at the end of the dialog function!

Categories

Resources