I'm using the Graphics and Myro packages in the Calico IDE, can anyone figure a way for me to hit the 'q' key and have the program terminate? Currently when I hit the 'q' key I have to click the mouse on my window to terminate.
def main():
win = Window(800,500)
bg = Picture("http://www.libremap.org/data/boundary/united_states/contig_us_utm_zone_14_600px.png")
bg.draw(win)
while True:
char = win.getKeyPressed()
if char == 'q':
win.close()
break
x, y = win.getMouse()
MPO = Rectangle(Point(x,y), Point(x+10,y+10))
MPO.fill = Color("white")
MPO.draw(win)
I've never heard of Calico before, but from 5 seconds looking at the docs, I see this:
getMouse() - waits until user clicks and returns (x, y) of location in window
So, I'm willing to bet this is why you have to click on your window before hitting the Q key has any effect—because you're program is stuck waiting inside the getMouse() call, just as the docs say it should be.
Even if the docs didn't explain this, you could probably figure it out pretty quickly by adding some printing/logging and/or running in a debugger, to see where it's bogged down when it's not responding to your keypresses.
For example, the quick&dirty way to do this:
while True:
print 'Before getKeyPressed'
char = win.getKeyPressed()
print 'After getKeyPressed, got', char
if char == 'q':
print 'About to close because of q'
win.close()
print 'Closed'
break
print 'Before getMouse'
x, y = win.getMouse()
print 'After getMouse, got', x, y
… and so on.
Of course in real life, you don't want to add a print statement for every single line of code. (And, when you do want that, you want a smarter way of instrumenting than manually writing all those lines.) But you can add a few to narrow it down to the general area, then zoom in and add a few more within that area, and so on, until you find the culprit.
Meanwhile, if you change your code to use getMouseNow() instead of getMouse(), that will solve the problem, but only by busy-looping and redrawing the window over and over as fast as possible, whether or not you've done anything.
What you really need here—as for any GUI app—is an event loop. I can see that there are functions called onMouseDown and onKeyPress, which looks like exactly what you need here.
Related
I'm trying to build a countup clock with a raspberry pi. Now I'm thinking about, how I can show the counter (yeras:months:days) on a screen. So how can I generate a window, which shows the counter and updates itself (e. g. every hour). My idea is to use it as a decoration, so it shouln't be nessessary to interact with the shown.
That's my code so far:
import time
now = time.localtime()
hour=now.tm_hour
minute=now.tm_min
summer=now.tm_isdst
#print("Tag:", day)
#print("Monat:", month)
#print("Jahr:", year)
while True:
time.sleep(3600.0)
day=7
month=4
year=now.tm_year
if month<4:
j=year-2020
elif month==4:
if day<4:
j=year-2020
else:
j=year-2019
else:
j=year-2019
if month<4:
m=month+7
elif month==4:
if day<7:
m=month+7
elif day>=7:
m=0
else:
m=month-4
else:
m=month-4
if day<7:
t=day+24
elif day==7:
t=0
else:
t=day-7
print(j , ":" , m , ":" , t)
look into the python curses module or one of the wrappers around it (i.e. blessings) which is a little easier to use. This will allow you to create a screen in the terminal and refresh the screen every so often, this would be every second in your case. This would allow you to open a terminal in the pi run it and just leave it.
However a much easier solution would be to just keep calling time.ctime() and clearing the screen and refreshing it, but if you want to get some practice with curses so you can do stuff with it in the future go for it.
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 playing around trying to emulate some Ctrl-P-like behaviour in Vim. What I have so far:
User calls my function.
A window is opened with a list of items in it.
A prompt is displayed under the list of items.
I want the user to be able to use the up and down arrows to select from the items but without leaving the prompt (in which they can also type).
I have got 1-3 mostly under control but am struggling with #4. I have got as far as this:
python << EOF
import vim
def MyWindow():
# Create a new window at the bottom
vim.command("below new")
vim.current.buffer.append("123")
vim.current.buffer.append("234")
vim.current.buffer.append("345")
vim.command("redraw")
vim.command("setl cursorline")
vim.command("echon '>> ' | echoh None")
while True:
vim.command("let x = getchar()")
character_code = vim.eval("x")
if character_code == "13":
# Exit by pressing Enter
break
elif character_code == "\x80kd": # Down
vim.command("keepj norm! j")
vim.command("redraw")
EOF
command! MyWindow python MyWindow()
Now the first down arrow keypress works perfectly, but subsequent down arrow kepresses don't, even though the code in the "if down key pressed" case is executed. Any ideas?
I just realised I can make it work by doing this:
elif character_code == "\x80kd": # Down
vim.command("keepj norm! j")
vim.command("setl cursorline") # <-- This fixed it
vim.command("redraw")
But I have no idea why or if this is a sensible thing to do.
The code below lets you walk around a small grid on the screen using the arrow keys putting "." where you've explored or been next to. Even though I have my refresh before the first getch (to get a key-stroke) the screen doesn't first display anything until you've moved off your starting position. Shouldn't the addstr followed by refresh immediately show and then the getch waits after that? I even tried adding a stdscr.refresh(), but that didn't help either. How do I get the screen to refresh immediately before waiting for the first key-stroke?
import curses
def start(stdscr):
curses.curs_set(0)
movement = curses.newpad(10, 10)
cur_x, cur_y = 5, 5
while True:
movement.addstr(cur_y, cur_x, '#')
for (x_off, y_off) in [(-1,0),(1,0),(0,-1),(0,1)]:
movement.addstr(cur_y + y_off, cur_x + x_off, '.')
movement.refresh(1, 1, 0, 0, 7, 7) #Nothing is displayed until after the first key-stroke
key_stroke = stdscr.getch()
move_attempt = False
if 0 < key_stroke < 256:
key_stroke = chr(key_stroke)
elif key_stroke == curses.KEY_UP and cur_y > 1:
cur_y -= 1
elif key_stroke == curses.KEY_DOWN and cur_y < 8:
cur_y += 1
elif key_stroke == curses.KEY_LEFT and cur_x > 1:
cur_x -= 1
elif key_stroke == curses.KEY_RIGHT and cur_x < 8:
cur_x += 1
else:
pass
if __name__ == '__main__':
curses.wrapper(start)
The docs are broken. I'd used curses back in the day, but libncurses is new to me.
My first hint came from ncurses(3):
The ncurses library permits manipulation of data structures, called windows, which can be thought of as two-dimensional arrays of characters representing all or part of a CRT screen. A default window called stdscr, which is the size of the terminal screen, is supplied. Others may be created with newwin.
…
Special windows called pads may also be manipulated. These are windows which are not constrained to the size of the screen and whose contents need not be completely displayed.
But then refresh(3) got decidedly evasive:
The routine wrefresh works by first calling wnoutrefresh, which copies the named window to the virtual screen, and then calling doupdate, which compares the virtual screen to the physical screen and does the actual update. … The phrase "copies the named window to the virtual screen" above is ambiguous. What actually happens is that all touched (changed) lines in the window are copied to the virtual screen. This affects programs that use overlapping windows; it means that if two windows overlap, you can refresh them in either order and the overlap region will be modified only when it is explicitly changed. [emphasis mine]
which prompted me to try adding
stdscr.refresh()
after your pad.refresh() which worked. And then I moved it further up start() to see if it was really needed on every pad modification. I moved it all the way up to the first point there is a stdscr to work with yielding:
def start(stdscr):
stdscr.refresh()
curses.curs_set(0)
…
which smacks of voodoo programming, but I'm not going to look at the innards of a 20-year old library made to cope with glass ttys to try to grok it.
Add stdscr.refresh() sometime before the movement.refresh() to solve the issue.
By adding time.sleep(1) after the refresh statement, it does write to the screen, but then it disappears when stdscr.getch() is called, but only the first time. Probably has to do with some sort of delayed initialization of stdscr.
Calling stdscr.refresh() after the movement.refresh() has the same effect: The very first time through the loop stdscr.refresh() clears the screen, but not in subsequent times through the loop. By calling stdscr.refresh() early in the program it gets this weird first time refresh out of the way.
When using a pad, for some reason—I don't know why—you have to call curses.doupdate after invoking the pad's refresh.
Adding window.nodelay(1) before while solved issue for me.
I am new to programming with Python. I have been working through a tutorial book that I found, and got the game up and running, but then decided I wanted to have a "Play Again?" option at the end. I can get the game to quit out with a press of the "n" key, but cannot work out how to get the game to restart.
Here is the code I think is giving the trouble:
#player reaches treasure
if player_rectangle.colliderect(treasure_rectangle):
#display text
screen.blit(text,(screen_width/2-195,screen_height/2-25))
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_n:
exit()
elif event.key==pygame.K_y:
pygame.display.update()
I know something needs to go after the elif event, and I have tried all that I can think of. I tried to define the whole program, and call it but that stopped the whole thing running. I have looked around internet sites, but just cannot seem to come up with a answer.
Can some one help out in easy terms how to get the game to restart to the starting position when the y key is pressed? I know it has something to do with a loop, I just cannot place my finger on what.
Many thanks.
It's not entirely clear how your code is organized, so I'll be very general. Usually games are implemented with a "main loop" that handles all of the action. In "pseudo"-python:
def main_loop():
while True:
handle_next_action()
draw_screen()
if game_is_over():
break
Before you start the loop, you usually do some setup to get the game state how you want it:
def main():
setup()
main_loop()
shut_down()
Given those parts, you can reset the game by having the main loop code call setup again (it may need to be specifically designed to be runable more than once):
def main_loop():
while True:
handle_events()
draw_screen()
if game_is_over():
if play_again(): # new code here!
setup()
else:
break
You might want to split the setup code into two parts, one which only needs to be run when the program begins (to read configuration files and set up things like the window system), and a second that gets repeated for each new game (setting up the game's state).
To restart a game you normally need to reset all variables to their initial value (e.g. number of lives, score, initial position, ...).
The best way is to put all initialisations inside a procedure:
def init():
global score,lives # use these global variables
score=0
lives=3
init() # call init() to define and reset all values
while 1: # main loop
...
elif event.key==pygame.K_y:
init() # restart the game