How to handle wxPython TextCtrls that influence each other's content - python

I am trying to build a GUI in wxPython. I have two TextCtrls. Let's say they are first_index_input and last_index_input, and they control model.f_i and model.l_i, respectively. There's a condition: For proper model execution, model.f_i must be <= model.l_i. Therefore the content of first_index_input should be <= last_index_input.
So what I want to do is fix it so that: 1) if the user enters a value into first_index_input that is greater than the value in last_index_input (established in a prior use case or something) then last_index_input and model.l_i will both be set equal to the new first_index_input. 2) If the user enters a last_index_input that is less than first_index_input, then last_index_input will be corrected again and set equal to first_index_input.
So what's so hard about that? It's a question of what event to key off of. Let's say that first_index_input has content "145" and I (correctly) want to give last_index_input an input of 10324. If this widget keys off of a wx.EVT_TEXT event, then it won't wait for me to type in "10324". As soon as it sees the "1" (i.e. first digit) it freaks out and says, "NO! That's less than first_index_input" and "corrects" my typing. On the other hand, I can get rid of this by keying off of a wx.EVT_TEXT_ENTER command, and everything works fine as long as I remember to hit return after entering my value and I never remember to hit return so I am sure my users won't either.
Is there another way to do this? Some other event that is available, perhaps? Thanks.

I would use EVT_TEXT together with a wx.Timer. The idea here is to monitor how long it's been since the user last typed something in that second control. So you'll have a start value of zero (self.start_value) and each time EVT_TEXT fires, you'll reset that value and restart the timer. When the EVT_TIMER event gets fired, it will increment the start value. You'll have to put an "if" statement in timer's event handler that checks if the start value is greater than X seconds. If it is, then do the check to see if the first input if less then the second and act accordingly. Make sure you stop the timer if it is as a running timer object can hang the app when you go to close it.
Here's a link on timers: http://www.blog.pythonlibrary.org/2009/08/25/wxpython-using-wx-timers/

Related

Countdown Timer that can be extended by user input?

I am trying to make a little game.
I need a timer, that starts counting down in the background when the game begins (it shows the lifespan of a bonfire).
There is no need to print the remaining duration (fire_time) every second, but the user should be able to input a command (like fire) to get the remaining seconds.
The user should also be able to input words that add different amounts of seconds to the timer (like "paper", "wood", etc.)
I've tried to create a timer in a thread that is counting down with the following code:
for seconds in range(fire_time, 0, -1):
fire_timer = Timer(0, fire_time_calc, [fire_time])
fire_timer.start()
#print(f"'Fire_time' aus for-loop = {fire_time}")
time.sleep(1)
fire_time -= 1
and I've got a function that is called, that prints the time (fire_time_calc).
Currently the function is only to show how the countdown is proceeding, but I don't know if I really need it.
Currently the timer is working fine because it counts down to zero.
The problem is, that no user input is allowed until it reaches zero.
The variable fire_time needs to be updated and accessible outside of the for-loop to add time to the countdown...
Anybody got an idea how to solve this?

Send a variable from outside the python console OR stop a script from outside

