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()
Related
I have a question regarding the use of a stop button in Tkinter.
For an experiment, I have to set up and X/Y stage that works by using two stepper motors. The arduino program works perfectly. The only problem is that when I activate the start function, which drives the stage to various coordinates, it freezes. Now the problem is that it has to run for weeks on end and it needs a stop button for emergencies and stopping the stepper motor in general. The stop button has to do two things: it has to stop the stepper driver motors, and it has to break the tkinter.after loop. However, due to the freezing, it is impossible to click on the button.
Here is my code:
import tkinter as tk
import serial
ser = serial.Serial('COM5', 115200)
running = False
def quit():
"""Function that closes the serial port and destroys the root of the GUI"""
global root
ser.close()
root.destroy()
def route():
"""Writes coordinates to the arduino, which in return drives the stepper motors"""
if running == True:
# The g line stands for go to!
ser.write(b'g115000\r\n')
root.after(50)
ser.write(b'g225000\r\n')
root.after(30000)
ser.write(b'g1400\r\n')
root.after(50)
ser.write(b'g2500\r\n')
root.after(12000,route())
def zeroing():
"""Zeros the program, this is necessary for the stage to
calibrate it's boundary conditions"""
#zeros the stage so that it is ready to use!
varLabel.set("zeroing, please move away from the stage")
#the z command zeros the motors for boundary business
ser.write(b'z\r\n')
def run_program():
"""Runs the function Route and sets running to True (not a good start/stop system)"""
#starts the program, but only after you zero the stage
global running
running = True
varLabel.set("Program running")
route()
def stop_program():
"""Sets the running flag to False and sends a stop command to the arduino"""
#stops the program immediately
global running
running = False
varLabel.set("Program stopped,please zero before continuing")
#the s byte is a command that stops the stepper motors
ser.write(b's\r\n')
if __name__== "__main__":
root = tk.Tk()
canvas1 = tk.Canvas(root, width=800, height=400)
canvas1.pack()
root.title('XY-stage controller')
#instructions
instructions = tk.Label(root,text='Enter the amount of hours you want your measurements to last in the text box.'
'\n Click on run program to start a measurement session.'
'\n Click on stop incase of an emergency or if it is wanted to stop the program.',
font = "Raleway")
instructions.pack(side='bottom')
# initialize active labels
varLabel = tk.IntVar()
tkLabel = tk.Label(textvariable=varLabel,)
tkLabel.pack(side='top')
# Buttons for initializing a bunch of good functions
zerobutton = tk.IntVar()
tkrunprogram= tk.Button(
root,
text='Zero',
command = zeroing,
height = 4,
fg = "black",
width = 10,
bg = 'gray',
bd = 5,
activebackground = 'green'
)
tkrunprogram.pack(side='top')
runprogbutton = tk.IntVar()
tkrunprogram= tk.Button(
root,
text='Run Program',
command = run_program,
height = 4,
fg = "black",
width = 10,
bg = 'gray',
bd = 5,
activebackground = 'green'
)
tkrunprogram.pack(side='top')
stopbutton = tk.IntVar()
tkstopprog= tk.Button(
root,
text='Stop Program',
command = stop_program,
height = 4,
fg = "black",
width = 10,
bg = 'gray',
bd = 5,
activebackground = 'red'
)
tkstopprog.pack(side='top')
Buttonquit = tk.IntVar()
tkButtonQuit = tk.Button(
root,
text='Quit',
command = quit,
height = 4,
fg = "black",
width = 10,
bg = 'yellow',
bd = 5
)
# initialize an entry box
entry1 = tk.Entry(root)
durbox = canvas1.create_window(400, 200, window=entry1)
tkButtonQuit.pack(side='top')
root.mainloop()
The after commands in the end will introduce pauses of 60 minutes, which would make the program freeze for 60 minutes. Hopefully there is an easy solution to interrupting the function!
Thank you in advance!
You can make use of multithreading. Make all the communication in a separate thread and also make sure you don't update the GUI components in the child thread.
Here is a minimal example:
import serial
import tkinter as tk
from threading import Thread
import time
def start():
global running
stop()
btn.config(text="Stop", command=stop)
running = True
info_label["text"] = "Starting..."
thread = Thread(target=run, daemon=True)
thread.start()
def run():
ser = serial.Serial("COM5", 115200, timeout=2)
while running:
ser.write(b'g115000\r\n')
time.sleep(50)
ser.write(b'g225000\r\n')
time.sleep(30000)
ser.write(b'g1400\r\n')
time.sleep(50)
ser.write(b'g2500\r\n')
ser.write(b's\r\n')
ser.close()
def stop():
global running
running = False
info_label["text"] = "Stopped"
btn.config(text="Start", command=start)
root = tk.Tk()
running = False
info_label = tk.Label(root, text="INFO:")
info_label.pack()
btn = tk.Button(root, text="Start", command=start)
btn.pack()
root.mainloop()
after(x000) is effectively the same as time.sleep(x) - it puts the whole app to sleep. As a general rule of thumb, you should never do this in the same thread as the GUI. That doesn't mean you need to use threads, however.
tkinter's after method lets you schedule commands to run in the future. If the commands you are running are fast such as sending a few bytes down a serial connection, this is really all you need. It is less complex and has less overhead than using threads.
For example, your route function can probably be written something like this:
def route():
if running == True:
# immediately write this:
ser.write(b'g115000\r\n')
# after 50ms, write this:
root.after(50, ser.write, b'g225000')
# after 30 more seconds, write this
root.after(50+30000, ser.write, b'g1400\r\n')
# and then after 50ms more, write this
root.after(50+30000+50, ser.write, b'g2500\r\n')
# and finally, after 12 seconds, do it all again
root.after(50+30000+50+12000,route)
Once you call this once, you don't need to call it again, and you don't need to call it in a thread. It simply places some work on a queue that gets picked up some time in the future.
Since each call to root.after returns an id, you can save these ids so that in the case of wanting to stop everything, you can call after_cancel on each saved id.
Another way is to define a job as a series of delays and then bytes to write. For example:
job = (
(0, b'g115000\r\n'),
(50, b'g225000'),
(30000, b'g1400\r\n'),
(50, b'g2500\r\n'),
)
Then, your route function can look something like this (untested, but this is pretty close)
def route(job):
global after_id
delay = 0
for (delta, data) in job:
delay += delta
root.after(delay, ser.write, data)
delay += 12000
root.after(delay, route, job)
There are many variations of that theme. For example, you could create a Job class that implements this logic, or the job could contain the commands rather than the data. The point being, you can define a data structure that defines work to be done, and then use after to schedule that work.
The purpose is that input fn takes string input and pass it into GUI fn which runs the condition and ammend tkinter window accordingly.
#*********************************** IMPORTING MODULES*****************
import tkinter
from tkinter import*
import tkinter.messagebox
import sqlite3
import os
import threading
from time import sleep
from input import*
conn = sqlite3.connect('portal.db')
c = conn.cursor()
global a
#*************** TKINTER GUI CODE******************
def gui(a):
window = tkinter.Tk()
window.title("Smart Notice Board")
#********************** FRAMES OF MAIN WINDOW(HOME)******************
top = Canvas(window,width=1024,height=184)
top.pack(fill=X)
middle = Canvas(window, width=1024, height=450, bg='steelblue')
middle.pack(fill=X)
main_left = Canvas(middle, width=275, height=450, bg='lightgreen')
main_left.pack(side=LEFT)
main_right = Canvas(middle, width=800, height=450, bg='steelblue')
main_right.pack(side=RIGHT)
bottom = Canvas(window, width=1024, height=70, bg='black')
bottom.pack(fill=X)
#************************** IMAGES********************
i_top = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\BG.png')
top.create_image(0,10, anchor=tkinter.NW,image = i_top)
i_right = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\aus1.png')
main_right.create_image(0,0, anchor=tkinter.NW,image = i_right)
#i_left = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\widget1.png')
#main_left.create_image(0,0, anchor=tkinter.NW,image = i_left)
t1 = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\first.png')
t2 = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\BG.png')
#***************** TIMETABLE IMAGE VIEWING FN***********************
def home():
main_right.create_image(0,0, anchor=tkinter.NW,image = t2)
#*********************** TIMETABLE BUTTON PRESS FN*************************
def timetable():
main_right.create_image(0,0, anchor=tkinter.NW,image = t1)
#******************************* CONDITIONS**********************
if a == "NULL":
timetable()
if a == "HOME":
home()
#*********************** MAIN MENU BUTTONS****************
button_1 = Button(text = ' HOME', anchor = 'w', height = 2, width = 8,activebackground = '#33B5e5',bg = 'brown',fg = 'white',command = home)
top.create_window(2,150,anchor = 'nw', window = button_1)
button_2 = Button(text='TIMETABLE', height = 2, width=12, activebackground = '#33B5e5', bg = 'brown', fg = 'white',command = timetable)
top.create_window(75,150, anchor='nw', window = button_2)
window.mainloop()
#************************** MAIN LOOP************************
if __name__ == "__main__":
#print(valuea())
a=valuea()
gui(a)
Now what I want is continuously run that thing and update Tkinter window; but the 2btn fn only takes it one time and pass it into GUI fn which runs tkinter and it stucks on window.mainloop as tkinter is infinity loop.
Please suggest me a solution also u can run this code by only setting pictures from your computer
The standard method to run code regularly in the mainloop is to register a timeout function using the after method of the root window.
But, whatever you do in such a timeout function (and indeed in all other callbacks), it should not block, because that would lock up the mainloop! So you cannot use input. But you could read from sys.stdin, which is a io.TextIOWrapper instance.
You could use input in a second thread. But since Tkinter isn't thread-safe, that second thread should not use Tkinter functions or methods. So you should not simply update a label from the second thread. You could save/append the input to a global variable, but you'd have to protect that with a lock or mutex. And you'd need to use a timeout function in the main Tkinter thread to test if the lock or mutex is released by the second thread so the Tkinter thread can claim it and access the data. As you can see this is really complicated. So mixing Tkinter and threads is generally not recommended.
In order to ammend tkinter window and show the text and open picture.I did slight change in the input.py file;and coded as below
def vala():
a=speech()
if a == "HOME":
home()
if a == "NULL":
timetable()
window.after(1000,vala)
what I have done is I convert it into recursive fn that call itself after 1000ms. In this way, it can be done.
Basically, I have done it with Pocketsphinx as Input (i.e. my project takes speech input and open file/image in tkinter screen and it runs continuously)
I'm new to python coding and I have been working on a project which could click on an image based on a chosen color. I have been using a program which loops the search 50 times when I click the start button. However, I have been trying to implement a stop button, but the problem is that my code freezes when the loop is running. Any ideas?
I have heard to try threading but it seems very complicated and I have been unable to follow any tutorials properly in relation to my code. By the way, the image searched has been testing images I've been using stored inside the program files.
from imagesearch import *
import pyautogui
import tkinter as tk
from tkinter import *
from tkinter.ttk import *
import time
import threading
# ---Defined Programs---
def run():
global enterColor
enterColor = str(enterColorField.get())
program(enterColor)
def program(color):
whitePos = imagesearch_numLoop(str(color) + ".PNG", 0, 50)
pyautogui.moveTo(whitePos[0] + 20, whitePos[1] + 10)
pyautogui.click()
def stop():
print("Placeholder")
# ---Main Runner---
window = tk.Tk()
window.geometry("250x250")
window.configure(background="#181b54")
app = tk.Frame(window)
app.grid()
enterColorLabel = tk.Label(window, text="Enter Color:", bg="#181b54", fg="white")
enterColorLabel.place(x=10, y=50)
enterColorField = Combobox(window)
enterColorField['values'] = ("Black", "White")
enterColorField.current("0") # set the selected item
enterColorField.place(x=10, y=70)
submitButton = tk.Button(window, text="Start", bg="#66ff00", command=run)
submitButton.place(x=10, y=130)
stopButton = tk.Button(window, text="Stop", bg="red", command=stop)
stopButton.place(x=50, y=130)
window.mainloop()
#---New Python Script---
import cv2
import numpy as np
import pyautogui
import random
import time
def imagesearch_numLoop(image, timesample, maxSamples, precision=0.8):
pos = imagesearch(image, precision)
count = 0
while pos[0] == -1:
print(image+" not found, waiting")
count = count + 1
if count>maxSamples:
break
pos = imagesearch(image, precision)
return pos
Whenever clicking start, the whole code freezes. I can't even (x) out.
Here's a hopefully simple multiprocessing recipe that will work for you. We'll have three main functions. The first will be an example loop that you would put your processing inside. I included arguments in the function to show you that it's possible to pass args and kwargs while using multiprocessing.
def loop(a, b, c, d):
# Will just sleep for 3 seconds.. simulates whatever processing you do.
time.sleep(3)
return
Next is a function we will use to queue the multiprocessing process.
def queue_loop():
p = multiprocessing.Process(target = loop,
args = (1, 2),
kwargs = {"c": 3, "d": 4})
# You can pass args and kwargs to the target function like that
# Note that the process isn't started yet. You call p.start() to activate it.
p.start()
check_status(p) # This is the next function we'll define.
return
Then, you may be interested in knowing the status of your process throughout its execution. For example it is sometimes desirable to disable certain buttons while a command is being run.
def check_status(p):
""" p is the multiprocessing.Process object """
if p.is_alive(): # Then the process is still running
label.config(text = "MP Running")
mp_button.config(state = "disabled")
not_mp_button.config(state = "disabled")
root.after(200, lambda p=p: check_status(p)) # After 200 ms, it will check the status again.
else:
label.config(text = "MP Not Running")
mp_button.config(state = "normal")
not_mp_button.config(state = "normal")
return
Throwing this all together into one snippet:
import tkinter as tk
import multiprocessing
import time
def loop(a, b, c, d):
# Will just sleep for 3 seconds.. simulates whatever processing you do.
time.sleep(3)
return
def queue_loop():
p = multiprocessing.Process(target = loop,
args = (1, 2),
kwargs = {"c": 3, "d": 4})
# You can pass args and kwargs to the target function like that
# Note that the process isn't started yet. You call p.start() to activate it.
p.start()
check_status(p) # This is the next function we'll define.
return
def check_status(p):
""" p is the multiprocessing.Process object """
if p.is_alive(): # Then the process is still running
label.config(text = "MP Running")
mp_button.config(state = "disabled")
not_mp_button.config(state = "disabled")
root.after(200, lambda p=p: check_status(p)) # After 200 ms, it will check the status again.
else:
label.config(text = "MP Not Running")
mp_button.config(state = "normal")
not_mp_button.config(state = "normal")
return
if __name__ == "__main__":
root = tk.Tk()
mp_button = tk.Button(master = root, text = "Using MP", command = queue_loop)
mp_button.pack()
label = tk.Label(master = root, text = "MP Not Running")
label.pack()
not_mp_button = tk.Button(master = root, text = "Not MP", command = lambda: loop(1,2,3,4))
not_mp_button.pack()
root.mainloop()
The result is that when you click the "Using MP" button, the command buttons will be disabled and the process will be started without freezing your UI. Clicking the "Not MP" button will start the function like 'normal' and will freeze your UI as you noticed in your own code.
A simple answer is you cannot use while loop in GUI design.
But you can use the method .after(delay, callback=None) instead.
Here is an example:
from tkinter import *
root = Tk()
def loop():
print("Hi!")
root.after(1000, loop) # 1000 is equal to 1 second.
root.after(1000, loop) # This line is to call loop() in 1 second.
root.mainloop()
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()
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.