python appjar function doesn't thread - python

having issues trying to get threading working in python using the awesome Appjar package.
The following program needs to count through a list, and update a progress bar simultaneously. I've followed the appjar documentation for threading, but it's returning NameError: name 'percent_complete' is not defined in the app.thread (line 35), in which you're meant to insert function params - my code is below:
from appJar import gui
import time
# define method the counts through a list of numbers, and updates the progress meter
def press(btn):
objects = [1,3,6]
total = len(objects)
current_object = 0
for i in objects:
print(i)
current_object += 1
current_percent_complete = (current_object / total) * 100
updateMeter(current_percent_complete)
time.sleep(1)
def updateMeter(percent_complete):
app.queueFunction(app.setMeter, "progress", percent_complete)
# create a GUI variable called app
app = gui("Login Window")
app.setBg("orange")
app.setFont(18)
# add GUI elements : a label, a meter, & a button
app.addLabel("title", "COUNTER")
app.setLabelBg("title", "blue")
app.setLabelFg("title", "orange")
app.addMeter("progress")
app.setMeterFill("progress", "green")
app.addButton("START COUNTING", press)
# put the updateMeter function in its own thread
app.thread(updateMeter, percent_complete)
# start the GUI
app.go()
I can get rid of the error by defining percent_complete like so:
from appJar import gui
import time
# define method the counts through a list of numbers, and updates the progress meter
percent_complete = 0
def press(btn):
...
However, when GUI loads and button is pressed it doesn't thread. Instead it iterates through the list, then updates the progress bar afterwards.
Has anyone come across the same issue? any insight would be awesomely appreciated!
Thanks!

There are a couple of issues here:
First, I'm not sure your maths result in good percentages to update the meter with, so you might not see much change - should you be using i?
Second, the GUI won't be updated until the loop (and the sleeps inside it) all complete. Instead, you should try counting how many items to process, and iterating through them with an after() function, see here: http://appjar.info/pythonLoopsAndSleeps/#conditional-loops
Third, the call to app.thread() at the end doesn't achieve much - it calls the update_meter() function with a parameter that doesn't exist, it can be removed.
Fourth, the actual update_meter() function isn't necessary, as you're not really using a thread - that can be removed as well...
Give this a try, once you've had a look at the maths:
current_object = 0
def press(btn):
global current_object
current_object = 0
processList()
def processList():
global current_object
objects = [1,3,6]
total = len(objects)
if current_object < total:
i = objects[current_object]
print(i)
current_object += 1
current_percent_complete = (current_object / total) * 100
app.setMeter("progress", current_percent_complete)
app.after(1000, processList)
UPDATE: just to clarify on the maths issue, you're dividing one integer by another: 0/3, 1/3, 2/3, 3/3 and so on. In python2 this will result in 0, in python3 you'll get fractions.

Related

How can i make this code do what it's supposed to do?

