Python tk(): No window appears when using in scripts (console works) - python-2.7

I have the following problem with this easy script:
from Tkinter import *
root = Tk()
while 1:
pass
I think, after the 2nd line everyone would expect a Tkinter window would appear. But it does not!
If I put this line into the Python console (without the endless-while-loop), it works.
[I wanted to add an image here, but as I'm new I'm to allowed to :-(]
But running the script (double-clicking on the *.py file in the Windows Explorer) results only in an empty Python console!
Background:
Actually I want to use Snack for Python. This is based on Tkinter. That means I have to create an Tk() instance first. Everything works fine in the Python console. But I want to write a bigger program with at least one Python script thus I cannot type the whole program into the console everytime :-)
I have installed Python 2.7 and Tcl/Tk 8.5 (remember: it works in the console)
EDIT: So here's my solution:
First, I create a class CSoundPlayer:
from Tkinter import*
import tkSnack
class CSoundPlayer:
def __init__(self, callbackFunction):
self.__activated = False
self.__callbackFunction = callbackFunction
self.__sounds = []
self.__numberOfSounds = 0
self.__root = Tk()
self.__root.title("SoundPlayer")
tkSnack.initializeSnack(self.__root)
def __mainFunction(self):
self.__callbackFunction()
self.__root.after(1, self.__mainFunction)
pass
def activate(self):
self.__activated = True
self.__root.after(1, self.__mainFunction)
self.__root.mainloop()
def loadFile(self, fileName):
if self.__activated:
self.__sounds.append(tkSnack.Sound(load=fileName))
self.__numberOfSounds += 1
# return the index of the new sound
return self.__numberOfSounds - 1
else:
return -1
def play(self, soundIndex):
if self.__activated:
self.__sounds[soundIndex].play()
else:
return -1
Then, the application itself must be implemented in a class thus the main() is defined when handed over to the CSoundPlayer() constructor:
class CApplication:
def __init__(self):
self.__programCounter = -1
self.__SoundPlayer = CSoundPlayer(self.main)
self.__SoundPlayer.activate()
def main(self):
self.__programCounter += 1
if self.__programCounter == 0:
self.__sound1 = self.__SoundPlayer.loadFile("../mysong.mp3")
self.__SoundPlayer.play(self.__sound1)
# here the cyclic code starts:
print self.__programCounter
CApplication()
As you can see, the mainloop() is called not in the constructor but in the activate() method. This is because the CApplication won't ever get the reference to CSoundPlayer object because that stucks in the mainloop.
The code of the class CApplication itself does a lot of overhead. The actual "application code" is placed inside the CApplication.main() - code which shall be executed only once is controlled by means of the program counter.
Now I put it to the next level and place a polling process of the MIDI Device in the CApplication.main(). Thus I will use MIDI commands as trigger for playing sound files. I hope the performance is sufficient for appropriate latency.
Have you any suggestions for optimization?

You must start the event loop. Without the event loop, tkinter has no way to actually draw the window. Remove the while loop and replace it with mainloop:
from Tkinter import *
root = Tk()
root.mainloop()
If you need to do polling (as mentioned in the comments to the question), write a function that polls, and have that function run every periodically with after:
def poll():
<do the polling here>
# in 100ms, call the poll function again
root.after(100, poll)
The reason you don't need mainloop in the console depends on what you mean by "the console". In IDLE, and perhaps some other interactive interpreters, tkinter has a special mode when running interactively that doesn't require you call mainloop. In essence, the mainloop is the console input loop.

Related

Why does Python crash when my PyQt window becomes inactive/is resized?

