Using the after-method with Tkinter - python

I'm having troubles using the "after" method in my GUI.
The goal is to create a loop, which calculates "flow" with given input variables.
So far I didn't manage to get my code running, with the documentation on "after" or this post Tkinter only calls after_idle once
My Code:
import Tkinter as tk
from Tkinter import *
import ttk
import tkMessageBox
class Regeln():
def __init__(self):
bla=1
def lesen(self,faktor,stromdichte,flaeche,anzahl,elektronen,konzentration):
self.faktor=faktor
self.stromdichte=stromdichte
self.flaeche=flaeche
self.anzahl=anzahl
self.elektronen=elektronen
self.konzentration=konzentration
try:
self.faktor=float(self.faktor)
self.stromdichte=float(self.stromdichte)
self.flaeche=float(self.flaeche)
self.anzahl=float(self.anzahl)
self.elektronen=float(self.elektronen)
self.konzentration=float(self.konzentration)
except:
tkMessageBox.showerror("Achtung!","Nur Zahlen eingeben!")
try:
1/(self.faktor*self.stromdichte*self.flaeche*self.anzahl*self.elektronen*self.konzentration)
except:
tkMessageBox.showerror("Achtung!","Nur Werte größer 0 eingeben!")
self.algorithmus()
def algorithmus(self):
flow=self.faktor*self.stromdichte*self.elektronen*60/(96485.34*self.konzentration*0.75/100)*self.flaeche*self.anzahl/1000
print flow
self.after(1500,self.algorithmus)
With this I get an "AttributeError". Anybody has any idea how to get it running?

after is a method available on all tkinter widgets. In your specific case, self is not a widget, so naturally it won't have this method.
You need to call after from an existing widget. For example, if you initially created your GUI with something like this:
root = tk.Tk()
You can later call after like this:
root.after(...)

Related

Is there some way I can pass information to a tkinter application after calling .mainloop()?

This is a very simple version of my application. Basically I have a Tkinter application I want to start from another python application, but I do not have the option to have tkinter as my main window.
So my question is, if it's possible to pass some information from my main class to my tkinter application. Not once, but repeatedly.
I tried something like this, but this does not work, probably because of the .mainloop() method.
Right now I just try to print the variable "labelContent" whenever the button is pressed.
Is there some way I can pass information to my tkinter application after calling .mainloop() (besides using external files like .txt because this would obviously work)?
gui.py
import tkinter
class BasicWindow:
def __init__(self):
tk = tkinter.Tk()
label = tkinter.Label(tk, text="Hello World!")
label.pack()
self.labelContent = ""
button = tkinter.Button(tk,text="OK",command=self.buttonMethod)
button.pack(side="bottom")
tk.mainloop()
def buttonMethod(self):
print(self.labelContent)
main.py
from gui import BasicWindow
import time
class Main:
def __init__(self):
self.gui = BasicWindow()
self.gui.labelContent = "This is the new label content"
mainApp = Main()
while True:
mainApp.gui.labelContent = time.time()
Thanks in advance! :)

issue with tkinter messagebox

I'm using several tkinter messageboxes in my code. But ive noticed a problem with the showinfo messageboxes I've used in my code. Basically, I want a function to be called when the ok on the messagebox is pressed. Also, the user can choose not to proceed by just closing the messagebox. But, it looks like when I press the x icon to close the messagebox, the function is still called. Here is a minimum reproducible code to explain what i mean.
from tkinter import *
from tkinter import messagebox
root = Tk()
def func() :
Label(root,text="This is the text").pack()
msg = messagebox.showinfo("Loaded","Your saved state has been loaded")
if msg == "ok" :
func()
root.mainloop()
My question is, What should i do so that the function is not called when the x icon is pressed?
There are different types of messagebox, the one your using here is not what you actually should be using for your case. What you want is something called askyesno, or something that is prefixed by 'ask' because your code depend upon the users action. So you want to 'ask' the user, like:
from tkinter import *
from tkinter import messagebox
root = Tk()
def func() :
Label(root,text="This is the text").pack(oadx=10,pady=10)
msg = messagebox.askyesno("Loaded","Your saved state has been loaded")
if msg: #same as if msg == True:
func()
root.mainloop()
Here, askyesno like other 'ask' prefixed functions will return True or 1 if you click on the 'Yes' button, else it will return False or 0.
Also just something I realized now, showinfo, like other 'show' prefixed messagebox function, returns 'ok' either you press the button or close the window.
Some more 'ask' prefixed messageboxes ~ askokcancel, askquestion, askretrycancel, askyesnocancel. Take a look here

