GUI button got stuck - python

I created a GUI using Tkinter. It has three buttons. one to capture image. second for capturing continuous images until i press third button i.e stop button.
When I press the first button it works fine i.e it runs a function which captures an image and save that image to a particular location.
But when I press the second button for continuous capturing of images then it starts capturing images but button gets stuck or GUI stopped working. because of this I am not able to stop this continuous capturing of images because I cannot press the third button i.e stop button.
I am running a while loop for continuous capturing of images and I can only break this loop if a Global variable STOP will have "0" in it and I can only make it zero by pressing stop button of GUI. But I cannot press stop button to make this STOP variable "0" so that loop can break. I think while loop is stopping the GUI main-loop and creating this problem. if you some have alternative or solution please share it.this is the code
Second button just call this function
def capture_video():
stop = '1'
l=Lepton()
l.enter()
path="/home/ubuntu/Desktop/IR_videos/vid_"
file=open("/home/ubuntu/Desktop/IR_videos/vid_no.txt",'r')
no=file.read()
file.close()
folder_name=path+no
os.mkdir(folder_name)
i=0
print("Image capturing started")
print("____Press Stop button to stop____")
while True:
a=l.capture()
cv2.normalize(a,a,0,65535,cv2.NORM_MINMAX)
np.right_shift(a,8,a)
img=np.uint8(a)
img_name=folder_name+"/"+str(i)+".jpg"
cv2.imwrite(img_name,img)
i=i+1
if stop == "0":
print("Image capturing stoped\n")
print("Press video button to capture again")
break
no=int(no)+1
file=open("/home/ubuntu/Desktop/IR_videos/vid_no.txt",'w')
file.write(str(no))
file.close()
cv2.destroyAllWindows()
l.exit()
here lepton is a class and capture() is function to capture image from flir lepton camera
and this is code of GUI stop button function :
def stop_it():
lep.stop='0'
time.sleep(1)
lep.stop='1'

You should run capture_video() on another thread than the main so that the main does not freeze. This way, you will be able to click on your Stop button. Also, make sure your stop variable inside capture_video is accessible by your stop_it function. In your capture_video, your stop variable is local and thus cannot be changed.
You can find an excellent answer on how to run a function on another thread here: https://stackoverflow.com/a/14331755/9729313
Here another example with threading and Tkinter:
https://stackoverflow.com/a/50175727/9729313

Related

A function is running despite me changing a variable that should stop it

