i have a small gui with a group of buttons , said buttons when pressed call to several Functions , which take about 30 seconds to finish , so the Gui freezes , after searching a lot here for a fix , I found a way to do it and it seemed to work okay for the most part , when i run the script it does work , but after a few minutes running it just stops working
this is a simple function that takes 1 argument , that argument is another function's name.
def prueba(fcn):
thread.start_new_thread (fcn,())
here's the code for the buttons:
button = Tkinter.Button(self,text=u"Irrelevant text",command= lambda: prueba(functionName))
button.grid(column=0,row=3)
button = Tkinter.Button(self,text=u"Irrelevant text",command= lambda: prueba(functionName))
button.grid(column=0,row=4)
button = Tkinter.Button(self,text=u"Irrelevant text",command= lambda: prueba(functionName))
button.grid(column=0,row=5)
button = Tkinter.Button(self,text=u"Irrelevant text",command= lambda: prueba(functionName))
button.grid(column=1,row=3)
button = Tkinter.Button(self,text=u"Irrelevant text",command= lambda: prueba(functionName))
button.grid(column=1,row=4)
button = Tkinter.Button(self,text=u"Irrelevant text",command= lambda: prueba(functionName))
button.grid(column=1,row=5)
This is what the console shows when clicking on the buttons after a while.
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1541, in __call__
return self.func(*args)
File "***", line 354, in <lambda>
button = Tkinter.Button(self,text=u"Irrelevant Text",command= lambda: prueba(functionName))
TypeError: 'bool' object is not callable
File "***", line 354, in
button = Tkinter.Button(self,text=u"Irrelevant Text",command= lambda: prueba(functionName))
TypeError: 'bool' object is not callable
First, use partial instead of lambda as that should be easier to understand
from functools import partial
## create all buttons is of course the same as one button
button = Tkinter.Button(self,text=u"Irrelevant text",
command=partial(prueba, functionName))
button.grid(column=0,row=3)
Then use after() instead of Threading to schedule it (non-blocking) although we don't have much info on what you are doing so this is a solution that generally works.
def prueba(func_name):
## 100=one tenth of a second
main.after(100, func_name, ()) ## () sends empty tuple to func_name???
Related
quite new to Python and come across a problem that I'm struggling to diagnose.
I am working on a project involving a Tkinter menu and using an ADC connected to a potentiometer (which will hopefully be replaced by a muscle sensor once I've gotten the essential code working). I am using a raspberry Pi and breadboard for this, with my IDE as Thonny.
The imports are:
from time import sleep
import busio
import digitalio
import board
import adafruit_mcp3xxx.mcp3008 as ADC
from adafruit_mcp3xxx.analog_in import AnalogIn
import tkinter as tk
from tkinter import Menu
import easygui
import RPi.GPIO as GPIO
The overall menu and code works until I try to use the Run() function, which uses all of the files and compares AnalogIn to a threshold value previously found. When the Run button is clicked, it comes up with the error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.9/tkinter/__init__.py", line 1892, in __call__
File "/home/pi/Documents/NEA_Python/NEA2023.py", line 280, in Run
File "/usr/local/lib/python3.9/dist-packages/adafruit_mcp3xxx/analog_in.py", line 53, in value
File "/usr/local/lib/python3.9/dist-packages/adafruit_mcp3xxx/mcp3xxx.py", line 80, in read
File "/usr/local/lib/python3.9/dist-packages/adafruit_bus_device/spi_device.py", line 93, in __enter__
File "/usr/local/lib/python3.9/dist-packages/busio.py", line 335, in configure
File "/usr/local/lib/python3.9/dist-packages/adafruit_platformdetect/board.py", line 650, in any_embedded_linux
File "/usr/local/lib/python3.9/dist-packages/adafruit_platformdetect/board.py", line 532, in any_raspberry_pi
File "/usr/local/lib/python3.9/dist-packages/adafruit_platformdetect/board.py", line 205, in _pi_rev_code
File "/usr/local/lib/python3.9/dist-packages/adafruit_platformdetect/__init__.py", line 42, in get_cpuinfo_field
OSError: [Errno 24] Too many open files: '/proc/cpuinfo'
Run() Function code:
def Run():
threshold_valid = False
output_valid = False
#validity checks for all necessary fields
if threshold_valid == False:
thres = open('Threshold.txt', 'r')
threshold = thres.read()
if threshold == '':
print("Missing threshold voltage. Please refer to Main Menu/Options/Edit Muscle Data/Find threshold voltage")
else:
threshold_valid = True
if output_valid == False:
output = open('OutputOption.txt', 'r')
outputOption = output.read()
if outputOption == '':
print("Missing output option. Please refer to Main Menu/Options/Edit Muscle Data/Choose output/ in order to select output")
elif outputOption == 'notification':
with open('Message.txt', 'r') as message:
if message.read() == '':
print("Missing message to display. Please refer to Main Menu/Options/Edit Muscle Data/Choose output/Display message/")
else:
output_valid = True
message.close()
elif outputOption() == 'LED':
output_valid = True
else:
print("Something went wrong. Try deleting stored data at Main Menu/Options/Edit Muscle Data/Delete Data/ and try again")
while threshold_valid == True and output_valid == True:
spiBus = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
# create the chip select
chipSelect = digitalio.DigitalInOut(board.D22)
#creates the object for adc
adc = ADC.MCP3008(spiBus, chipSelect)
#creates analogue input channel on ADC's pin 0
analogueInChan = AnalogIn(adc, ADC.P0)
instantaneousAIn = analogueInChan.value
if instantaneousAIn> int(threshold):
if outputOption == 'LED':
output_flashLED()
elif outputOption == 'notification':
output_notification()
thres.close()
output.close()
All of the validity checks for this work fine, but it fails once it reaches the part inside the 'while threshold_valid == True and output_valid == True:' statement.
The menu is a menubar with cascade menus coming off of it.
The Tkinter menu code is:
#Root window
root = tk.Tk()
root.title("Main Menu")
#Creates a menubar
menubar = Menu(root, bg = 'powderblue', activebackground = '#84b7c4', tearoff = 0)
root.config(menu = menubar)
#Creates the main menu with the menubar as its parent window
#bg is the natural background colour, activebackground is the colour when the mouse hovers over it
main_menu = Menu(menubar, bg = 'lightcyan', activebackground = 'powderblue', tearoff = 0)
#Adds option to main_menu
main_menu.add_command(
label = "Instructions",
command = instructions
)
main_menu.add_command(
label= "Run",
command = Run
)
#Adds the main_menu as cascade to the menubar
menubar.add_cascade(
label = "Options",
menu = main_menu
)
#Creates menu muscleData_menu with main_menu as parent window
muscleData_menu = Menu(main_menu, tearoff = 0, bg = 'lightcyan', activebackground = 'powderblue')
#Adds option to muscleData_menu
muscleData_menu.add_command(
label = "Instructions",
command = instructions
)
muscleData_menu.add_command(
label = "Find threshold voltage",
command = findThreshold
)
#Creates menu output_menu with muscleData_menu as parent window
output_menu = Menu(muscleData_menu, tearoff = 0, bg = 'lightcyan', activebackground = 'powderblue')
#Adds option to output_menu
output_menu.add_command(
label = "Flash LED",
command = menuoption_flashLED
)
output_menu.add_command(
label = "Display message",
command = menuoption_notification
)
#Adds output_menu as cascade within muscleData_menu
muscleData_menu.add_cascade(
label = "Choose output",
menu = output_menu
)
#Creates menu deleteData_menu with muscleData_menu as parent window
deleteData_menu = Menu(muscleData_menu, tearoff = 0, bg = 'lightcyan', activebackground = 'powderblue')
#Adds option to deleteData_menu
deleteData_menu.add_command(
label = "Delete stored threshold voltage",
command = deleteThreshold
)
deleteData_menu.add_command(
label = "Delete stored output option",
command = deleteOutput
)
deleteData_menu.add_command(
label = "Delete stored notification message",
command = deleteMessage
)
deleteData_menu.add_command(
label = "Delete all stored data",
command = wipeData
)
#adds deleteData_menu as cascade to the muscleData_menu
muscleData_menu.add_cascade(
label = "Delete stored data",
menu = deleteData_menu
)
muscleData_menu.add_command(
label = "Run",
command = Run
)
#Adds muscleData_menu as cascade within main_menu
main_menu.add_cascade(
label = "Edit Muscle Data",
menu = muscleData_menu,
)
#Ends menu program
#root.destroy completely erases root/menu
main_menu.add_command(
label = "Quit",
command=root.destroy,
)
#Runs menu continuously until quit, awaiting user inputs
root.mainloop()
The actual menu works fine, the issue resides inside of the Run() function, however it is a Tkinter related issue. As I understand it, the menu works by the root looping continuously in the background whilst allowing other parts of the code to work, so this may be the issue but I'm not sure how to fix it. I have checked and in all instances of me opening the textfiles across the code, I have closed them again directly after. I have changed the ulimit and it continues to show the same error. I couldn't find a working solution in other stackoverflow answers and so believed it was an issue more localised to my exact code, but apologies if this is a repetition.
The full code is in this google doc, if anyone finds it relevant:
https://docs.google.com/document/d/1Oj5M6jSTXepXSBm9kjj4o_41AN4IhGcgc63iedZSMdQ/edit?usp=sharing
Can you limit the number of files that have to be open at the same time?
I was also reading from this thread,
IOError: [Errno 24] Too many open files:
Please have a look , it might help.
You need to use a with block to deal with your files, so they actually get closed and you won't run out of file descriptors.
Replace things like
thres = open('Threshold.txt', 'r')
threshold = thres.read()
with
with open('Threshold.txt', 'r') as f:
threshold = f.read()
I'm having a ton of trouble killing a tkinter window created using the below fashion. I'm getting the error shown below. I'm pretty new to Python, so any help would be greatly appreciated.
class InspectWindow(tk.Frame):
def __init__(self, sender_email, recipient_email, email_body,
master = None):
super().__init__(master)
self.create_widgets()
def create_widgets(self):
self.yes = tk.Button(self)
self.yes['text'] = 'send me!'
self.yes['command'] = self.send_email()
def send_email(self):
root.destroy()
root = tk.Tk()
popup = InspectWindow(sender_email, recipient_email, email_body,
master=root)
popup.mainloop()
Traceback (most recent call last):
File "spam.py", line 108, in <module>
master=root)
File "spam.py", line 16, in __init__
self.create_widgets()
File "AutomateFellowshipEmails.py", line 23, in create_widgets
self.yes['command'] = self.send_email()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1486, in __setitem__
self.configure({key: value})
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1479, in configure
return self._configure('configure', cnf, kw)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1470, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!inspectwindow.!button"
The problem is this line of code:
self.yes['command'] = self.send_email()
It is the exact same as if you did this:
result = self.send_email()
self.yes['command'] = reuslt
Since self.send_email() destroys the root window and then returns None, it's the same as doing this:
root.destroy()
self.yes['command'] = None
Once you destroy the root widget -- which causes all other widgets to be destroyed -- you will get an error anytime you try to call a method on a widget. When you try to configure self.yes, self.yes no longer exists so you get an error.
The solution is to pass a reference to the function of a button so that you don't immediately call it. You do it like this:
self.yes['command'] = self.send_email
Notice the lack of parenthesis on self.send_email. Instead of calling the function immediately, you are telling tk "here is the name of a function that I want you to call when the user clicks the button".
you must keep a reference of master in order to call methods on it.
(and a few other errors too)
something like this (I removed undefined variables mail, etc...)
import tkinter as tk
class InspectWindow(tk.Frame): # if this is a popup, you may like to inherit from tk.TopLevel?
def __init__(self, master = None): # removed undefined variables, you can put them back in
self.master = master # keep a reference of master
super().__init__(self.master)
self.create_widgets()
self.pack() # pack into parent window
def create_widgets(self):
self.yes = tk.Button(self)
self.yes['text'] = 'send me!'
self.yes['command'] = self.send_email # removed () call, this takes a method, not a method call
self.yes.pack() # pack into self
def send_email(self):
self.master.destroy() # call destroy on self.master
# maybe you want to call on self instead, depending what you want to destroy
root = tk.Tk()
popup = InspectWindow(master=root) # removed undefined variables, you can put them back in
popup.mainloop()
No idea what the comments are on this. But, never mind, I fixed it myself.
Took a while to figure out that Python is really sloppy and leaves timers running when you destroy the process completely. Then a while later, you get a blow up when it comes out of the timer and has nowhere to go. That's a very basic thing. You clean up after yourself in a destroy operation. Python does not.
I am able to open and close my toplevel window while keeping the main window going but I get a large, cryptic error message when I close the child window. This happens both when I close it via the X or the defined button in the child window. If I comment out the timer event (self.ater), the error messages do not come up. But I have to have that to update the form.
The error message is coming up on the next timer pop. It is not destroyed and pops but there is nowhere to go any longer.
I can't make the error message show up exactly right. Did the best I could.
Here is the error message block, then the code to create and close the child window.
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.4/tkinter/init.py", line 1536, in call
return self.func(*args)
File "/usr/lib/python3.4/tkinter/init.py", line 585, in callit
func(*args)
File "/home/pi/IltiShares/#Mikes Files/#Python3 Source/GPS Data Show 11-02-2016.py", line 630, in UpdateSky
t_canvas.itemconfig(zz, text=str(self.counter))
File "/usr/lib/python3.4/tkinter/init.py", line 2419, in itemconfigure
return self._configure(('itemconfigure', tagOrId), cnf, kw)
File "/usr/lib/python3.4/tkinter/init.py", line 1313, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".1959370864.1959118928.1959118192.1959118800"
def ShowSky(self):
global t_canvas
global zz
fontsize = 8
labelwidth = 15
textwidth = 17
self.counter += 1
t = Toplevel(self)
t.wm_title("Sky Map or SV's")
frame2 = Frame(t)
frame2.config(height = 400, width = 400) # These are pixels
frame2.config(relief = RIDGE)
frame2.pack(side = LEFT, anchor= 'ne')
t_canvas = Canvas(frame2, height = 300, width = 300, borderwidth = 2,
bg= '#b3ffff', relief = GROOVE)
t_canvas.pack()
self.waiting = StringVar()
self.waiting.set('.')
zz = t_canvas.create_text(50, 50, text = '01')
Button(frame2, text='Close', width = 10, bg = '#FFc0c0', command=t.destroy).pack()
self.after(1000, self.UpdateSky)
pass
def UpdateSky(self):
global t_canvas
global zz
self.counter += 1
# Test movement and text change.
t_canvas.itemconfig(zz, text=str(self.counter))
t_canvas.coords(zz,self.counter,60)
self.after(8000, self.UpdateSky)
pass
I am making a program that draws a line(you decide where is the beggining and end of it with the sliders/scales), problem is im getting these errors(That i wish i understood) when i press the psy Button(code below the errors) :
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\python351\lib\tkinter\__init__.py", line 1549, in __call__
return self.func(*args)
File "C:/Users/Koteu/PycharmProjects/guji/fsd.py", line 23, in creat
cans.create_line(ar1,ar2,br1,br2)
File "C:\python351\lib\tkinter\__init__.py", line 2331, in create_line
return self._create('line', args, kw)
File "C:\python351\lib\tkinter\__init__.py", line 2319, in _create
*(args + self._options(cnf, kw))))
_tkinter.TclError: bad screen distance ".14855536.14855504"
Process finished with exit code 0
anyways, the code :
import os
import sys
from tkinter import *
root = Tk()
app=Frame(root)
root.geometry("1200x1200")
ar1 = Scale(root,from_=0,to=600)
ar2= Scale(app,from_=0,to=600,deafultvar=0)#app instead of root because the button for unknown to me reason
#wouldn't appear in GUI otherwise
br1= Scale(root,from_=0,to=600)
br2= Scale(root,from_=0,to=600)
cans = Canvas(root,width = 500,height = 500)
cans.create_line(600,50,0,50) #This has nothing to do with the actual program by my understanding
def creat():
cans.create_line(ar1,ar2,br1,br2)#< this is what causes the problem i don't understand
psy=Button(root,command=creat,text="karole")
psy.pack()
cans.pack()
ar1.pack()
ar2.pack()
br1.pack()
br2.pack()
mainloop()
also, if that helps, im using py345
cans.create_line(x0, y0, ...) takes an even number of integer coordinates as positional args. You passed widgets, which were turned into their string identifiers. In ".14855536.14855504", '.' represents root, '14855536' is the canvas, and '14855504' is the scale ar1. Instead you need to use the .get() method on the scales to get their integer values. The following works.
from tkinter import *
root = Tk()
root.geometry("1200x1200")
ar1 = Scale(root,from_=0,to=600)
ar2= Scale(root,from_=0, to=600)
br1= Scale(root,from_=0, to=600)
br2= Scale(root,from_=0, to=600)
cans = Canvas(root, width=500, height=500)
def creat():
cans.create_line(ar1.get(), ar2.get(), br1.get(), br2.get())
psy=Button(root, command=creat, text="karole")
ar1.pack()
ar2.pack()
br1.pack()
br2.pack()
psy.pack()
cans.pack()
root.mainloop()
A couple of other fixes: the defaultvar option is not valid and caused an error; mainloop() instead of root.mainloop() caused tk to create a second Tk object, which is a bad idea.
EDIT: added the code that works.
I am attempting to run the following code for a text editor.
def newfile():
current = None
def create_file(entry):
nonlocal current
current = open(entry.get(),'w')
e.master.destroy()
chdir(askdirectory())
name=Tk()
name.title("Name the File?")
prompt=Label(name, text="Enter name for new file:")
prompt.grid(row=0)
e=Entry(name)
e.grid(row=1)
e.insert(0, "Untitled")
create=Button(name, text="Create", command = lambda: create_file(e))
create.grid(row=2, column=3)
name.mainloop()
return current
But I get this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.2/tkinter/__init__.py", line 1402, in __call__
return self.func(*args)
File "<pyshell#1>", line 15, in <lambda>
create=Button(name, text="Create", command = lambda: create_file(e))
File "<pyshell#1>", line 5, in create_file
current = open(entry.get(),'w')
TypeError: an integer is required
It wants an integer argument.
Does anyone know what that is?
An instance of the Entry widget does not require any arguments for the get method. You are calling it correctly. Neither does the standard open command require an integer. My guess is, one of entry or open is not what you think it is. Maybe you have a method or another object with one of those names?
I suggest putting the call to get and the open on separate lines, to make sure you know which part of that statement is throwing the error:
text = entry.get()
current = open(text, 'w')