closing tkinter window together with messagebox error window

In my script I sometimes call my ErrorWindow class to show an error message. This creates an empty tkinter window and a messagebox error window. I either only want the messagebox window, or I want the tkinter window to close automatically when I close the error window.
I've tried two pieces of code:
class ErrorWindow:
def __init__(self,error_message):
self.error_window = tk.Tk()
messagebox.showerror("ERROR",error_message,command=self.close)
self.error_window.protocol("WM_DELETE_WINDOW", self.close)
self.error_window.mainloop()
def close(self):
self.error_window.destroy()
.
class ErrorWindow:
def __init__(self,error_message):
messagebox.showerror("ERROR",error_message) #automatically creates a tk window too
But even with the second one, the tkinter window remains after I close the messagebox.
How can I program the class so that I only have to press a button (either Ok or the X in the top right of a window) once to close all windows (whether that is one or two)?
You need to withdraw the main window:
class ErrorWindow:
def __init__(self,error_message):
if not tk._default_root: # check for existing Tk instance
error_window = tk.Tk()
error_window.withdraw()
messagebox.showerror("ERROR",error_message)
This has no business as a class. You should remake this a simple function.
You did not specify whether there is just one place or multiple places where you might want want an error message. If the latter, you can create and withdraw a tk window just once. I believe a wrapper function rather than class should be sufficient for your purposes.
import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
# consider placing root to control where messagebox appears
root.withdraw()
def showerror(message):
messagebox.showerror('XYZ ERROR', message, parent=root)
To avoid possible problems, I always use an explicit master or parent for everything and never depend on _default_root.
The small function below will do the job. By setting the type you can choose for: info, warning or error message box, the default is 'Info'. You can set also the timeout, the default is 2.5 seconds.
def showMessage(message, type='info', timeout=2500):
import tkinter as tk
from tkinter import messagebox as msgb
root = tk.Tk()
root.withdraw()
try:
root.after(timeout, root.destroy)
if type == 'info':
msgb.showinfo('Info', message, master=root)
elif type == 'warning':
msgb.showwarning('Warning', message, master=root)
elif type == 'error':
msgb.showerror('Error', message, master=root)
except:
pass
Call the function as follow:
For message type 'Info' and timeout of 2.5 seconds:
showMessage('Your message')
Or by your own settings for type message 'Error' and timeout 4 seconds:
showMessage('Your message', type='error', timeout=4000)

Python 2.7 tkSimpleDialog.Dialog crashes when pressing Enter

I inherited a class from tkSimpleDialog.Dialog. From a tkInter application, I open it, it has some Entry fields, an OK and a Cancel button.
When this dialog is open and I press the Enter button the dialog and also the main application freezes completely and won't do anything further.
I tried to bind <"Return"> event to it, to catch, but nothing else happened, just the freeze.
Sadly I can't attach code, because it is my work in a company.
Does anyone have an idea how could I resolve the problem and maybe set the Enter button to activate th OK?
I use Python 2.7 and cannot use other versions.
from Tkinter import *
import ttk
from tkFileDialog import askopenfilename, asksaveasfilename
import tkSimpleDialog
from mem_data import *
from collections import namedtuple
class OpenFileDialog(tkSimpleDialog.Dialog):
def body(self, master):
self.master = master
def apply(self):
self.result = "OK"enter code here
And I call it like this:
d = OpenFileDialog(self.master)
mcu = d.result
The problem is that you are setting self.master. Unfortunately, Tkinter uses self.master internally. To fix this you need to choose a different variable name.
class OpenFileDialog(tkSimpleDialog.Dialog):
def body(self, master):
self._master = master
...

TKInter checkbox variable is always 0