I was hoping someone could help me with this issue. I'm hoping it's fairly simple to fix, but I have been trying for a while to figure this out. I have trimmed my larger code to this, as I believe the issue here is the crux of the problem.
I have a raspberry pi and an external button. This is on python3 on Linux. I am using GPIOZero for the button. The code below I believe is simple to understand, but basically, I want a function to loop at all times. When I press a button I want another function to run, but only if a variable is a certain number. I describe what I ultimately want to happen in a comment below, but my code is unfinished and simplified just for this problem.
I only want button.when_pressed to work when timer = 0. The problem is, once the code naturally gets into the button.when_pressed function, it never "lets go" of the function again. When I successfully redefine the variable to timer = 1, it still prints button is pressed when I press the button. I don't know why. To me, it seems like it should only work once timer = 0.
Any suggestions? I think I have a misunderstanding of global variables I will plan on researching. I'm not sure if that's the issue. I have also tried using break and continue to try to get it "back on its loop" but that hasn't worked either. Also I want to use button.when_pressed instead of btn.is_pressed, because I want the program to only do something when I'm holding the button down once, and not loop when I'm holding it down. In this case, I want button is pressed to print a single time. If I did btn.is_pressed it would print button is pressed every two seconds, which I dont want.
Thanks for any help. I'm happy to learn.
#!/usr/bin/python3
from gpiozero import Button
from time import sleep
import time
button = Button(4)
timer = 0
def press():
print("Button is pressed")
global timer
timer = 1
def loop():
global timer
timer = 1
while True:
if timer == 0:
button.when_pressed = press
else:
loop()
sleep(2)
If you want to disable the callback you had set to button.when_pressed, you need to do another assignment with button.when_pressed = None. This is listed in the documentation:
when_released:
[...] Set this property to None (the default) to disable the event.
It's not exactly clear what behavior you want from your current code. If you want the button to be active for 2 seconds, then be deactivated indefinitely, you can use:
button.when_pressed = press
sleep(2)
button.when_pressed = None
There's no need for a loop, since you don't want to repeat anything.
If you only want the button to be active for a single button press, that needs to happen within 2 seconds, you could instead call button.wait_for_press(2). I hesitate to write a full block of code for that though, as the docs don't specify how a timeout is signaled (it might be by return value, or via an exception). I don't have a Raspberry Pi so I can't test myself, but you could try it out and see what happens.
Treat your whole piece of code as one "black box", ask yourself, what is the input/output? button press or timer mode? (because I don't quite understand what does timer variable mean in your code)
Your code implies timer mode is the top level input to control the flow,
while True:
if timer == 0:
button.when_pressed = press
else:
loop()
sleep(2)
Is it expected?
If you allow user to press the button at any time, suggest you make button press to be your top level input, change the logic to keep when_pressed callback always on, set flag once triggered, and then check if the button has been pressed and still is_pressed in your while loop.
pressed = False
def play_video_1():
pass
def play_video_2():
pass
def press():
print("Button is pressed")
global pressed
pressed = True
button.when_pressed = press
while True:
if pressed and not_playing_video2:
if is_pressed:
play_video_1()
else:
pressed = False
play_video_2()
else:
play_video_2()

Is there a way to use cv2.waitkey() without showing the window?

I followed this program tutorial that captures the screen and puts it in a video file. The recording stops when the 'q' button is pressed. However, I didn't want to show the screen in a mini window and just write straight to the file. I am just calling the while loop shown in the tutorial except that I didn't include the part about showing the image to the 'Live' window. Now when I use:
while True:
img = pyautogui.screenshot()
frame = np.array(img)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
out.write(np.array(frame))
# cv2.imshow('Live', frame)
if cv2.waitKey(1) == ord('q'):
print('done')
break
it won't detect my pressing q. What is going on here? When I press q the loop should stop right? I am just getting a 44 bit mp4 file as an end result from this. I think it might be because I didn't use imshow so waitkey won't do anything but I am not sure. Is there a way to get it to stop recording on a key press?
It's very likely that waitKey() doesn't do anything unless there's a window, since it's likely related to a window key event handler.
If you're on Windows, you could try msvcrt.kbhit(), which is a non-blocking call to request whether a key has been pressed on the console and could be read.
On other platforms, waiting for a keypress in a non-blocking manner might be non-trivial.
I think, you are right in assuming that since you didn't start the display(ie., cv2.imshow()) the cv2.watikey() doesn't work. Since your goal is to stop recording on key press I think you can follow this link's suggestion.
or try this pattern:
try:
while True:
break
#replace break with your code
except KeyboardInterrupt:
print("Press Ctrl-C to terminate while statement")
pass

How to show the current window in Tkinter?

Hope you can help me with the following issue.
I'm using the library tkinter and
I have a main_screen main_screen= Tk() which is looping with mainloop()
Inside of the main_screen, there is a little data form where you need to type some necessary data.
And at the end of the window, I have a button to open a second window.
Once I click in that button, report_screen appears (See below in my code)
The new window should appear with the command Toplevel() and print a label that says Starting:
request_start = LabelFrame(report_screen, text="Starting...").pack()
then my program must run a process where it takes around 10 seconds to complete it.
Let's suppose that my process is just this
time.sleep(10)
And finally, run the next line:
request_done = LabelFrame(report_screen, text="Done").pack()
What is my problem?
The problem is that report_screen doesnt appear until the process of 10 sec has finished, and appears with both labels "Starting..." and "Done" at the same time.
I don't want that, I require that report_screen appears with the label "Starting", and then run the process, and when the process finished, add the "Done" label
This is the part of my code where I have this issue
report_screen = Toplevel()
request_start = Label(report_screen, text="Starting...").pack()
time.sleep(10) #example of my process that takes around 10 seconds
request_done = Label(report_screen, text="Done").pack()
Using update() method works to update whatever is in the screen

I get errors when I click the turtle game exit (X) button

Gets error when I click the exit (X) button:
Failed to execute script pong
How can I close the turtle game by clicking the exit button with no error?
Getting errors from turtle when you close the window is highly indicative of not playing by the rules event-wise. Typically it's due to a while True: loop instead of using timer events. A properly event-drive turtle program should reach the mainloop statement (or one of its equivalents) and allow events to run the show. If you want specifics, provide your code as part of your question.
The error in the terminal must be due to the the while True: loop that most beginners like me use. Closing the window abruptly stops the process while the while True: loop was still going on. So, in order to avoid this error you can define a function
#Function
def quit():
global running
running = False
#Keybinding
screen.onkeypress(quit, "q")
#Main game loop
while running:
...
#update events
...

appJar python: button press action

I am trying to get a hand-on experience on appJar with python3 for GUI programming. I am trying to add an action to take place when button is pressed. it works OK but it ends strangely.
Part of the code is below. the function "pressed" is getting called whenever the button is pressed. it works fine. However, when the count reaches 0, the number 0 is not pressed (although, as per the code, the number 0 should be printed on the label named "lb1").
when the button is pressed and the count variable =1, the count variable should get decremented by one, then the label text should be updated with the new count number. then, it checks if the counter =0 and if true, it exits the code.
Now, the form get terminated before updating the label with the new value. with some troubleshooting, I found the label value is only updated upon the termination of the form although the line of code that updates it is executed already.
Anyone can shed some light on this?
from appJar import gui
count=10
def pressed(btnName):
global count
count-=1
win.setLabel("lb1","Count= "+ str(count))
if count==0:
win.stop()
I think what you're seeing is expected behaviour.
When count reaches 0 - the label update is queued to happen, but then the next line of code stops the GUI, this will happen just a few milliseconds later - so the GUI will be gone before there is a chance to update what is being shown on the screen.
If you want to introduce a slight delay, between the label updating and the GUI closing, you could use the .after() function.
For example:
if count == 0:
win.after(500, win.stop)
This will delay 500 milliseconds before calling the stop function.
Alternatively, if you want the GUI to stay open, displaying Count= 0, and only close the next time the user presses the button, change your original code to only call win.stop() when count == -1
So, the full code might look like:
from appJar import gui
count=10
def pressed(btnName):
global count
count-=1
win.setLabel("lb1","Count= "+ str(count))
if count == 0:
win.after(500, win.stop)
win = gui()
win.addLabel("lb1", "empty")
win.addButton("PRESS", pressed)
win.go()

Categories