So, what I am trying to do is when you open the window, it starts a process in which every 0.2 seconds it changes the first and 3rd value of the color (in which it converts the elements of the range into a hex value and then a string) to go from rgb( 86, 32, 86) to rgb(126, 32, 126). Although I thought this might just work, it doesn't. I only get a background of the first color and that's all.
from tkinter import *
import time
root = Tk()
for i in range(86,126):
h = hex(i)
h = str(h)
h = h[2] + h[3]
root.configure(background=("#" + h + "32" + h ))
time.sleep(0.2)
root.mainloop()
You must use the after function to give the window system time to process updates. Calling window update functions in a loop like that on the main thread will lock up the window until the loop terminates.
Try moving the code in the for loop into a new function, say updateBackground, and making it call itself recursively using after:
def updateBackground(i):
# ...
if i < 126:
root.after(200, lambda: updateBackground(i + 1))
Note that I used a lambda in order to increment i.
Credit: https://stackoverflow.com/a/36670519/1757964
Your main issue with this code is the use of sleep(). Because Tkinter is a single thread application and is event driven what ends up happening when you use sleep() the entire Tkinter instance freezes.
To work around this Tkinter provides a method called After() that is designed to schedule an event to happen after a time. So to get the same affect you are trying to get with sleep we can instead create a function that can call itself after 0.2 sec and provide it with the starting number and ending number.
from tkinter import *
root = Tk()
def do_something(start_number, end_number):
if start_number <= end_number:
h = str(hex(start_number))[2:] # combined all your work on h to one line
root.configure(background=("#{}32{}".format(h, h)))
start_number += 1
root.after(200, do_something, start_number, end_number)
do_something(86, 126)
root.mainloop()
Note that the color change is mild and hard to see. If you increase the `end_number thought it will become more obvious.

Count number of times function with varying arguments executed, then reset count when ENTER key pressed

I would like to be able to count the number of times a defined function that requires a varying argument is executed, then reset that counter to 0 every time the ENTER key is pressed. I want to be able to utilize this action with the defined function below:
def draw(x):
global drawing
q.put(x)
process = False
drawingLock.acquire()
if not drawing:
process = True
drawing = True
drawingLock.release()
if process:
if not q.empty():
x()
drawingLock.acquire()
drawing = False
drawingLock.release()
This function is a queue that takes function arguments and executes them one by one, and the arguments for this function are functions that draw letters in the turtle graphics window. Additionally, this function gets called by a keypress of a certain letter that corresponds to the letter drawn. For example:
onkey(lambda: draw(draw_A), "a")
# This draws an "A" in the Turtle Graphics window when "a" is pressed on the keyboard
# If multiple "A"s pressed, it draws them one by one
How would I count how many times this draw() function has been executed by the user in the turtle graphics window, and then reset it every time the ENTER key is pressed? I have tried creating a decorator for this, but the ones I have created does not return ANYTHING with turtle graphics, and does not seem to work with functions whose arguments can vary with those chosen by the user. Any help regarding this issue is greatly appreciated! :)
Functions allow attributes:
def draw(x):
if not hasattr(draw, 'counter'):
draw.counter = 0
draw.counter += 1
# body
Catch the ENTER from the user:
def handle_enter():
draw.counter = 0
onkey(handle_enter, 'Enter')
Find out how many times it has been called:
print('`draw` has been called {} times'.format(draw.counter))

Creating Random Events Pygame

I'm working on a small game in Python called "Blank Screen Simulator" (but it won't just be a black screen, I want to have events. Like a time where random images pop up, but for now I only have a time where you click the mouse alot and a time where you press buttons alot) anyway, I need to make these random. Here's my system I tried:
import pygame
from pygame.locals import *
import time
import random
pygame.init()
width, height = 640, 480
screen=pygame.display.set_mode((width, height))
Event1 == False
Event2 == False
screen_rect=screen.get_rect()
player=pygame.Rect(180, 180, 20, 20)
mouseClickNumber = 0
keyPressNumber = 0
a = random.choice(Event1, Event2)
a = True
def Event1:
if pygame.mouse.get_pressed(button 1):
mouseClickNumber = mouseClickNumber + 1
time.sleep(20)
clickHappyFunTime = False
print mouseClickNumber
mouseClickNumber = 0
def Event2:
if pygame.key.get_pressed:
keyPressNumber = keyPressNumber + 1
time.sleep(20)
buttonPressTime = False
print keyPressNumber
keyPressNumber = 0
My system involves having two events as False, then having a random.choice pick between the two variables, Event1 and Event2, but then when I run it, def Event2: is an invalid syntax. Am I just doing something wrong?
Function definitions must contain a parameter list, even if it's empty. Like this:
def Event1():
However, you've got a lot of other problems here.
Event1 == False doesn't assign False to Event1, it just compares Event1 to False—which is going to raise NameError because you don't have anything named Event1 yet, but even if you did, this wouldn't do anything useful, especially since you ignore the result.
If you fix that, a = random.choice(Event1, Event2) is going to fail because choice takes a single argument, a sequence to choose from, not a separate argument for each choice; you wanted a = random.choice([Event1, Event2]).
If you fix that, it's going to choose between False and False. Maybe you wanted it to choose between the two functions you're going to define later? But you can't choose between them before you define them. Defining them to some different value earlier doesn't help; it's just choosing the earlier value. (Also, reusing the same name for a normal value and a function is very confusing.)
If you fix that, it doesn't matter anyway, because a = True is just going to replace the value you chose.
You never call the functions directly, or attach them to events so they'll be called indirectly by PyGame, so they aren't doing much good.
The functions will raise an UnboundLocalError if you call them, because assigning to mouseClickNumber inside a function makes it a local variable, hiding the global variable of that name. To avoid that, you need to add global mouseClickNumber.
time.sleep inside a GUI application freezes the entire GUI, preventing it from updating the screen.

self." ". after() does not work properly?

I am trying to make a timer to execute a block of code every second let's say, using tkinter in python. But instead of executing the code every second, which is moving a label across a canvas, it seems to buffer and wait until the loop is finished and only then display the moved label. Beneath is the piece of coding where I think the problem is found. I personally think the for-loop in the second function is creating problems, but I don't know how to solve this.
def roll(self):
number=randint(2,12)
print number
if self.a==0:
self.place_player_1(self.start_turn_pos_1,number+self.start_turn_pos_1)
self.start_turn_pos_1+=number
elif self.a==1:
self.place_player_2(self.start_turn_pos_2,number+self.start_turn_pos_2)
self.start_turn_pos_2+=number
return number
def place_player_1(self,start_turn_pos_1,number):
#Define the board
for i in range(self.start_turn_pos_1,number+1,1):
self.c.after(1000,self.move_1(i))
def move_1(self,i):
e1=streets_x[i]
g1=streets_y[i]
self.label_player1.place(x=e1,y=g1)
self.move_1(i) calls the method immediately. To postpone the call:
self.c.after(1000, self.move_1, i) #note: no parentheses
To repeat the call every second, add .after call at the end of self.move_1 method:
def place_player_1(self,start_turn_pos_1,number):
self.c.after(1000, self.move_1, start_turn_pos_1, number) # call in a sec
def move_1(self,i, limit):
e1=streets_x[i]
g1=streets_y[i]
self.label_player1.place(x=e1,y=g1)
if i < limit: # schedule the next call
self.c.after(1000, self.move_1, i + 1, limit)
See setTimeout(), setInterval() analogs in Python using tkinter, or gtk, or twisted.
All function calls happen at the same time:
self.c.after(1000,self.move_1(i))
Because the are called after 1000 milliseconds.
Make the delay larger for each step. For example:
def place_player_1(self,start_turn_pos_1,number):
#Define the board
delay = 1000
for index, i in enumerate(range(self.start_turn_pos_1, number + 1), 1):
self.c.after(delay * index, self.move_1, i)
Now you schedule the function calls for different times.

Running a loop alongside a Tkinter application

I am a high school programming student and I have a small question. I have been tasked with writing a simple game in Tkinter where an icicle falls from the ceiling and you have to avoid it with your mouse. Simple enough. However, I have hit an issue. Whenever I run a loop in a Tkinter application, it won't open. I've tried with a for loop that pauses every .5 seconds using time.sleep() and the window opens as soon as the loop finishes. Is there some special thing I need to do to make loops work in Tkinter?
from Tkinter import *
import time
import random
class App:
def __init__(self, parent):
self.frame = Frame(root, bg= '#1987DF', width=800, height=800)
self.frame.bind("<Motion>", self.motionevent)
self.frame.pack()
#self.run()
def randhex(self):
b = "#"
for i in range(1, 7):
a = random.randint(0, 15)
if a == 10:
a = "A"
elif a == 11:
a = "B"
elif a == 12:
a = "C"
elif a == 13:
a = "D"
elif a == 14:
a = "E"
elif a == 15:
a = "F"
b = b+str(a)
return b
def motionevent(self, event):
xpos, ypos, bg = event.x, event.y, self.randhex()
str1 = "X : %d Y : %d BG : %s" % (xpos, ypos, bg)
root.title(str1)
x,y, delta = 100, 100, 10
self.frame.config(bg=bg)
def run(self):
for i in range(0, 10):
time.sleep(.5)
print 'i'
self.frame.config(bg=self.randhex())
root = Tk()
app = App(root)
root.mainloop()
Currently all it is supposed to do is change the background when the mouse moves. When the line in init that says self.run() is uncommented it will print 'i' 10 times then the window will open. Help?
Writing an event based program is not the same as writing traditional programs. There is already an infinite loop running, and like any infinite loop, if you place another loop inside, the outer loop can't continue until the inner loop finishes. Since the outer loop is what causes the screen to refresh and events to be processed, inner loops effectively freeze your app until they are done.
Since there is already a loop running you don't need to create another loop. All you need to do is add little jobs to the event queue one at a time. Each job is, in effect, one iteration of your inner loop.
For example, if you wanted to write an inner loop like this:
for i in range(10):
print "i:", i
... you would instead add an event to the event queue and each time the event loop iterates (or more precisely, each time it finishes processing any other events) it will do one iteration of your loop. You do it like this:
def do_one_iteration(i):
print "i:", i
if i < 9:
root.after_idle(do_one_iteration, i+1)
Then, after the first time you call do_one_iteration, it will place the next iteration of itself on the event queue, and continue to do so until it decides it is done.
Typically you would call do_one_iteration when the user presses a button (eg: the "start" button). Call it once, then it does one bit of work (ie: moving the icicle down a couple of pixels) and then reschedules itself.
In game development you might have a function called update_display which is in charge of redrawing everything. It could, for example, subtract 1 from the Y coordinate of each icicle. You can add bindings for the left and right arrows to move the player (by incrementing or decrementing the X coordinate), and your update function would use these new coordinates to redraw the player.
By the way, you can slow down your program by using after instead of after_idle to call the function after a slight delay. This delay can be used to control the frame rate. For example, assuming the update is nearly instantaneous (and it probably will be in your case), calling after with an argument of 41 (milliseconds) yields a framerate of approximately 24 fps (24 frames times 41 milliseconds equals 984 milliseconds, or roughly 24 frames per second)
It may sound complicated but it's really pretty easy in practice once you do it once or twice.

Categories