Python Execution Order with GUI - python

I'm having some issues with the following code. This is the first time that I'm working with a GUI and it's been a while since I've worked with python as well. When I try to execute the solfield function with the button, it yields no output.
from Tkinter import *
import math
master = Tk()
n = float()
I = float()
def solfield():
pass
label_coils = Label(text='Number of Coils Per Meter', textvariable=n)
label_coils.grid()
coils = Entry(master)
coils.grid()
label_current = Label(text='Current in Amps', textvariable=I)
label_current.grid()
current = Entry(master)
current.grid()
calculate_button = Button(text='Calculate', command=solfield())
calculate_button.grid()
label_bfield = Label(text='B Field in +z Direction')
label_bfield.grid()
label_result = Label(text='solfield')
label_result.grid()
master.title('Coil Gun Simulation')
master.mainloop()
def solfield():
mu0 = math.pi*4e-7
solfield = mu0*n*I
print solfield
Any other tips would be appreciated as well, as there will eventually be much more coding for me to do.
This has been solved. If anyone is interested, here is the code after several fixes were made:
from Tkinter import *
import math
master = Tk()
label_coils = Label(text='Number of Coils Per Meter')
label_coils.grid()
coils = Entry(master)
coils.grid()
label_current = Label(text='Current in Amps')
label_current.grid()
current = Entry(master)
current.grid()
def solfield():
mu0 = math.pi*4e-7
n = float(coils.get())
I = float(current.get())
fieldmag = mu0*n*I
print fieldmag
calculate_button = Button(text='Calculate', command=solfield)
calculate_button.grid()
label_bfield = Label(text='B Field in +z Direction')
label_bfield.grid()
label_result = Label(text='solfield')
label_result.grid()
master.title('Coil Gun Simulation')
master.mainloop()

