Hi
I made a music downloading program that works great it tells you the percent its done and then if i move the window at all it stops downloading .
I made a diffrent little script that downloads a specified mp3 on the web and i can move it as much as i want and it doesent stop.
the only reason im not putting up the code is that it is really long. Its around 1500 lines. Here is the small script that i made to download one file.
does any one know why it stops the program from working?
the little script:
from Tkinter import *
from urllib2 import *
admin = Tk()
Admin = Tk()
listbox = Listbox(admin, bg="PURPLE")
listbox.pack()
def __init__(self, master):
def replay():
Admin.destroy()
os.system('WhaleWire.exe')
frame = Frame(master)
frame.pack()
image1 = PhotoImage(file="whalewire.gif")
w = image1.width()
h = image1.height()
master.geometry("%dx%d+0+0" % (w, h))
# tk.Frame has no image argument
panel1 = Label(master, image=image1)
panel1.pack(side='top', fill='both', expand='yes')
panel1.image = image1
self.e = Entry(frame)
self.e.grid(row=0, column=0)
b = Button(frame, text='Search', command=self.whale)
b.grid(row=0, column=1)
def library():
path = 'C:\WhaleWire\Downloaded'
aw=[]
for infile in glob.glob( os.path.join(path,'*.mp3') ):
libr = infile.split('Downloaded',1)
aw.append('\n')
aw.append(infile)
la = Label(Admin,width=100,height=50, text=aw).grid(row=0,column=7)
b2s = Button(Admin,text='Search', command=replay).grid(row=0,column=8)
b11 = Button(frame, text='Library', command=library)
b11.grid(row=0, column=3)
def fores():
chunks = 10000
dat = ''
song = '3 rounds and a sound'
url = 'http://bonton.sweetdarkness.net/music/Blind%20Pilot%20--%203%20Rounds%20and%20A%20Sound.mp3'
down = urlopen(url)
downso = 0
tota = down.info().getheader('Content-Length').strip()
tota = int(tota)
while 1:
a = down.read(chunks)
downso += len(a)
if not a:
break
dat += a
percent = float(downso) / tota
percent = round(percent*100, 1)
listbox.insert(END, percent)
listbox.update()
listbox.delete(0, END)
listbox.insert(END, percent)
listbox.update()
button = Button(Admin, text='Download', command=fores)
button.pack()
button = Button(Admin, text='Download', command=fores)
button.pack()
mainloop()
Most likely the problem is because you are calling update. You should never do that unless you know for certainty what the ramifications are. update causes a new event loop to be entered. Essentially, you end up with an infinite loop inside an infinite loop.
Try changing your update to update_idletasks and see if that solves your problem. This variation of update only processes "idle" events such as screen redraws and is considerably less likely to cause problems.
Also, you definitely don't need "update; insert; delete; update". That won't have any noticeable effect. A single call to update_idletasks after the delete is sufficient.
Finally, you can avoid the use of update_idletasks completely by rearranging your code. Write a function that reads a single chunk of data and updates the progress bar. Then, if it hasn't reached EOF, use after to call that function again a few milliseconds later. When it reaches EOF it stops calling itself. Doing this means you don't have to create your own potentially infinite loop, and the event loop is guaranteed to be entered once per iteration. Once this EOF is detected you can then call a function (again using after) to do any final processing.
Related
I'm trying to create a series of tkinter buttons with a loop that are .grid'd to their own respective frames. I want every button to have a function that .tkraises the next frame in the list of frames that I create. Any idea how? Here's what I've got. The buttons/ frames are created I think but the .tkraise function doesn't work. Thanks
from tkinter import *
from PIL import ImageTk, Image
## Define root and geometry
root = Tk()
root.geometry('200x200')
# Define Frames
winlist = list()
winlist = Frame(root, bg='red'), Frame(root, bg='green'), Frame(root, bg='blue')
# Configure Rows
root.grid_rowconfigure(0, weight = 1)
root.grid_columnconfigure(0, weight = 1)
# Place Frames
for window in winlist:
window.grid(row=0, column = 0, sticky = 'news')
# Raises first window 'To the top'
winlist[0].tkraise()
# Function to raise 'window' to the top
def raise_frame(window):
window.tkraise()
d = {}
count = 0
for x in range(0, 3):
d["label{0}".format(x)] = Label(winlist[x], text = "label{0}".format(x))
if count <=1:
try:
d["button{0}".format(x)] = Button(winlist[x], text = "button{0}".format(x), command = raise_frame(winlist[x+1]))
d["button{0}".format(x)].pack(side=TOP)
except:
pass
else:
d["label{0}".format(x)].pack(side=TOP)
count += 1
root.mainloop()
The issue is on the command option of the line:
d["button{0}".format(x)] = Button(winlist[x], text = "button{0}".format(x), command = raise_frame(winlist[x+1]))
It will execute raise_frame(winlist[x+1]) immediately and then assign the result (which is None) to command option. Therefore, clicking the button later does nothing.
You need to use lambda instead:
d["button{0}".format(x)] = Button(winlist[x], text="button{0}".format(x),
command=lambda x=x: raise_frame(winlist[x+1]))
I answered my own question. instead of using frames I went back to creating Tk() objects. I made a loop that runs a function that creates Tk() objects and passed in a variable that carried the count of my loop. I used that count to change information on each Tk() object and instead made the 'command =' of each button include a Tk().destroy function. This creates all the windows I wanted all at once and I can perform an action and exit the window. It's progress. Thanks,
Tim,
I've made a Python program which reads the file and then sends/receives the data towards/from the microcontroller and everything worked well until I added a menu to display short instructions.
Since the UART communication has to run in a separate thread I used threading and StringVar() to access the data from the main thread.
To demonstrate the problem I've made a short example which has nothing to do with microcontrollers.
The steps to reproduce the problem:
Click the Next screen radio button to open the second screen (at the initial screen the menu works well)
Click Help > Instructions
After (or sometimes even before) closing the message box the program will crash with:
TclStackFree: incorrect freePtr. Call out of sequence?
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
Note:
In the original program where there are more UI elements the program always crashes before showing the message box and after removing even more UI elements the program doesn't crash every time - that's why I've left some "redundant" labels. When some more labels are added the program will crash every time.
I have narrowed the cause of the crash to:
displayVal.set()
in checkForData() function. By removing that instruction everything works well.
Moreover, after removing displayVal.trace("w", displayVal_trace) the program will not crash anymore but opening the menu will still temporarily freeze the working thread.
I thought after displayVal.set() Tkinter is trying to update the label but can't because of showing the menu - however, the problem remained even after I removed label_data = Label(up2, textvariable = displayVal).
Here is the stripped down code tested with Python 2.7:
from Tkinter import *
import tkMessageBox
import threading
import time
threadRun = True
checkDelay = 0.5
def checkForData():
global threadRun, checkDelay
print "Simulating thread for receiving messages from UART"
while threadRun == True:
print time.time()
displayVal.set(time.time()) # <-- if removed the menu works OK (no crash)
time.sleep(checkDelay)
print "No more receiving of messages"
def listenForData():
t = threading.Thread(target=checkForData)
t.daemon = False
t.start()
def stopListening():
global threadRun, checkDelay
threadRun = False
time.sleep(checkDelay + 0.1)
def exit_handler():
print "Exiting..."
stopListening()
root.destroy()
root = Tk()
right = int((root.winfo_screenwidth() - 600) / 2)
down = int(root.winfo_screenheight() / 3 - 400 / 2)
root.geometry("600x400+%d+%d" % (right, down))
root.resizable(width = False, height = False)
root.protocol("WM_DELETE_WINDOW", exit_handler)
displayVal = StringVar()
displayVal.set("nothing")
def setupView():
global masterframe
masterframe = Frame()
masterframe.pack(fill=BOTH, expand=True)
selectPort() # the 1st screen for selecting COM ports
def selectPort():
global masterframe, radioVar
# remove everything from the frame
for child in masterframe.winfo_children():
child.destroy()
radioVar = StringVar()
l1 = Label(masterframe, text = "Select...")
l1.pack(pady=(50, 20))
# this would be a list of detected COM ports
lst = ["Next screen"]
if len(lst) > 0:
for n in lst:
r1 = Radiobutton(masterframe, text=n, variable=radioVar, value=n)
r1.config(command = next_screen)
r1.pack()
def mainScreen():
global masterframe, term, status
# remove previous screen from the frame
for child in masterframe.winfo_children():
child.destroy()
up1 = Frame(masterframe)
up1.pack(side=TOP)
up2 = Frame(masterframe)
up2.pack()
terminal = Frame(masterframe)
terminal.pack()
down = Frame(masterframe)
down.pack(side=BOTTOM, fill=BOTH)
label_top = Label(up1, text="Something")
label_top.pack(pady=5)
label_data = Label(up2, textvariable = displayVal)
label_data.pack(pady=(10, 0))
term = Text(terminal, height=10, width=35, bg="white")
term.pack()
term.tag_config("red", foreground="red")
term.tag_config("blue", foreground="blue")
term.insert(END, "The file has been read\n", "red")
term.insert(END, "File contents:\n")
term.insert(END, data)
status = Label(down, text="Status...", bd=1, relief=SUNKEN, anchor=W, bg="green")
status.pack(fill=X)
displayVal.trace("w", displayVal_trace) # <-- if removed only temporary freeze but no crash
def displayVal_trace(name, index, mode):
global term
if(displayVal.get() != "NOTHING"):
term.insert(END, "\nReceived: ", "blue")
term.insert(END, displayVal.get())
term.see(END)
def next_screen():
listenForData()
mainScreen()
def stop():
stopListening()
def instructions():
tkMessageBox.showinfo("Help", "This is help")
main_menu = Menu(root)
root.config(menu = main_menu)
help_menu = Menu(main_menu)
main_menu.add_cascade(label="Help", menu=help_menu)
help_menu.add_command(label="Instructions", command=instructions)
data = [[1], [2], [3]] # this would be the data read from the file
b1 = data[0][0]
b2 = data[1][0]
b3 = data[2][0]
print "Read from file:", b1, b2, b3
setupView()
root.mainloop()
I am currently trying to make a GUI to an existing python program using Tkinter. The program gives the user two options from which the user must choose to either accept or decline. Before using Tkinter the options were placed in the terminal and awaited for a raw_input. (y/n). How can I make this so the canvas text updates with the new data and awaits for the users button click?
To make my question more specific: How can I run another programs code while the Tkinter mainloop is running and make these two interact?
Example code below.
from Tkinter import *
root = Tk()
root.resizable(width=False, height=False)
root.geometry('{}x{}'.format(500,550))
root.wm_title("Tkinter test")
BtnFrame = Frame (root)
BtnFrame.pack(side = BOTTOM)
BtnFrame.place(y=450, x=20)
canvas_1 = Canvas(root, width = "200", height ="300")
canvas_2 = Canvas(root, width = "250", height ="300")
canvas_1.pack(side = LEFT)
canvas_2.pack(side = RIGHT)
textfield_1 = canvas_1.create_text(100,50)
textfield_2 = canvas_2.create_text(100,50,)
def update_textfiel_1(text):
global textfield_1
canvas_1.delete(textfield_1)
textfield = canvas.create_text(100,50,text = text)
def update_textfiel_2(text):
global textfield_2
canvas_2.delete(textfield_2)
textfield1 = canvas1.create_text(100,50,text = text)
Accept = Button(BtnFrame, text="Accept", width=25)
Decline = Button(BtnFrame, text="Decline", width=25)
Accept.pack(side = LEFT)
Decline.pack(side = RIGHT)
root.mainloop()
First off you have some inconsistent variable names in your update_textfiel functions, you can greatly simplify it by using .itemconfigure (documentation for methods on canvas widget)
def update_textfiel_1(new_text):
canvas_1.itemconfigure(textfield_1, text=new_text)
def update_textfiel_2(new_text):
canvas_2.itemconfigure(textfield_2, text=new_text)
If I understand correctly you want a way to have a function that will simply wait for the user to press one of the buttons and then return the result, this is very easy with tkMessageBox:
question = """Do you accept {}?
if you say no you will instead get {}"""
#this message can GREATLY be improved
# But I really don't understand what you are using it for...
def user_confirmation(name1, name2):
response = tkMessageBox.askyesno("Accept or Decline",question.format(name1,name2))
print(response)
if response: # == True
return name1
else:
return name2
I have not yet found a way to make a blocking function that works with the window you have currently...
from Tkinter import *
import tkFileDialog
import tkMessageBox
import os
import ttk
import serial
import timeit
import time
######################################################################################
class MyApp:
def __init__(self, parent):
########################################################
#Setup Frames
self.MiddleFrame = Frame(parent) #Middle Frame
self.MiddleFrame.pack()
#GLOBAL VARIABLES
self.chip_number = 0 #number of chip testing
###########################################
#Middle Frame setup
Label(self.MiddleFrame, text='Done').grid(row=8, column=1, sticky = E)
self.Done = Canvas(self.MiddleFrame, bg="yellow", width=10, height=10)
self.Done.grid(row=8, column=2)
Label(self.MiddleFrame, text='Chip Number:').grid(row=9, column=1, sticky = E)
#start button
self.button1 = Button(self.MiddleFrame,state=NORMAL, command= self.start_pre)
self.button1["text"]= "START"
self.button1.grid(row=1, column=2, sticky = E)
###########################################
#Action of Start Button
def start_pre(self):
x = 0
while x<10000:
self.start_button()
x=x+1
#Talking to Board
def start_button(self):
#increase chip count number and update
self.chip_number += 1
Label(self.MiddleFrame, text=str(self.chip_number)).grid(row=9, column=2, sticky = E)
#reset-yellow
self.reset_color()
print "Still Working", self.chip_number
self.Done.configure(background="green")
self.Done.update_idletasks()
###############################################################
#Color Boxes
#Reset
def reset_color(self):
self.Done.configure(background="yellow")
self.Done.update_idletasks()
###############################################################################################################
#Start Programs
root = Tk() #makes window
root.title("Interface")
myapp = MyApp(root) #this really runs program
root.mainloop() #keep window open
With my program, i first push the start button.
I will print "still working" and the GUi will update chip number and blink done light over and over. The start button go to function that will execute 10000 times. However after 3000 iterations, the gui freeze, but the program is still print "still working". How do I keep the gui from crashing?
There are many problems with your code. For one, this is fundamentally flawed:
while self.stop == True:
self.start_button()
time.sleep(0.5)
You simply can't expect a GUI to behave properly with code like that. As a general rule of thumb you should never have the main thread of a GUI call sleep. Causing sleep prevents the event loop from processing any events, including low level events such as requests to refresh the screen.
The use of sleep has been asked and answered many times on stackoverflow. You might find some of those questions useful. For example,
windows thinks tkinter is not responding
Python Tkinter coords function not moving canvas objects inside loop
How do widgets update in Tkinter?
Tkinter multiple operations
Python Tkinter Stopwatch Error
You have another problem that falls into the category of a memory leak. From that while loop, you call self.start_button() indefinitely. This happens about once a second, due to sleep being called for half a second in the loop, and another half a second in start_button.
Each time you call start_button, you create another label widget that you stack on top of all previous widgets in row 9, column 2. Eventually this will cause your program to crash. I'm surprised that it causes your program to fail so quickly, but that's beside the point.
My recommendation is to start over with a simple example that does nothing but update a label every second. Get that working so that you understand the basic mechanism. Then, once it's working, you can add in your code that reads from the serial port.
May I suggest that you start over with the following code? You can port in back to Python 2 if needed, but your program has been rewritten to use Python 3 and has been designed to use tkinter's ability to schedule future events with the after methods. Hopefully, you will find the code easier to follow.
import collections
import timeit
import tkinter
def main():
root = Application()
root.setup()
root.mainloop()
class Application(tkinter.Tk):
def setup(self):
mf = self.__middle_frame = tkinter.Frame(self)
self.__middle_frame.grid()
bf = self.__bot_frame = tkinter.Frame(self)
self.__bot_frame.grid()
self.__port_set = False
self.__chip_number = 0
self.__chip_pass_num = 0
self.__chip_fail_num = 0
self.__chip_yield_num = 0
self.__stop = True
self.__widgets = collections.OrderedDict((
('COT', 'Continuity Test'), ('CHE', 'Chip Erase'),
('ERT', 'Erase Test'), ('WRT', 'Write Test'),
('WIRT', 'Wire Reading Test'), ('WIT', 'Wire Reading Test'),
('WRAT', 'Write All Test'), ('DO', 'Done')))
for row, (key, value) in enumerate(self.__widgets.items()):
label = tkinter.Label(mf, text=value+':')
label.grid(row=row, column=0, sticky=tkinter.E)
canvas = tkinter.Canvas(mf, bg='yellow', width=10, height=10)
canvas.grid(row=row, column=1)
self.__widgets[key] = label, canvas
self.__cn = tkinter.Label(mf, text='Chip Number:')
self.__cn.grid(row=8, column=0, sticky=tkinter.E)
self.__display = tkinter.Label(mf)
self.__display.grid(row=8, column=1, sticky=tkinter.E)
self.__button = tkinter.Button(bf, text='START',
command=self.__start_pre)
self.__button.grid(sticky=tkinter.E)
def __start_pre(self):
self.__button['state'] = tkinter.DISABLED
self.__start_button(0)
def __start_button(self, count):
if count < 100:
self.__chip_number += 1
self.__display['text'] = str(self.__chip_number)
self.__widgets['DO'][1]['bg'] = 'yellow'
start_time = timeit.default_timer()
print('Still Working:', self.__chip_number)
self.after(500, self.__end_button, count)
else:
self.__button['state'] = tkinter.NORMAL
def __end_button(self, count):
self.__widgets['DO'][1]['bg'] = 'green'
self.after(500, self.__start_button, count + 1)
if __name__ == '__main__':
main()
I made a very simple gui that has a button and shows an image(.gif). My goal is to output another .gif whenever you press the button. There are 2 .gif files in my file directory and the point is to keep switching between these two whenever you press the button.
#Using python2.7.2
import Tkinter
root = Tkinter.Tk()
try:
n
except:
n = 0
def showphoto(par):
if par%2 == 0:
try:
label2.destroy()
except:
pass
photo = Tkinter.PhotoImage(file="masc.gif")
label2 = Tkinter.Label(image=photo)
label2.image = photo
label2.pack()
else:
try:
label2.destroy()
except:
pass
photo = Tkinter.PhotoImage(file="123.gif")
label2 = Tkinter.Label(image=photo)
label2.image = photo
label2.pack()
myContainer1 = Tkinter.Frame(root, width = 100, height = 100)
myContainer1.pack()
def callback(event):
global n
showphoto(n)
n = n + 1
button1 = Tkinter.Button(myContainer1)
button1["text"]= "Next pic"
button1["background"] = "green"
button1.bind("<Button-1>", callback(n))
button1.pack()
root.mainloop()
The current code just outputs the first image (masc.gif) but when I press the button it doesn't switch to the other image(123.gif). What am I doing wrong?
This can achieved much easier with classes as the class holds all the data necessary without the use of global variables.
import Tkinter as tk
from collections import OrderedDict
class app(tk.Frame):
def __init__(self,master=None, **kwargs):
self.gifdict=OrderedDict()
for gif in ('masc.gif','123.gif'):
self.gifdict[gif]=tk.PhotoImage(file=gif)
tk.Frame.__init__(self,master,**kwargs)
self.label=tk.Label(self)
self.label.pack()
self.button=tk.Button(self,text="switch",command=self.switch)
self.button.pack()
self.switch()
def switch(self):
#Get first image in dict and add it to the end
img,photo=self.gifdict.popitem(last=False)
self.gifdict[img]=photo
#display the image we popped off the start of the dict.
self.label.config(image=photo)
if __name__ == "__main__":
A=tk.Tk()
B=app(master=A,width=100,height=100)
B.pack()
A.mainloop()
Of course, this could be done more generally ... (the list of images to cycle through could be passed in for example), and this will switch through all the images in self.gifs ...
This approach also removes the necessity to destroy and recreate a label each time, instead we just reuse the label we already have.
EDIT
Now I use an OrderedDict to store the files. (keys=filename,values=PhotoImages). Then we pop the first element out of the dictionary to plot. Of course, if you're using python2.6 or earlier, you can just keep a list in addition to the dictionary and use the list to get the keys.
button1 = Tkinter.Button(myContainer1)
button1["text"]= "Next pic"
button1["background"] = "green"
button1.bind("<Button-1>", callback(n))
First, you bind the <Button-1> event to None (that's what callback(n) evaluates to). You should bind it to callback (no parentheses a.k.a the call operator).
Second, I suggest you change callback to not accept any arguments, remove the bind call and create your button as:
button1 = Tkinter.Button(myContainer1, command=callback)