I'm using Python's TkInter module for a GUI. Below is a simple checkbox code.
def getCheckVal():
print cbVar.get()
windowTime=Tk.Tk()
cbVar = Tk.IntVar()
btnC = Tk.Checkbutton(windowTime, text="Save", variable = cbVar, command=getCheckVal)
btnC.grid()
windowTime.mainloop()
This code works fine. Each time I tick the checkbox, I get 1, else 0.
However, when I run the same code in a function that is called from another TkInter command (when a button is pressed), it stops working. I always get 0 as the value.
class GUIMainClass:
def __init__(self):
'''Create the main window'''
self.window = Tk.Tk()
def askUser(self):
def getCheckVal():
print cbVar.get()
windowTime=Tk.Tk()
cbVar = Tk.IntVar()
btnC = Tk.Checkbutton(windowTime, text="Save", variable = cbVar,
command=getCheckVal)
btnC.grid()
windowTime.mainloop()
def cmdWindow(self):
frameShow=Tk.Frame(self.window)
frameShow.grid()
btnSwitch = Tk.Button(frameShow, text='Show Plots', command=self.askUser)
btnSwitch.grid()
self.window.mainloop()
GUIObj=GUIMainClass()
GUIObj.cmdWindow()
This is very unusual. What could be going wrong?
EDIT: I've used 2 mainloops because I want a separate window (windowTime) to open up when I click "Show Plots" button. This new window should have the checkbox in it.
Your windowTime, cbVar, etc. variables are defined in the function's local scope. When askUser() completes execution, those values are thrown away. Prepend self. to them to save them as instance variables.
There should only be one mainloop() in your program, to run the main Tkinter root object. Try putting it as the very last line in the program. I recommend doing some reading on Effbot for how to set up a Tkinter application.
I'm not sure what all you're trying to do, but one problem is that the TK.IntVar called cbVar that you create in your askUser() method will be deleted when the function returns, so you need to attach it to something that will still exist after that happens. While you could make it a global variable, a better choice would be to make it an attribute of something more persistent and has a longer "lifespan".
Another likely issue is that generally there should only be one call to mainloop() in a single Tkinter application. It appears what you want to do is display what is commonly known as a Dialog Window, which Tkinter also supports. There's some standard ones built-in, plus some more generic classes to simplify creating custom ones. Here's some documentation I found which describes them in some detail. You may also find it helpful to look at their source code.
In Python 2 it's in the /Lib/lib-tk/tkSimpleDialog.py file and
in Python 3 the code's in a file named /Lib/tkinter/simpledialog.py.
Below is code that takes the latter approach and derives a custom dialog class named GUIButtonDialog from the generic one included the Tkinter library which is simply named Dialog.
try:
import Tkinter as Tk # Python 2
from tkSimpleDialog import Dialog
except ModuleNotFoundError:
import tkinter as Tk # Python 3
from tkinter.simpledialog import Dialog
class GUIButtonDialog(Dialog):
"""Custom one Button dialog box."""
def __init__(self, btnText, parent=None, title=None):
self.btnText = btnText
Dialog.__init__(self, parent, title)
def getCheckVal(self):
print(self.cbVar.get())
def body(self, master):
"""Create dialog body."""
self.cbVar = Tk.IntVar()
self.btnC = Tk.Checkbutton(master, text=self.btnText, variable=self.cbVar,
command=self.getCheckVal)
self.btnC.grid()
return self.btnC # Return the widget to get inital focus.
def buttonbox(self):
# Overridden to suppress default "OK" and "Cancel" buttons.
pass
class GUIMainClass:
def __init__(self):
"""Create the main window."""
self.window = Tk.Tk()
def askUser(self):
"""Display custom dialog window (until user closes it)."""
GUIButtonDialog("Save", parent=self.window)
def cmdWindow(self):
frameShow = Tk.Frame(self.window)
frameShow.grid()
btnSwitch = Tk.Button(frameShow, text='Show Plots', command=self.askUser)
btnSwitch.grid()
self.window.mainloop()
GUIObj = GUIMainClass()
GUIObj.cmdWindow()

Categories