The problem is here:
calculate_button = Button(text='Calculate', command=solfield())
To pass the function solfield itself as the command, just use its name:
calculate_button = Button(text='Calculate', command=solfield)
What you're doing is calling the function, and then passing the return value of that function as the command.
Since you defined solfield above as do-nothing function, that return value is None, so you're telling calculate_button that its command=None, and it's properly doing nothing.
Meanwhile, as SethMMorton pointed out (but then deleted):
You have two functions named solfield, and you are naming a variable solfield in one of your solfield functions. Remove the empty function (the one with pass), and using a different variable name in the remaining function.
This isn't causing your actual problem, but it's certainly adding to the confusion that makes it harder for you to find the problem. (For example, if you hadn't included the excess empty definition of solfield at all, you would have gotten a NameError in the incorrect line, which would have made things easier to debug.)
Putting it all together, what you should do is:
Get rid of the empty (pass-only) definition of solfield.
Move the real implementation of solfield up above the point where you build the GUI.
Don't name a local variable solfield within the function.
Pass just solfield, not solfield() as the command for calculate_button.

Related

How to solve Problem with Multiprocessing in Tkinter?

Over here I am using multiprocessing to run multiple algorithms in tkinter. At first I tried using threading, but it can't work properly in my program. Below is an idea of my program workflow, it works something like this, but just different functions:
from tkinter import *
from multiprocessing import Process
def SquarFunc(Square):
for i in range(1,1000):
Square.set(str(i**2))
def CubeFunc(Cube):
for i in range(1,1000):
Cube.set(str(i**3))
if __name__ == "__main__":
window= Tk()
Square= StringVar()
Cube= StringVar()
window.geometry("500x500")
A= Label(window, textvariable= Square)
A.place(x=200, y=200)
B= Label(window, textvariable= Cube)
B.place(x=300, y=300)
Squaring= Process(target=SquarFunc, args=(Square, ))
Cubing= Process(target=CubeFunc, args=(Cube, ))
Squaring.start()#Error originates here
Cubing.start()
Squaring.join()
Cubing.join()
window.mainloop()
The error produced is this:
TypeError: cannot pickle '_tkinter.tkapp' object
Anybody knows how to fix this?? thanks in advance!
Here is an example of how to communicate with other processes if using multiprocessing (explanation is in comments, time.sleep is used just for the example because otherwise those loops will complete in a few microseconds):
from tkinter import Tk, StringVar, Label
from multiprocessing import Process, Manager
import time
def square_func(d, name):
for i in range(1, 1000):
# update data in the shared dict
d[name] = i
time.sleep(0.1)
def cube_func(d, name):
for i in range(1, 1000):
# update data in the shared dict
d[name] = i
time.sleep(0.1)
def update_string_vars(d, *variables):
for var in variables:
# get the value from shared dict
value = d[str(var)]
if value is not None:
# set string var to the value
var.set(str(value))
# schedule this to run again
window.after(100, update_string_vars, d, *variables)
# cleanup process upon closing the window in case
# processes haven't finished
def terminate_processes(*processes):
for p in processes:
p.terminate()
if __name__ == "__main__":
window = Tk()
window.geometry("500x500")
# bind the terminator to closing the window
window.bind('<Destroy>', lambda _: terminate_processes(
square_process, cube_process))
square_var = StringVar()
cube_var = StringVar()
Label(window, text='Square:').pack()
Label(window, textvariable=square_var).pack()
Label(window, text='Cube:').pack()
Label(window, textvariable=cube_var).pack()
# create the manager to have a shared memory space
manager = Manager()
# shared dict with preset values as to not raise a KeyError
process_dict = manager.dict({str(square_var): None, str(cube_var): None})
square_process = Process(
target=square_func, args=(process_dict, str(square_var))
)
cube_process = Process(
target=cube_func, args=(process_dict, str(cube_var))
)
square_process.start()
cube_process.start()
# start the updater
update_string_vars(process_dict, square_var, cube_var)
window.mainloop()
Useful:
Sharing state between processes
shortly about tkinter and processes
See also:
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.
I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Don't have space around = if it is used as a part of keyword argument (func(arg='value')) but have space around = if it is used for assigning a value (variable = 'some value'). Have space around operators (+-/ etc.: value = x + y(except here value += x + y)). Have two blank lines around function and class declarations. Object method definitions have one blank line around them.

Using Entry and updating an entry entered, as the user continues to change the values (using trace method) in TKinter

I am learning Python and I am starting to learn how to use the TKinter GUI. I am making a small interface that does some simple statistical analysis like STDEV, T-tests, etc. I have this method in a class which basically gets the data to work with.
I want the user to be able enter as many data entries as they want (I mean as long as the computer can handle of course). The problem I am having is -- I think when I use the method .get() on an Entry, None is returned?
I am also using method .trace() of DoubleVar() to trace when the values of the entries are updated using the method shown here:
Python Tkinter update when entry is changed
I thought it made sense but it's not working for me. Whenever I change a box in my TK interface, all the other boxes get changed, but the values used to calculate standard deviation are not the numbers that are being shown on the Entry boxes (which are all the same anyways).
Here is the code:
class StandardDeviation:
"""
Runs standard deivation calculations.
"""
def __init__(self) -> None:
"""
Initializes an instance of the functions!
"""
self.stdev_pop = Button(top_frame,
text="Calculate the population "
"standard deviation of the data set")
self.stdev_pop.bind("<Button-1>", self.show_result_population)
self.stdev_pop.pack()
stdev_samp = Button(top_frame,
text="Calculate the sample "
"standard deviation of the data set")
stdev_samp.bind("<Button-1>", self.show_result_sample)
stdev_samp.pack()
self.data = []
self.enter_data = Button(top_frame, text="Enter data")
self.enter_data.bind("<Button-1>", self.pack_add_entry_button)
self.add_entry = Button(top_frame, text="Add data entry",
command=self.add_new_entry)
self.enter_data.pack()
self.all_entries = {}
self.tracer = DoubleVar()
self.tracer.trace("w", self.update)
def pack_add_entry_button(self, *args) -> None:
"""
Pack the add_entry button.
"""
self.add_entry.pack()
def update(self, *args) -> None:
"""
Update the values of the entries.
"""
global update_in_progress
if update_in_progress:
return
update_in_progress = True
data = [str(self.all_entries[item]) for item in self.all_entries]
self.data = [int(item) for item in data if item.isnumeric()]
update_in_progress = False
def add_new_entry(self):
"""
Add a new entry.
"""
new_entry = Entry(root, textvariable=self.tracer)
new_entry.pack()
new_entry_data = new_entry.get()
self.all_entries[new_entry] = new_entry_data
I'm not sure where I'm wrong here if anyone could help me I'd really appreciate it. Thank you!
There is no way to run the code you posted as the indentation is off and some of the buttons call functions that don't exist so this is a standard trace program that shows how to use the tkinter variable associated with the trace, a StringVar in ths case, to get the contents.
import tkinter
def text_changed(*args):
print(tk_name.get())
top = tkinter.Tk()
tk_name=tkinter.StringVar()
tk_name.set("nothing")
tk_name.trace("w", text_changed)
tkinter.Label(top, textvariable=tk_name).grid(row=0, column=1)
entry_1 = tkinter.Entry(top, textvariable=tk_name)
entry_1.grid(row=1, column=1, sticky="W")
entry_1.focus_set()
top.mainloop()

tkinter (py3) - change image labels, inside functions, in real time

I'm learning to use tkinter, and what I've tried to do is have 4 buttons on a coffee machine, and each button would create a new window, which would show images in order, like a slideshow. What I've done doesn't work, as it only shows the last image in the slideshow, and ignores the rest. In python 2.7, I was able to fix it by printing nothing into the console after every configuration of the label, but it doesn't seem to work. If you could tell me why this happens, and/or how to fix it, it would be greatly appreciated.
(P.S. I know that my code is probably very ugly/inefficient, but bear in mind that I'm very new to tkinter, so I only really care that it works).
def Latte():
global Grind
global Hot_Water
global Cocoa_Poweder
global Steamed_Milk
global Foamed_Milk
global LattePhoto
MakingCoffee=Toplevel(Test, width=200, height=200)
MakingCoffee.wm_title("Latte")
MakingCoffee.iconbitmap("Photos\Latte.ico")
photolabel= Label(MakingCoffee,image=Grind)
photolabel.pack()
time.sleep(2)
photolabel.configure(image=Hot_Water)
time.sleep(2)
photolabel.configure(image=Steamed_Milk)
time.sleep(4)
photolabel.configure(image=Foamed_Milk)
time.sleep(1)
photolabel.configure(image=LattePhoto)
time.sleep(2)
MakingCoffee.destroy()
Here's a small sample of one way to make this work. You can restructure as needed. The list is just so I can iterate through it showing the changes. You don't need all of those globals in your original code. They're already global variables and you're not attempting to reassign them so this a redundancy. You can factor out ~50% of your code at least to be reusable. You can make this a class and make a "factory class" to create different types of slideshows with different types of coffee, and then you could also get rid of the global here as well.
from tkinter import *
import tkinter
import time
Test = tkinter.Tk()
Test.wm_title("Coffee Store")
Test.resizable(0, 0)
americano_images = [
PhotoImage(file='1.gif'),
PhotoImage(file='2.gif'),
PhotoImage(file='3.gif')
]
AFTER = None
AmericanoPhoto= PhotoImage(file="1.gif")
def switch_images(im_list, label, index=0):
global AFTER
label.configure(image=im_list[index])
if index != len(im_list) - 1:
index += 1
else:
index = 0
AFTER = Test.after(1000, lambda: switch_images(im_list, label, index))
def Americano():
MakingCoffee=Toplevel(Test, width=200, height=200)
MakingCoffee.wm_title("Americano")
photolabel= Label(MakingCoffee)
photolabel.pack_propagate(0)
photolabel.pack()
after = switch_images(americano_images, photolabel)
cancel = Button(MakingCoffee, text='Kill Slideshow',
command=lambda: Test.after_cancel(AFTER))
cancel.pack(fill=X, expand=1)
B1= Button(Test, text='BUTTON', command=Americano)
B1.grid(row=0,column=0)
Test.mainloop()

Pausing from within a tkinter function

I'm trying to create a couple of functions which do things in a sequential order. First they need to open a new window and display a label, then they need to wait for some seconds, then they need to call another function. However, I'm struggling to get the functions to wait, all the methods I've tried (.after, .sleep, .wait_visibility) seem to be ignored and it just skips to the next function call without pausing.
Here's what I have (sorry if it's messy, I'm new to python):
from tkinter import *
import time
root =Tk()
root.geometry('600x600')
def scale_screen(event = None):
global s_screen
s_screen = Toplevel(root)
s_screen.title('Residual Inhibition Tester')
s_screen.geometry('600x600')
s_screen.transient(root)
s_screen.bind('<Return>', sel)
global var
var = IntVar()
scale = Scale(s_screen, variable = var, orient = HORIZONTAL, length = 1000)
scale.focus_set()
scale.pack(anchor=CENTER)
button = Button(s_screen, text="Select", command=sel)
button.pack(anchor=CENTER)
def sel(event = None):
label = Label(s_screen)
selection = "Value = " + str(var.get())
label.config(text = selection)
interval_screen()
def interval_screen():
global i_screen
i_screen = Toplevel(root)
i_screen.geometry('600x600')
i_screen.transient(root)
i_label = Label(i_screen, text = "Please Wait")
i_label.pack(anchor = CENTER)
s_screen.destroy()
i_screen.after(3000, masker_screen)
#time.sleep(3)
#i_screen.after(300,i_label.configure(text="Playing New Masker Noise"))
#root.wait_visibility(window = i_screen)
def masker_screen():
global m_screen
m_screen = Toplevel(root)
m_screen.geometry('600x600')
m_screen.transient(root)
m_label = Label(m_screen, text = "Playing New Masker Noise").pack(anchor = CENTER)
m_screen.after(3000, lambda: scale_screen(event = None))
i_screen.destroy()
b1 = Button(root, command = scale_screen).pack(anchor=CENTER)
root.bind('<Return>', scale_screen)
root.mainloop()
In this example, the program will run but just skip the interval_screen entirely and just do the masker_screen. I'm also not averse to just using one screen and using the .configure methods to change the label text if that's easier.
Thanks!
Without seeing all the ways you tried it, it's impossible to know what you did wrong. In general you should never call time.sleep and you should never call after with just a single argument. Also, when you use after with two arguments, the second argument must be a reference to a function.
The proper way to do this is to have your first function call your second function via after:
def interval_screen():
...
i_screen.after(3000, maker_screen)
def masker_screen():
...
m_screen.after(3000, lambda: scale_screen(event = None))
Note that in your updated question you're using after incorrectly:
m_screen.after(3000, scale_screen(event = None))
You're calling the function scale_screen(...) immediately, and giving the result of that to the after function. If you need to pass arguments to your function you must create another function that does not require arguments. The simplest way to do this is with lambda, though you can also use functools.partial or you can create your own function.

Combined Event Handling of widgets in TKinter

I am making a GUI Program in Tkinter and am running into problems.What I want to do is draw 2 checkboxes and a button. According to the user input next steps should take place. A part of my code has been shown below :-
CheckVar1 = IntVar()
CheckVar2 = IntVar()
self.C1 = Checkbutton(root, text = "C Classifier", variable = CheckVar1, onvalue = 1, offvalue = 0, height=5,width = 20).grid(row=4)
self.C2 = Checkbutton(root, text = "GClassifier", variable = CheckVar2, onvalue = 1, offvalue = 0, height=5, width = 20).grid(row=5)
self.proceed1 = Button(root,text = "\n Proceed",command = self.proceed(CheckVar1.get(),CheckVar2.get())).grid(row=6)
# where proceed prints the combined values of 2 checkboxes
The error that I am getting is typical ie a default value of both the selected checkboxes gets printed up and then there is no further input. The error that I get is NullType Object is not callable.
I searched on the net and I think the answer is related to lambda events or curry.
Please help ..
You're passing the value of self.proceed(CheckVar1.get(),CheckVar2.get()) to the Button constructor, but presumably what you want is for command to be set to a function which will call self.proceed(CheckVar1.get(),CheckVar2.get()) and return a new, possibly different value every time the button is pressed. You can fix that with a lambda, or by wrapping the call in a short callback function. For example, replace the last line with:
def callback():
return self.proceed(CheckVar1.get(), CheckVar2.get())
self.proceed1 = Button(root, text="\n Proceed", command=callback).grid(row=6)
This is pretty typical Tkinter. Remember: when you see a variable called command in Tkinter, it's looking for a function, not a value.
EDIT: to be clear: you're getting 'NullType Object is not callable' because you've set command to equal the return value of a single call to self.proceed (that's the NullType Object). self.proceed is a function, but its return value is not. What you need is to set command to be a function which calls self.proceed.
Like Peter Milley said, the command option needs a reference to a function (ie: give it a function name (ie: no parenthesis). Don't try to "inline" something, create a special function. Your code will be easier to understand and to maintain.

Categories

Resources