While playing around with different ways to show a webcam feed (obtained using imageio/ffmpeg) in a PyQt4 window, I stumbled upon this answer. After implementing this in Python 2.7 as an ImageDisplayWidget class (as summarized below), everything seems to work just fine: A window opens, showing my webcam feed without a glitch. If I close the window, everything is stopped and closed neatly.
But... Whenever I click anywhere outside this PyQt window (while it is showing the webcam feed), causing it to lose focus, Python.exe crashes with an unhandled win32 exception. The same happens when I try to resize the window.
I am probably making some kind of exceedingly silly beginner's mistake, but I just don't see it. Can anyone point me in the right direction? Am I breaking some basic rule of (Py)Qt or even Python?
Here's a minimal example:
import sys
import numpy
from PIL import Image, ImageQt # pillow
from PyQt4 import QtGui, QtCore
class DummyVideoGrabber(QtCore.QTimer):
signal_image_available = QtCore.pyqtSignal(QtGui.QImage)
def __init__(self):
super(DummyVideoGrabber, self).__init__()
self.timeout.connect(self.update_image)
self.counter = 0
def update_image(self):
# Dummy rgb image (in reality we get a numpy array from imageio's Reader)
self.counter += 1
numpy_image = numpy.zeros(shape=(480, 640, 3), dtype=numpy.int8)
numpy_image[:, :, self.counter%3] = 255
qt_image = ImageQt.ImageQt(Image.fromarray(numpy_image, mode='RGB'))
# Emit image
self.signal_image_available.emit(qt_image)
class ImageDisplayWidget(QtGui.QWidget):
"""
Custom widget that displays an image using QPainter.
Mostly copied from: https://stackoverflow.com/a/22355028/4720018
"""
def __init__(self, size_wxh=None, parent=None):
super(ImageDisplayWidget, self).__init__(parent)
self.image = QtGui.QImage()
def set_image(self, qimage, resize_window=False):
self.image = qimage
self.repaint()
def paintEvent(self, QPaintEvent):
if not self.image:
return
painter = QtGui.QPainter(self)
painter.drawImage(self.rect(), self.image, self.image.rect())
app = QtGui.QApplication(sys.argv)
# instantiate a display object
display = ImageDisplayWidget()
display.resize(640, 480)
display.show()
# instantiate a grabber object
grabber = DummyVideoGrabber()
grabber.signal_image_available.connect(display.set_image)
grabber.start(100) # timer interval in ms
# start the event loop
app.exec_()
I found that the crash can be prevented by adding a wasActiveWindow flag (initialized to True in the constructor) and encapsulating the drawImage() call in some logic like so:
if self.isActiveWindow():
if self.wasActiveWindow:
painter.drawImage(self.rect(), self.image, self.image.rect())
self.wasActiveWindow = True
else:
self.wasActiveWindow = False
However, resizing the window still crashes python.
Problem solved by keeping a reference to the qt_image as self.qt_image:
...
# Emit image
self.qt_image = ImageQt.ImageQt(Image.fromarray(numpy_image, mode='RGB'))
self.signal_image_available.emit(self.qt_image)
...
This way it works as it should. Don't need the self.wasActiveWindow workaround anymore.
Still not sure why not keeping a reference would lead to a low-level python crash though...

Unable to save persistence file using wxPython PersistenceManager