I wrote a Python script that executes an optimization and runs days to get a solution (due to the costly objective function). In all days work it will be sufficient to just stop the calculation at some point because the solution is good enough for me (but not for the optimization algorithm).
The problem is, I can always abort hitting Ctrl+C. But then there is no chance to nicely output the current best parameters, plot the data, save it etc. It would be great to stop the script in a controlled way after the next calculation of the objective function. So my thought was so question some variable (if user_stop=True) and programatically stop the optimization. But how to set such a variable? The python console is blocked during execution.
I thought about setting the content of a text file and reading it in each iteration but it's more than poor and hard to explain for other users of the script. Theoretically, I could also ask the user for an input but than the script won't run automatically (which it should until someone decides to stop).
Any ideas for my problem?
Basically that's it - stop the loop at some point but execute the print:
a = 0
while True:
a = a + 1
print(a)
If you poll your "variable" infrequently (say at most once every 20 seconds) then the overhead of testing for a file is negligible. Something like
import os
QUITFILE = "/home/myscript/quit_now.txt"
# and for convenience, delete any old QUITFILE that may exist at init time
... # days later
if os.path.isfile( QUITFILE)
# tidy up, delete QUITFILE, and exit
Then just echo please > home/myscript/quit_now.txt to tell your program to exit.
maybe you can use a do-while loop. holding your target in a varible
outside the loop and start looping the calculatio while <= your target calculation.
For Windows, I would use msvcrt.getch()
For example, this script will loop until a key is pressed, then, if it is q, prompt for the user to quit: (Note that the if statement uses 'short circuiting' to only evaluate the getch() - which is blocking - when we know that a key has been pressed.)
import msvcrt, time
while True: #This is your optimization loop
if msvcrt.kbhit() and msvcrt.getch() == 'q':
retval = raw_input('Quit? (Y/N) >')
if retval.lower() == 'y':
print 'Quitting'
break #Or set a flag...
else:
time.sleep(1)
print('Processing...')
If you place this if block at a point in the optimization loop where it will be frequently run, it will allow you to sop at a convenient point, or at least set a flag which you can check for at the end of each optimization run.
If you cannot place it somewhere where it will be frequently checked, then you can look at handling the KeyboardInterrupt raised by Ctrl-C
If you are running on Linux, or need cross-platform capability, have a look at this answer for getting the keypress.

How to use the button on a LCD instead of 'except keyboard interrupt'?

