I have an algorithm in a game that simplified looks like this:
if click:
if a and not b and not c:
b = True
c = True
follow_mouse_pos()
if b and c:
b = False
c = False
stay_on_slot()
This is for an inventory system, and the first if statement picks up an item from the clicked slot, and the other one puts it on the clicked slot.
The problem I have is that I want to have the same mouse button for both actions, but if I do that, the item just flickers between the slot and the mouse position.
a, b and c are conditions I have set up to decide wether I pick up the item or I put it in a slot.
a is irrelevant here because it is just for visual stuff.
b is True if the mouse is holding an item
c is True if the clicked slot is empty
The problem is that meeting one of the statements requirements will lead to meeting the conditions of the other statement, and it will loop as fast as the main loop goes. This is a problem because then I have to time it every time I want to move something in the inventory, and that is not good.
I have tried if elif statements but since this is in a loop, the only difference is that on each loop only one of them will be checked, but one of them will always be met.
Is there any way I can avoid this problem?
The full project is on github
The file that contains this part of the code is here:
VoidShips/scripts/GUI/gui.py at line 203
One possibility is that instead of one event "click", you have one like "mouse button up" and another "mouse button down".
Like this you could maybe build something like a drag and drop if that is what you want.
just use if...elif, so if the 'if' condition checks it will not continue checking 'elif'.
Related
I need my GUI to feedback the user. For this purpose I color the background of a certain button in Green or Red according to the result of a certain verification. My purpose is to leave the Green color for several seconds and then return to the initial state (clear the fields and recover the button's original color).
My problem is that the "after" function, that I use for delaying the GUI before I return to the default GUI, doesn't show the before and after. I only see that the button is SUNKEN and then raised after the fields and buttons already returned to their defaults.
What am I doing wrong ?
if condition1 == condition2:
orig_color = self.button2.cget("background")
self.button2.config(bg='springgreen2')
self.return2default(orig_color)
self.after(3000) # 3 seconds delay to realize a Pass result
# return to the defaults
self.SN_field.delete("1.0", "end")
self.HwVer_field.delete("1.0", "end")
self.button2.config(bg=color)
else:
self.button2.config(bg='red2')
If you want something to happen after a delay, move that "something" into a function and schedule it to be called with after. That will allow the event loop to continue to process events (including events that cause the display to refresh).
For example:
def reset(self):
self.SN_field.dellete("1.0", ,"end")
self.HwVer_field.delete("1.0", "end")
self.button2.config(bg=color)
To call it, use after:
if condition1 == condition2:
orig_color = self.button2.cget("background")
self.button2.config(bg='springgreen2')
self.return2default(orig_color)
self.after(3000, self.reset)
When you call after with only a time argument, it causes your entire program to go to sleep, which means it is not able to update the display while it is sleeping.
I have an app which asks a series of 8 math questions to the user. The user has a keypad with which he can manipulate a label. This label answer_text holds the answer, the user wants to submit. Since I had issues, there are two "mechanisms" which allow the checking of this answer (if it is true, the next qestion should pop up, if it is false, nothing should happen). Consider the following code, where I suppress everything unrelated to this problem.
from functools import partial
class KeyPad():
on_key_pad_pressed(self, btn, text):
cs = App.get_running_app().root.ids.calculation_screen
if text.isdigit():
answer_text.text += text
cs.current_answer = answer_text.text
elif text == 'clear':
answer_text.text = ''
elif text == 'submit':
cs.on_current_answer(self, btn, text)
elif text == 'joker'
Clock.schedule_once(partial(cs.on_current_answer(btn, MathBackEnd().get_true_answer()), 1)
class CalculationScreen(Screen):
current_answer = StringProperty('')
def __init__(self):
pass
def on_current_answer(self, btn, text):
true_answer = MathBackEnd().get_true_answer()
if text == true_answer:
Logger.info('CS: Cool! You got it')
else:
Logger.info('CS: Please try again.')
So there are at least 3 different ways in Kivy to achieve the wiring between the KeyPad (holds the information about the given answer) and the CalculationScreen (assess given answers). Note that the approach in the first if-case (cs.current_answer = answer_text.text) does work, because current_answer is a StringProperty. Therefore, when it changes, its callback on_current_answer will get called.
The submit button and the joker button both work fine. If you click 'submit' your answer gets checked. If you click 'joker' the correct answer gets checked with a one second penalty for pressing 'joker'. However answers should get checked automatically (first if-case in "on_key_pad_pressed") and this works except for one case: if the current true answer is the same as the former true answer. Suppose the first question is "3+4", you enter 7 and the game automatically checks your answer. It is true, so you get the next question "1+6". You enter 7... nothing happens. You click 'clear' to clear the answer_text label and press 7 again... nothing happens. You click 'submit'. The answer gets checked and you get the next question.
So, which of these 3 approaches is superior/inferior? Should I always use schedule_once (seems to be very robust)? Do you see why I get this buggy behavior in the first if case, if two successive (true) answers are equal? During calculation, the eventloop gets congested by a timer (timing the response of the user), which updates every tenth of a second. May this congestion lead to this weird behavior?
Any help is much appreciated!
I've been creating a game using pygame, and I need a 'wait until' function for something in it to work. I know I could quite easily do this by just creating a while loop and breaking once the condition becomes true every time I need this functionality, but it would be far better if I actually had a function that I could use to do this. I wrote this function here and found that it didn't work:
def wait_until(condition):
while True:
Quit() #These three lines are required to allow for the program to continue running
pygame.display.update()
CLOCK.tick(FPS)
if condition(): break
I believe this to be a result of when calling the function, the state of the condition in that moment is saved to the variable 'condition' as a Boolean. So it wouldn't do anything if it changed after that. For instance:
block = classes.sprite.Sprite(x = 50, y = 50)
wait_until(block.distance_to(pygame.mouse.get_pos()) <= 100)
would just be the same as passing False into the function if the mouse was further than 100 pixels away.
So what can I do to solve this problem, it really has stumped me and I've no idea what I could do to create a function that does what I'd like it to do.
Any help on the matter would be appriciated
wait_until(lambda: block.distance_to(pygame.mouse.get_pos()) <= 100)
This creates a function that can be called as many times as needed, to evaluate your condition.
I am creating an experiment using Psychopy builder.
The participant is presented with an image containing numbers, e.g. 10 and 20.
They enter what they think is the mean of the numbers, 15 in this case, and then press the spacebar to move on to the next image.
I am trying to have it so there is a display/box on screen that shows them their entry, as with larger numbers in the hundreds of thousands and millions I think they might lose track of what they have pressed.
The ability to change their entry would be excellent also, but really I am most interested in them being able to see it on screen.
In builder I can't find a way to do this, and the ratings scale is not appropriate for huge numbers.
I found these solutions in code to do something that sounds like it:
http://www.psychopy.org/wiki/home.php/Snippets/LiveUpdatingText
However when I try to add them using the code insert function , or just adding them to the compiled script the screen locks up when I try to run the experiment. I am a novice at python, and am not sure where to start fixing this. Is what I'm trying to do possible?
I'm happy to provide some example code from the compiled builder experiment.
Thanks in advance!
Those code snippets are designed for Coder, where you control everything that is happening and when. The same thing can be done in Builder, but you will have to amend the code to fit in with Builder's event loop cycle. i.e. Builder does certain things at the start of an experiment, on every trial, on every screen refresh and so on. So you can't just insert this sort of code without modification, because, for example, it attempts to wait indefinitely for a keypress. Builder meanwhile, is checking the keyboard every screen refresh (typically at 60 Hz), so if you try to wait indefinitely for a keypress in code, you'll be halting Builder from doing everything else it needs to do.
In essence, you just need to break up the code into snippets that go in the appropriate tab in a Builder Code Component (for code to be executed at experiment start, on each frame, and so on), and avoid indefinite functions like event.waitKeys() in favour of instantaneous checking via event.getKeys()
e.g. to adapt the second example from Jonas Lindeløv, in the "Begin Routine" tab, put:
chars = list('0123456789.') # the valid characters
meanText = '' # start with an empty answer on each trial
In the "Each Frame" tab, put something like:
response = event.getKeys() # get a list of keys pressed at this instant
if len(response) > 0: # if there was one,
key = response[0] # just convenient shorthand
if key in chars:
meanText = meanText + response[0]
elif key == 'space':
meanText = meanText + ' '
elif key == 'backspace' and len(meanText) > 0:
meanText = meanText[:-1]
elif key == 'return':
thisExp.addData('Answer', meanText) # save the response
continueRoutine = False # finish this trial
# update the appropriate text stimulus with the current response value:
insertNameOfYourTextStimulusComponent.text = meanText
i'm currently developping a program that runs on a robot. The program needs to add positions to a list but you have to push a button to add a new position. This has to be inside a while loop. But when i press the button the function repeats itself multiple times instead of just 1. Here is the part of code:
while not self.done():
if self._io_left_lower.state:
self._gripper_left.open()
elif self._io_left_upper.state:
self._gripper_left.close()
if self._io_right_lower.state:
self._gripper_right.open()
elif self._io_right_upper.state:
self._gripper_right.close()
if self._io_left_nav.button0:
print("repeats")
#self._mapping.append([self._limb_left.joint_angles(), self._gripper_left.position()])
if self._io_right_nav.button0:
self._mapping.append([self._limb_right.joint_angles(), self._gripper_right.position()])
if self._io_left_nav.button1:
self.stop()
As you can see when the self._io_left_nav.button0 is pressed it will now just print 'repeats' several times but it has to to be printed just onces. same for the self._io_right_nav.button0.
If button0 is a physical button and your loop is polling the button state then it is normal that many iterations run in the time your button is being pressed. You have several options to solve this situation, two of them are:
Do not use a loop check the button but hardware interruptions used to keep an updated representation of your state.
If you still want to use your loop, you should detect button state changes instead checking its current state at each iteration. That may require your program to implement a noise filter.
A very simple way to implement the second option is to have a button state variable with a timestamp associated: At each loop iteration, if the difference between the current time and the timestamp is big enought then you should check if the current state is the same than the one stored in the variable. If not then you have detected a change. In any case, at this point you should update your state-timestamp variable.
This is an example:
from time import time
prevState = (False,time())
while not self.done():
current = time()
if self._io_left_nav.button0 and (current-prevState[1])*1000>2: #For example, 2 ms
if self._io_left_nav.button0 != prevState[0]:
print("repeats")
prevState = (self._io_left_nav.button0,time())
I presume that's some kind of flag, that the button was pressed.
If you don't clear the state, the condition in the if statement will evaluate true. Again, and again....