I'm developing a GUI application using wxpython that has roughly 110 user-chosen parameters. Since I would like for users to be able to save these options to a project file, I decided to use the PersistenceManager module that's included with wxPython.
The persistence works great as long as I don't try to specify the filename in which to save the settings, i.e., I use the default value (C:\users\username\AppData\programName\Persistence_Options), and just have the program save the settings when it exits.
What I'm trying to do is allow the user to choose a file to save the settings (since they might have multiple projects with different options). But, when I use the SetPersistenceFile method with the user-specified filename, no file gets saved, and no error message is returned, even though it's definitely executing those lines of code, which are given below. (The OnSave function is a method of the main window of the program.)
def OnSave(self, e):
self.dirname = os.getcwd()
if self.ProjectFile == '':
dlg = wx.FileDialog(self, "Save project file", self.dirname, "", "Project configuration file (.prj)|*.prj", wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
if dlg.ShowModal() == wx.ID_CANCEL:
return
else:
self.ProjectFile = os.path.join(dlg.GetDirectory(), dlg.GetFilename())
#print self.ProjectFile
if self.ProjectFile != '':
print "Made it to here (Save)..."
#self.Register(self) # Also tried calling Register in __init__
self._persistMgr = PM.PersistenceManager.Get()
print self.ProjectFile # Gives correct filename
self._persistMgr.SetPersistenceFile(self.ProjectFile)
self._persistMgr.Save(self)
print "Finished saving."
I've tried using a local PersistenceManager object, rather than having it as a class member, and this made no difference. Interestingly enough, if I declare the self.__persistMgr object in the window's __init__ function and use the SetPersistenceFile method with a hard-coded filename there, it writes the file, however this is not helpful since the user needs to specify that at runtime.
Does anyone know why the file isn't saving and how I can fix this?
Not sure why your code is giving you grief, the following works on Linux, although that may be of no consolation to you.
It is cobbled together from a number of sources, not having come across the PersistenceManager myself.
#!/usr/bin/python
import wx , os
import wx.lib.agw.persist as PM
class persist(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "A window that maintains size and position after restart")
self.Bind(wx.EVT_CLOSE, self.OnClose)
# Very important step!!
if self.GetName() == "frame":
self.SetName("My Persist Frame") # Do not use the default name!!
dirname = os.getcwd()
dlg = wx.FileDialog(self, "Project file", dirname, "", "Project configuration file (.prj)|*.prj|All files (*.*)|*.*", wx.FD_SAVE)
if dlg.ShowModal() == wx.ID_CANCEL:
_configFile = os.path.join(os.getcwd(), "persist-saved.prj") # getname()
else:
_configFile = os.path.join(dlg.GetDirectory(), dlg.GetFilename())
print _configFile
self._persistMgr = PM.PersistenceManager.Get()
self._persistMgr.SetPersistenceFile(_configFile)
self._persistMgr.RegisterAndRestoreAll(self)
self._persistMgr.Save(self)
def OnClose(self, event):
self._persistMgr.SaveAndUnregister()
event.Skip()
if __name__ == '__main__':
my_app = wx.App()
p = persist(None)
p.Show()
my_app.MainLoop()
Result in my .prj file:
[Persistence_Options]
[Persistence_Options/Window]
[Persistence_Options/Window/My\ Persist\ Frame]
x=('int', '9')
y=('int', '134')
w=('int', '319')
h=('int', '78')
Maximized=('bool', 'False')
Iconized=('bool', 'False')
Note the setting of the name this would be true of whatever it is that you are saving for persistence.
Edit: With regard to your comment
I think that you might be hoping for more than the PersistenceManager can cope with currently.
wxWidgets has built-in support for a (constantly growing) number of controls. Currently the following classes are supported:
wxTopLevelWindow (and hence wxFrame and wxDialog)
wxBookCtrlBase (i.e. wxNotebook, wxListbook, wxToolbook and wxChoicebook)
wxTreebook
To automatically save and restore the properties of the windows of classes listed above you need to:
Set a unique name for the window using wxWindow::SetName(): this step is important as the name is used in the configuration file and so must be unique among all windows of the same class.
Call wxPersistenceManager::Register() at any moment after creating the window and then wxPersistenceManager::Restore() when the settings may be restored (which can't be always done immediately, e.g. often the window needs to be populated first). If settings can be restored immediately after the window creation, as is often the case for wxTopLevelWindow, for example, then wxPersistenceManager::RegisterAndRestore() can be used to do both at once.
If you do not want the settings for the window to be saved (for example the changes to the dialog size are usually not saved if the dialog was cancelled), you need to call wxPersistenceManager::Unregister() manually. Otherwise the settings will be automatically saved when the control itself is destroyed.
Source: http://www.ccp4.ac.uk/dist/checkout/wxPython-src-3.0.2.0/docs/doxygen/out/html/overview_persistence.html
Of course I could be hopelessly wrong, as I have already admitted, I haven't used it before or really investigated it properly.

Closing Tkinter GUI from another function A and passes Tkinter variable to function A

This question is about programming in Python 2.7.x
I wanted to code a programme where there are two functions exist: one of those is a method to get input from the user, and the other one is to show the input. Both are supposed to be done in GUI. Let's call the first function as GET TEXT function, and the second as SHOW TEXT function; my strategy is to open a GUI, show a text box, and put a button to go to SHOW TEXT function. Then, the first line of the SHOW TEXT function is to close the window opened by the GET TEXT function, get the value of the input text, and print it in another GUI.
So, I tried doing this,
from Tkinter import *
import tkMessageBox
def texttobeenteredhere():
application = Tk()
textbox = Text(application)
textbox.pack()
submitbutton = Button(application, text="OK", command=showinputtext)
submitbutton.pack()
application.mainloop()
def showinputtext():
application.quit()
thetext = textbox.get()
print "You typed", thetext
texttobeenteredhere()
I got errors that I could not comprehend, but I hope you get my idea even though my explanation could be really bad. Please suggest a solution to my problem, where the GET TEXT function and SHOW TEXT function have to exist separately in the code.
EDIT:
Thanks Josselin for introducing the syntax class in python. What I actually wanted to say was, I want the programme to open a window to get input from the user, and then close the window, and finally open another window to show the input text. I am honestly new to this, but through my prior knowledge and guessing, I tried to modify the code to meet my expectation.
import Tkinter as tk
global passtext
class application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.textbox = tk.Text(self)
self.textbox.pack()
self.submitbutton = tk.Button(self, text="OK", command=self.showinputtext)
self.submitbutton.pack()
self.mainloop()
def showinputtext(self):
self.thetext = self.textbox.get("1.0", "end-1c")
print "You typed:", self.thetext
self.destroy()
class showtext(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.setthetext = tk.StringVar()
self.setthetext.set(passtext)
self.showthetext = tk.Label(self, textvariable=self.setthetext)
self.showthetext.pack()
self.submitbutton = tk.Button(self, text="OK", command=self.destroy)
self.submitbutton.pack()
self.mainloop()
# Launch the GUI
app = application()
# Access the entered text after closing the GUI
passtext = app.thetext
text = showtext()
My English can sometimes be not understandable, but this question is answered. Thank you very much.
There are 2 main problems in your code:
First, in your showinputtext function, you want to access elements of your GUI, but they are not defined within the scope of the function.
Second, when reading the content of a tk.Text widget, the .get() method takes 2 arguments (see this link).
To fix the first problem, the best is to define your application as a class, with an inner function taking the class instance self as input argument, such that application widgets can be called within the function.
Code:
import Tkinter as tk
class application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.textbox = tk.Text(self)
self.textbox.pack()
self.submitbutton = tk.Button(self, text="OK", command=self.showinputtext)
self.submitbutton.pack()
self.mainloop()
def showinputtext(self):
self.thetext = self.textbox.get("1.0", "end-1c")
print "You typed:", self.thetext
self.destroy()
# Launch the GUI
app = application()
# Access the entered text after closing the GUI
print "you entered:", app.thetext

Capture the output from function in real time python

I didn't find quite what I was looking for.
I want to obtain the output (stdout) from a python function in real time.
The actual problem is that I want to plot a graph (with cplot from sympy) with a progress bar in my UI. The argument verbose makes cplot output the progress to stdout.
sympy.mpmath.cplot(lambda z: z, real, imag, verbose=True)
The output would be something like:
0 of 71
1 of 71
2 of 71
...
And so on.
I want to capture line by line so I can make a progress bar. (I realize this might not be possible without implementing multithreading). I'm using python2.7 (mainly because I need libraries that aren't in python3)
So, ¿How do I achieve that?
You can capture stdout by monkeypatching sys.stdout. A good way to do it is using a context manager, so that it gets put back when you are done (even if the code raises an exception). If you don't use a context manager, be sure to put the original sys.stdout back using a finally block.
You'll need an object that is file-like, that takes the input and does what you want with it. Subclassing StringIO is a good start. Here's an example of a context manager that captures stdout and stderr and puts them in the result of the bound variable.
class CapturedText(object):
pass
#contextmanager
def captured(disallow_stderr=True):
"""
Context manager to capture the printed output of the code in the with block
Bind the context manager to a variable using `as` and the result will be
in the stdout property.
>>> from tests.helpers import capture
>>> with captured() as c:
... print('hello world!')
...
>>> c.stdout
'hello world!\n'
"""
import sys
stdout = sys.stdout
stderr = sys.stderr
sys.stdout = outfile = StringIO()
sys.stderr = errfile = StringIO()
c = CapturedText()
yield c
c.stdout = outfile.getvalue()
c.stderr = errfile.getvalue()
sys.stdout = stdout
sys.stderr = stderr
if disallow_stderr and c.stderr:
raise Exception("Got stderr output: %s" % c.stderr)
(source)
It works as shown in the docstring. You can replace StringIO() with your own class that writes the progress bar.
Another possibility would be to monkeypatch sympy.mpmath.visualization.print, since cplot uses print to print the output, and it uses from __future__ import print_function.
First, make sure you are using from __future__ import print_function if you aren't using Python 3, as this will otherwise be a SyntaxError.
Then something like
def progressbar_print(*args, **kwargs):
# Take *args and convert it to a progress output
progress(*args)
# If you want to still print the output, do it here
print(*args, **kwargs)
sympy.mpmath.visualization.print = progressbar_print
You might want to monkeypatch it in a custom function that puts it back, as other functions in that module might use print as well. Again, remember to do this using either a context manager or a finally block so that it gets put back even if an exception is raised.
Monkeypatching sys.stdout is definitely the more standard way of doing this, but I like this solution in that it shows that having print as a function can actually be useful.

Trace not working on Python TKinter checkbutton control

I'm doing my own tweak on the technique from this post, using a canvas vs. a text widget to get some finer control over the scrolling behavior. My code appears below.
All is working as I want it but for some reason the trace I'm using to track the checkbutton values isn't working. No errors of any kind show up in the console window. But I don't get the expected printed message (from _cbWasClicked) when I click one any of the checkbuttons. As best I can tell the method is just never invoked.
I know it's got to be a simple and obvious bug but I'm stumped. I've used print statements to confirm that the 100 IntVars get instantiated as expected. Then I deliberately misspelled the method name in the .trace and this time it generated an error. So when I yank those diagnostic tweaks all should be working.... it just isn't. Can someone tell me what I'm missing?
Environment is Python 2.7 on Windows 7.
import Tkinter as tk
class myCheckList(tk.Frame):
def __init__(self, root, *args, **kwargs):
tk.Frame.__init__(self, root, *args, **kwargs)
self.root = root
self.vsb = tk.Scrollbar(self, orient="vertical")
self.canvas = tk.Canvas(self, width=200, height=290,
relief=tk.GROOVE,bd=3,
scrollregion=(0,0,0,2020),
yscrollcommand=self.vsb.set,
yscrollincrement=20)
basecolor = self.canvas.cget('background')
self.vsb.config(command=self.canvas.yview)
self.canvas.grid(row=0,column=0,sticky=tk.NSEW,padx=(0,0),pady=0)
self.vsb.grid(row=0,column=1,sticky=tk.NS,padx=(0,0),pady=0)
for i in range(100):
cbv = tk.IntVar()
cbv.trace('w',self._cbWasClicked)
cb = tk.Checkbutton(self, background=basecolor,
variable=cbv,
text="Checkbutton #%s" % i)
self.canvas.create_window(5,20*i+5,anchor=tk.NW,window=cb)
self.canvas.bind_all('<MouseWheel>',
lambda event: self.canvas.yview_scroll(-1*event.delta/120, tk.UNITS))
def _cbWasClicked(self,*args):
print 'checkbox clicked'
if __name__ == "__main__":
root = tk.Tk()
myCheckList(root).grid(row=0,column=0,sticky=tk.W,padx=0,pady=0)
root.mainloop()
Found it, after much wrestling and experimenting. It turns out that the trace works perfectly when I add a couple of lines to the class's __init__:
self.status = []
...and then, inside the loop...
self.status.append((cb,cbv))
...which tells me that garbage collection is the culprit. By creating a list and storing the object references in it, they couldn't be garbage-collected and so the .trace remains effective.
First off, you should prepend self. to cbv and cb within the FOR cycle.
Secondly, even then it is going to work only for the very last checkbox, because with each iteration you overwrite the variable cbv again and again.
As a workaround I used a list of vaiables (self.li) generated one step before the cycle. This way you can link each checkbox to its own variable:
self.li = ['cbv' + str(i) for i in range(100)]
for i in range(100):
self.li[i] = tk.IntVar()
self.cb = tk.Checkbutton(self, background=basecolor,
variable=self.li[i],
text="Checkbutton #%s" % i)
self.li[i].trace('w', self._cbWasClicked)
self.canvas.create_window(5,20*i+5,anchor=tk.NW,window=self.cb)
...
This code worked fine for me.
You will then need to identify each checkbox somehow. You can do it using the internal variable name which is passed as the first param to the callback function in the trace method (What are the arguments to Tkinter variable trace method callbacks?):
def _cbWasClicked(self, name, *args):
print('checkbox %s clicked:' % name)
In the output you'll get something like this:
checkbox PY_VAR10 clicked:
checkbox PY_VAR99 clicked:
checkbox PY_VAR0 clicked: