Python/Tkinter force wait on button click - python

Back after another 30 minute search and either failure to comprehend results or unable to find results...
I want to force my application to wait for a button click before continuing, and have the following code snippet as my example:
...
def crack(self, Filenamelist, forceclick):
forceclick += 1
self.crackButton.configure(state='active')
if forceclick != 2:
self.crackButton.bind('<ButtonRelease-1>', self.crack(Filenamelist, forceclick))
self.outputBox.insert(END, '\n' + 'Parsing answer numerator...' + '\n')
...
I basically want it to load the function crack(), increment 1 to forceclick (which was set to 0 beforehand), change the 'crack button' to an active state, and then bind the button while waiting for the user to provoke the bind. After the bind is provoked, the function reloads, increments one to forceclick, and then skips the if statement.
However, when I run the program through, it just binds the key to the crack button and automatically reloads the function to bypass the if statement... I tried a while loop before, but that did not end well...
Any suggestions?

You need to make the bound function a lambda:
self.crackButton.bind('<ButtonRelease-1>', lambda e: self.crack(Filenamelist, forceclick))
Currently it is calling the function.
Although there are probably better ways to do what you are trying to accomplish, this should fix your immediate issue.

Related

Executing "SCI_GOTOLINE" API of Qscintilla inside indicatorReleased() event handler is not functioning properly

I am developing a desktop application using "PyQt4". The application contains an XML editor implemented on top of Qscintilla. However I have an issue whenever I click a hyperlink-alike text defined via indicators. The "indicatorClicked" event is invoked, but when I execute "SCI_GOTOLINE" API inside it, it goes to the desirable line properly but unluckily, for some reason, it selects the text from the clicked text position till the destination line. For me, it seems as if mouse does not get released! I tried also to use "indicatorReleased" event with no luck! Do u have any idea how to resolve this?
This is how I am hooking to indicator released event:
self.__editor.indicatorReleased.connect(self.__on_indicator_released)
event handler is simply invoking SCI_GOTOLINE API to some line number:
def __on_indicator_released(self, line_number, index, keys):
self.__editor.SendScintilla(QsciScintilla.SCI_GOTOLINE, line_number)
I got a response from #Matic Kukovec & #K.Mulier on another thread. I am posting it here so others can benefit from it.
#Matic Kukovec replied to me: "There are two solutions: Use a QTimer.singleShot inside the indicatorReleased event to execute the SCI_GOTOLINE after a short delay (I use 50ms), or inside the indicatorReleased event use: while mouse_state != data.Qt.NoButton: PyQt5.QTest.qWait(1) to wait until there is no muse button pressed, and then execute SCI_GOTOLINE."
After trying the two approaches, the singleShot-based solution worked as charm:
Hooking to an indicator_released handler:
self.__editor.indicatorReleased.connect(self.__on_indicator_released)
Corresponding event handler:
def __on_indicator_released(self, line_number, index, keys):
QTimer.singleShot(50, lambda: self.__go_to_line(line_number))
Calling SCI_GOTOLINE in __go_to_line API:
def __go_to_line(self, line_number):
self.__editor.SendScintilla(QsciScintilla.SCI_GOTOLINE, line_number)
However, The mouse_state-based solution did not work, since mouse_button equals to "Qt.NoButton" all the time.
mouse_state = QtCore.QCoreApplication.instance().mouseButtons()
while mouse_state != Qt.NoButton:
mouse_state = QtCore.QCoreApplication.instance().mouseButtons()
QTest.qWait(1)

python: adding a reset function to a threaded application

My python code runs a function that takes a long time:
Navigation()
During this execution I'd like to have a reset button. For reset button to work, I used threading and I managed to get my code into threaded callback function with the button press. How do I terminate the Navigation() that is currently running and call another fresh Navigation() function? thanks.
Thanks in advance.
If your navigation function has a loop that it executes, you could have a check to see if an "interrupt" variable is set to True. If so, you could have the Navigation function terminate during that check:
def navigation(self):
# reset self.interrupt as appropriate
while not self.interrupt:
pass # Do something here
# You will want some other exit condition as well, of course.
def button_pressed_response(self):
self.interrupt = True
self.navigation()
If you have access to a multithreaded library as you indicated, you could use a more elegant callback function and simplify the reset logic as well.
This answer pretty much sums up issues and pitfalls of terminating threads:
https://stackoverflow.com/a/325528

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

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/

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

When while loop placed in wxPython events

I'm trying to write a GUI program grabbing specific contents from a webpage. The idea is when I hit the start button, the program should start extracting information from that page. And I want to add some code to check if connected to the Internet. If not, continue trying until connected.
So I just added the following code in the event, but found it didn't work. Also the whole program has to be closed in a forced way. Here's my code:
import urllib2
import time
InternetNotOn = True
while InternetNotOn:
try:
urllib2.urlopen("http://google.com")
InternetNotOn = False
print "Everyting is fine!"
except urllib2.URLError, e:
print "Error!"
time.sleep(10)
What could the problem be?
When you have an event based program, the overall flow of the program is this:
while the-program-is-running:
wait-for-an-event
service-the-event
exit
Now, lets see what happens when service-the-event calls something with a (potentially) infinite loop:
while the-program-is-running:
wait-for-an-event
while the-internet-is-on:
do-something
exit
Do you see the problem? In the worse case your program may never call wait-for-an-event again because your loop is running.
Remember: the event loop is already an infinite loop, you don't need to add another infinite loop inside of it. Instead, take advantage of the existing loop. You can use wx.CallAfter or wx.CallLater to call a method which will cause your function to be called at the next iteration of the event loop.
Then, within your function you call wx.CallAfter or wx.CallLater again to cause it to again be called on the next iteration of the event loop.
Instead of time.sleep(10) you can call wxApp::Yield and time.sleep(1) ten times.
Beware of reentrancy problems (e.g. pressing the start button again.). The start button could be dimmed while in the event handler.
But Bryan Oakley's solution is probably the better way.

Categories