Im trying to get wifite (https://github.com/derv82/wifite) working on my 16x2 Adafruit LCD (http://www.adafruit.com/product/1110).
But for some Reason, if I press the specified button nothing happens.
I want to replace all pieces of code that look like this:
try
....
except KeyboardInterrupt:
....
With the code for the LCD Buttons:
try
....
except lcd.buttonPressed(lcd.SELECT):
....
But for some reason nothing happens if I press the button, I don't get a error - And wifite just keeps doing it's thin.
Any Idea why this isn't working how it should ?
Or is there maybe a better way ?
As others have pointed out except KeyboardInterrupt ... is a special construct in Python ... because a [Ctrl]-[C] is handled by your terminal driver and presents an "Interrupt" signal to your program (SIGINT under Unix, Linux and similar operating systems). (There is similar handling under Microsoft operating systems, with different details and slightly different terminology; but the Python code works the same either way.
There are other ways of accessing various other forms of hardware event ... and the Python exception module is not a typical way for those to be implemented.
In the case of the AdaFruit, or other Rasberry Pi devices, you'd use the modules they include with their package. You've already seen it, and presumably done the required import in your code (based on your reference to lcd.buttonPressed()). The problem is that this isn't how you use that function at all.
Read this carefully: https://blog.soton.ac.uk/pi/modules-available/adafruit-rgb-lcd-display/
... it includes example which show how you should be using it. That should be something like:
#!python
# Set up stuff here ...
got_event = False
while not got_event:
if lcd.buttonPressed(lcd.SELECT)
got_event = True
break
# Do other stuff, perhaps
# Or time.sleep(0.1)?
if got_event:
# In case we had other exit conditions from doing other stuff?
subprocess.call(YOUR_OTHER_PROGRAM)
Of course their code is a complete running program. I'm only highlighting a couple of points here. You need to loop around until you get the event your looking for (or loop around forever processing these events for as long as your device is up).
The lcd.buttonPressed() method is checking to see if the button has been pressed since the last time it was cleared; but the method/function doesn't block ... it returns True or False (or possibly None --- that wouldn't affect these code examples --- any "false" value means the button has not been pressed).
You want to sleep for some amount of time between checks. They use a half second delay in their example; I used a tenth of a second. (People will typically perceive a response within a tenth of a second from a computerized device as "instantaneous" while a half second delay will, typically, be slightly annoying). Checking as fast as you can will just needlessly heat up the electronics. Even a 0.01 (one hundredth of a second) sleep is sufficient ... but 0.05-0.1 are probably the best practice for something like this.
If I understand correctly, you want to have one of the buttons on the Adafruit LCD panel interrupt the program at almost any stage. (It would have been great if you'd mentioned this yourself!)
KeyboardInterrupt is a signal sent to a process, usually when a user presses Ctrl + c on a keyboard. To be more precise, a signal is sent by the OS and caught by the Python runtime, which raises a KeyboardInterrupt exception.
However, Ctrl + c is special! In almost any other case, when a user presses a key or a button, this is not translated into a special signal.
I'm not sure how you could get the behavior you want; this may depend quite a bit on the operating system you are using.
What you need is event detection try something like this. You might have to get familiar with Tkinter
from Tkinter import *
root = Tk()
def callback_end(event)
# do whatever ending procedure you want here
quit()
def main()
# do everything in your main code here
if lcd.buttonPressed(lcd.SELECT):
callback_end("<End>")
root.after(Period,main)
root.bind("<End>",callback_end) # if you press the end key it will call the callback_end function
root.after(Period,main) # repeats main every Period in miliseconds
root.mainloop()
I realize that this is not a complete answer but I hope it gets you going in the right direction

Python / LibTCOD - libtcod.console_wait_for_keypress(True) triggering twice for each input

I'm going through the Roguebasin python/libtcod roguelike tutorial. The problem I encounter is, every time key = libtcod.console_wait_for_keypress(True) is called, the main loop fires off not one, but two times. The code handling keyboard input is as follows:
def handle_keys():
#key = libtcod.console_check_for_keypress() #real-time
key = libtcod.console_wait_for_keypress(True) #turn-based
if key.vk == libtcod.KEY_ENTER and key.lalt:
#Alt+Enter: toggle fullscreen
libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
elif key.vk == libtcod.KEY_ESCAPE:
return 'exit' #exit game
if game_state == 'playing':
#movement keys
if libtcod.console_is_key_pressed(libtcod.KEY_UP):
player_move_or_attack(0, -1)
elif libtcod.console_is_key_pressed(libtcod.KEY_DOWN):
player_move_or_attack(0, 1)
elif libtcod.console_is_key_pressed(libtcod.KEY_LEFT):
player_move_or_attack(-1, 0)
elif libtcod.console_is_key_pressed(libtcod.KEY_RIGHT):
player_move_or_attack(1, 0)
else:
return 'didnt-take-turn'
The code is lifted verbatim (save for the extra four-space indentation) from this part of the tutorial. Note there are two code versions on that page. I have my own version written up, but I encounter the problem even with a direct copy/paste. As far as I can tell, the problem permeates the whole tutorial.
console_wait_for_keypress(True) is supposed to wait for a single key and put it in "key" variable, so I can react to it. All other input is supposed to be flushed. Then new iteration of main loop calls console_wait_for_keypress(True) again, which is supposed to wait for new input, and so on. Instead, every second iteration of console_wait_for_keypress(True) triggers without waiting for a new input.
The game actually works fine, since the second input does not trigger any of the console_is_key_pressed() conditions, and the handle_keys() function returns 'didnt-take-turn', which tells game logic to do nothing. This still means for every cycle, two are spent instead, which is not the desired behaviour. The problem is easy to observe if you print the result of handle_keys() every cycle. It alternates between 'None' and 'didnt-take-turn'.
I am honestly stumped on this. Simply holding down a directional button seems to not produce 'didnt-take-turn' output, but the function is supposed to be used for single keypresses. It can't be too short a delay, since a normal keypress always produces exactly two outputs. The libtcod documentation fails to help me.
What should I do to make a single keystroke trigger only a single console_wait_for_keypress()?
It's fixed in the last svn:
http://doryen.eptalys.net/forum/index.php?topic=1500.msg8507#msg8507
or here:
https://bitbucket.org/jice/libtcod

Infinity Loop and user input as the termination

I have my code and it does go run to infinity. What I want is that if on the unix command window if the user inputs a ctrl C, I want the program to finish the current loop it in and then come out of the loop. So I want it to break, but I want it to finish the current loop. Is using ctrl C ok? Should I look to a different input?
To do this correctly and exactly as you want it is a bit complicated.
Basically you want to trap the Ctrl-C, setup a flag, and continue until the start of the loop (or the end) where you check that flag. This can be done using the signal module. Fortunately, somebody has already done that and you can use the code in the example linked.
Edit: Based on your comment below, a typical usage of the class BreakHandler is:
ih = BreakHandler()
ih.enable()
for x in big_set:
complex_operation_1()
complex_operation_2()
complex_operation_3()
# Check whether there was a break.
if ih.trapped:
# Stop the loop.
break
ih.disable()
# Back to usual operation

Categories