I am trying to allow a user to input multiple answers but only within an allocated amount of time. The problem is I have it running but the program will not interrupt the input. The program will only stop the user from inputing if the user inputs an answer after the time ends. Any ideas? Is what I am trying to do even possible in python?
I have tried using threading and the signal module however they both result in the same issue.
Using Signal:
import signal
def handler(signum, frame):
raise Exception
def answer_loop():
score = 0
while True:
answer = input("Please input your answer")
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
try:
answer_loop()
except Exception:
print("end")
signal.alarm(0)
Using Threading:
from threading import Timer
def end():
print("Time is up")
def answer_loop():
score = 0
while True:
answer = input("Please input your answer")
time_limit = 5
t = Timer(time_limit, end)
t.start()
answer_loop()
t.cancel()
Your problem is that builtin input does not have a timeout parameter and, AFAIK, threads cannot be terminated by other threads. I suggest instead that you use a GUI with events to finely control user interaction. Here is a bare bones tkinter example.
import tkinter as tk
root = tk.Tk()
label = tk.Label(root, text='answer')
entry = tk.Entry(root)
label.pack()
entry.pack()
def timesup():
ans = entry.get()
entry.destroy()
label['text'] = f"Time is up. You answered {ans}"
root.after(5000, timesup)
root.mainloop()
Related
I am trying to show an animated "working" prompt while running some python code. I've been searching for a way to do so but the solutions i've found are not quite what i want (if i recall correctly tqdm and alive-progress require a for loop with a defined number of iterations) and I'd like to find a way to code it myself.
The closest I've gotten to making it is using asyncio as follows:
async def main():
dummy_task = asyncio.create_task(dummy_search())
bar_task = asyncio.create_task(progress())
test = await dummy_task
bar_task.cancel()
where dummy task can be any async task and bar_task is:
FLUSH_LINE = "\033[K"
async def progress(mode=""):
def integers():
n = 0
while True:
yield n
n += 1
progress_indicator = ["-", "\\", "|", "/"]
message = "working"
message_len = len(message)
message += "-" * message_len
try:
if not mode:
for i in (n for n in integers()):
await asyncio.sleep(0.05)
message = message[1:] + message[0]
print(f"{FLUSH_LINE}{progress_indicator[i % 4]} [{message[:message_len]}]", end="\r")
finally:
print(f"{FLUSH_LINE}")
The only problem with this approach is that asyncio does not actually run tasks in parallel, so if the dummy_task does not use await at any point, the bar_task will not run until the dummy task is complete, and it won't show the working prompt in the terminal.
How should I go about trying to run both tasks in parallel? Do I need to use multiprocessing? If so, would both tasks write to the same terminal by default?
Thanks in advance.
I am using an image processing code in python opencv. Since that process is taking a lot of time to process say 30 images. I tried to process these image parallel using Multiprocessing. The multiprocessing part is working good in CPU but I want to use that multiprocessing thing in GPU(cuda).
I use torch.multiprocessing for running task in parallel. So I am using torch.device('cuda') for our class to run whole thing in to this perticular device. When I run the code it's showing device using "cuda" but not using any GPU processing.
import cv2
import numpy as np
import torch
import torch.nn as nn
from torch.multiprocessing import Process, Pool, Manager, set_start_method
import sys
import os
class RoadShoulderWidth(nn.Module):
def __init__(self):
super(RoadShoulderWidth, self).__init__()
pass
// Want to run below method in parallel for 30 images.
#staticmethod
def get_dim(image, road_shoulder_width_list):
..... code
def get_road_shoulder_width(self, _root_dir, _img_path_list):
manager = Manager()
road_shoulder_width_list = manager.list()
processes = []
for img_path in img_path_list[:30]:
img = cv2.imread(_root_dir + '/' + img_path)
img = img[72 * 5:72 * 6, 0:1280]
# Do work
p = Process(target=self.get_dim,args=(img,road_shoulder_width_list))
p.start()
processes.append(p)
for p in processes:
p.join()
return road_shoulder_width_list
Use below set of code to run your class
if __name__ == '__main__':
root_dir = '/home/nikhil_m/r'
img_path_list = os.listdir(root_dir)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print('Using device:', device)
dataloader_kwargs = {'pin_memory': True}
set_start_method('fork')
obj = RoadShoulderWidth().to(device)
val = obj.get_road_shoulder_width(str(root_dir), img_path_list)
print(val)
print(torch.cuda.is_available())
Can anybody suggest me how to fix this?
Your class RoadShoulderWidth is a nn.Module subclass which lets you use .to(device). This only means that all other nn.Module objects or nn.Parameters that are members of your RoadShoulderWidth object are moved to the device. As from your example, there are none, so nothing happens.
In general PyTorch does not move code to GPU but data. If all data of a pytorch operation are on the GPU (e.g. a + b, a and b are on GPU) then the operation is executed on the GPU. You can move the data with a.to(device), given a is a torch.Tensor object.
PyTorch can only execute its own operations on GPU. It's not able to execute OpenCV code on GPU.
I would like a label to be .grid() then the program to wait 3 seconds and then .grid_forget(). I am very confused at the point .grid is executed. For example:
def remove_choice(self):
while True:
try:
get = int(self.entry_remove_choice.get())
except ValueError:
self.label_error_remove.grid(row=10,column=6) #A
time.sleep(3)
self.label_error_remove.grid_forget() #B
#Empty entry box
break
else:
#continue code
break
Once the button is pressed and remove_choice is executed, the button is displayed to be pressed in for three seconds then #A and #B are executed in one go and nothing is displayed.
If #B is removed then the error message is displayed after three seconds.
If #A and #B are swapped for print to terminal then program works how you would think, with one message, a wait of three seconds, then another message.
If you do a very sloppy solution (which Im not that bothered about for this program) and do this:
def remove_choice(self):
while True:
try:
get = int(self.entry_remove_choice.get())
except ValueError:
self.label_error_remove.grid(row=10,column=6) #A
for n in range (1,1000):
print("abc")
self.label_error_remove.grid_forget()
break
else:
#continue code
break
When executed "abc" is printed 1000 times taking around 1.5 seconds and then after this the program displays the grid.
Any suggestions to how to make TKinter wait please.
Also can someone explain why grid works like this, thanks.
Rather than trying 'forgetting' the label each time, why not just clear the error message text?
My example below will wait for the user to press the button and display the error message for 3 seconds. I'm using the .after method to schedule the hideError method 3 seconds (3000 ms) after the error message is displayed.
try:
import tkinter as tk
except:
import Tkinter as tk
import time
class App(tk.Frame):
def __init__(self,master=None,**kw):
tk.Frame.__init__(self,master=master,**kw)
self.errorMessage = tk.StringVar()
self.ErrorLabel = tk.Label(textvar=self.errorMessage)
self.ErrorLabel.grid()
self.button = tk.Button(text="Press Me",command=self.showError)
self.button.grid()
def showError(self):
# Disable the button and show the error message
self.button['state'] = tk.DISABLED
self.errorMessage.set("Error Message!!!!")
self.after(3000,self.hideError)
def hideError(self):
#Enable the button and clear the error message.
self.button['state'] = tk.NORMAL
self.errorMessage.set("")
if __name__ == '__main__':
root = tk.Tk()
App(root).grid()
root.mainloop()
It is considered bad practice to use while True loops or time.sleep inside GUI applications. They prevent the GUI from updating so in your code both actions appear to happen at the same time because the time.sleep operation is blocking the GUI and preventing the screen from being redrawn.
EDIT: Passing arguments from callbacks.
Current problem is that the after method expects to receive a reference to a function. self.hideError(3) returns NoneType not reference to a function call. We can solve this using anonymous functions and lambda.
I've started to use this snippet of code to help, its from guizero
def with_args( func_name, *args):
"""Helper function to make lambda functions easier
Thanks to guizero"""
return lambda: func_name(*args)
Then in your main section of code the line would look like this.
self.after(3000,with_args(self.hideError,3))
EDIT: There is an even simpler way. The .after method can take arguments itself.
self.after(3000,self.hideError,3)
I am currently writing a wrapper for a small console program I wrote.
The c program needs a password string as input and because I intend to use it through dmenu and such, I'd like to use a little gtk entry box to enter that string.
However, I have to fork after I get the input (because I'm also handling clipboard stuff which needs deletion after some time) and the window simply won't close until the child process exits.
from subprocess import Popen, PIPE
from gi.repository import Gtk
import sys
import os
import time
import getpass
HELP_MSG = "foobar [options] <profile>"
class EntryDialog(Gtk.Dialog):
def run(self):
result = super(EntryDialog, self).run()
if result == Gtk.ResponseType.OK:
text = self.entry.get_text()
else:
text = None
return text
def __init__(self):
super(EntryDialog, self).__init__()
entry = Gtk.Entry()
entry.set_visibility(False)
entry.connect("activate",
lambda ent, dlg, resp:
dlg.response(resp),
self,
Gtk.ResponseType.OK)
self.vbox.pack_end(entry, True, True, 0)
self.vbox.show_all()
self.entry = entry
def get_pwd():
if sys.stdin.isatty():
return getpass.getpass()
else:
prompt = EntryDialog()
prompt.connect("destroy", Gtk.main_quit)
passwd = prompt.run()
prompt.destroy()
return passwd
The thought is, that it should close when I hit enter, but I'm pretty sure I'm doing something entirely wrong.
The script basically continues like this:
profile = argv[0]
pwd = get_pwd()
if pwd is None:
print(HELP_MSG)
sys.exit()
out = doStuff()
text_to_clipboard(out)
# now fork and sleep!
if os.fork():
sys.exit()
time.sleep(10)
clear_clipboard()
sys.exit(0)
I dropped the python wrapper and wrote it directly in c. However, for anyone having the same problem, the (untested) solution would be to add a function
def quit():
self.emit("destroy")
where 'self' is the dialog box - and connect that to the "activate" signal,
entry.connect("activate", quit)
so that the dialog widget emits the destroy signal as soon as the user hits Return and thus Gtk.main_quit gets called.
In c the content can be extracted nicely by specifying a GtkEntryBuffer and calling it's
gtk_entry_buffer_get_text()
I didn't find it right now, but there is probably an equivalent for pygtk available.
I have a simple GUI which run various scripts from another python file, everything works fine until the GUI is running a function which includes a while loop, at which point the GUI seems to crash and become in-active. Does anybody have any ideas as to how this can be overcome, as I believe this is something to do with the GUI being updated,Thanks. Below is a simplified version of my GUI.
GUI
#!/usr/bin/env python
# Python 3
from tkinter import *
from tkinter import ttk
from Entry import ConstrainedEntry
import tkinter.messagebox
import functions
AlarmCode = "2222"
root = Tk()
root.title("Simple Interface")
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
ttk.Button(mainframe, width=12,text="ButtonTest",
command=lambda: functions.test()).grid(
column=5, row=5, sticky=SE)
for child in mainframe.winfo_children():
child.grid_configure(padx=5, pady=5)
root.mainloop()
functions
def test():
period = 0
while True:
if (period) <=100:
time.sleep(1)
period +=1
print(period)
else:
print("100 seconds has passed")
break
What will happen in the above is that when the loop is running the application will crash. If I insert a break in the else statement after the period has elapsed, everything will work fine. I want users to be able to click when in loops as this GUI will run a number of different functions.
Don't use time.sleep in the same thread than your Tkinter code: it freezes the GUI until the execution of test is finished. To avoid this, you should use after widget method:
# GUI
ttk.Button(mainframe, width=12,text="ButtonTest",
command=lambda: functions.test(root))
.grid(column=5, row=5, sticky=SE)
# functions
def test(root, period=0):
if period <= 100:
period += 1
print(period)
root.after(1000, lambda: test(root, period))
else:
print("100 seconds has passed")
Update:
In your comment you also add that your code won't use time.sleep, so your original example may not be the most appropiate. In that case, you can create a new thread to run your intensive code.
Note that I posted the alternative of after first because multithreading should be used only if it is completely necessary - it adds overhead to your applicacion, as well as more difficulties to debug your code.
from threading import Thread
ttk.Button(mainframe, width=12,text="ButtonTest",
command=lambda: Thread(target=functions.test).start())
.grid(column=5, row=5, sticky=SE)
# functions
def test():
for x in range(100):
time.sleep(1) # Simulate intense task (not real code!)
print(x)
print("100 seconds has passed")