Extract value from a list in another function in Python - python

I am programming a robot and I want to use an Xbox Controller using pygame. So far this is what I got (original code credits to Daniel J. Gonzalez):
"""
Gamepad Module
Daniel J. Gonzalez
dgonz#mit.edu
Based off code from: http://robots.dacloughb.com/project-1/logitech-game-pad/
"""
import pygame
"""
Returns a vector of the following form:
[LThumbstickX, LThumbstickY, Unknown Coupled Axis???,
RThumbstickX, RThumbstickY,
Button 1/X, Button 2/A, Button 3/B, Button 4/Y,
Left Bumper, Right Bumper, Left Trigger, Right Triller,
Select, Start, Left Thumb Press, Right Thumb Press]
Note:
No D-Pad.
Triggers are switches, not variable.
Your controller may be different
"""
def get():
out = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
it = 0 #iterator
pygame.event.pump()
#Read input from the two joysticks
for i in range(0, j.get_numaxes()):
out[it] = j.get_axis(i)
it+=1
#Read input from buttons
for i in range(0, j.get_numbuttons()):
out[it] = j.get_button(i)
it+=1
first = out[1]
second = out[2]
third = out[3]
fourth = out[4]
return first, second, third, fourth
def test():
while True:
first, second, third, fourth = get()
pygame.init()
j = pygame.joystick.Joystick(0)
j.init()
print 'Initialized Joystick : %s' % j.get_name()
test()
Do you see the list called "out"? Each element in it is a button on the Xbox Controller. I want to extract those elements and put them on variables, one variable to each element/button so I can control my robot.
How could I do it?
I've tried to use global variables but then everything turned down to a mess.
Please note that I am a beginner in Python.

If you want to have out in your program then just return it from your function get:
def get():
# rest of the code ...
return out
Also change your function test:
def test():
while True:
out = get()
LThumbstickX = out[0]
LThumbstickY = out[1]
# and so on
Then run your program as before. What the function test does is constantly (while True) read the keypad. You could for example do:
def test():
while True:
out = get()
LThumbstickX = out[0]
if LThumbstickX != 0:
print 'Left button has been pressed'
# and so on

You can just return the list and use python's unpacking feature:
def get():
out = [1,2,3,4]
return out
first, second, third, fourth = get()

Related

Update text in real time by calling two functions Pygame

I have program that takes input from the user and displays multiple variations of the input using the Population() function. The store_fit function adds these different variations to a list then deletes them so that the list is only populated with one variation at a time.
I want to be able to get the variation from the list and use it to update my text. However, my program only updates the text after the Population function is completed. How could I run the Population function and update my text simultaneously?
code:
fit = []
...
def store_fit(fittest): # fittest is each variation from Population
clear.fit()
fit.append(fittest)
...
pg.init()
...
done = False
while not done:
...
if event.key == pg.K_RETURN:
print(text)
target = text
Population(1000) #1000 variations
store_fit(value)
# I want this to run at the same time as Population
fittest = fit[0]
...
top_sentence = font.render(("test: " + fittest), 1, pg.Color('lightskyblue3'))
screen.blit(top_sentence, (400, 400))
I recommend to make Population a generator function. See The Python yield keyword explained:
def Populate(text, c):
for i in range(c):
# compute variation
# [...]
yield variation
Create an iterator and use next() to retrieve the next variation in the loop, so you can print every single variation:
populate_iter = Populate(text, 1000)
final_variation = None
while not done:
next_variation = next(populate_iter, None)
if next_variation :
final_variation = next_variation
# print current variation
# [...]
else:
done = True
Edit according to the comment:
In order to keep my question simple, I didn't mention that Population, was a class [...]
Of course Populate can be a class, too. I this case you've to implement the object.__iter__(self) method. e.g.:
class Populate:
def __init__(self, text, c):
self.text = text
self.c = c
def __iter__(self):
for i in range(self.c):
# compute variation
# [...]
yield variation
Create an iterator by iter(). e.g.:
populate_iter = iter(Populate(text, 1000))
final_variation = None
while not done:
next_variation = next(populate_iter, None)
if next_variation :
final_variation = next_variation
# print current variation
# [...]
else:
done = True

Is there a focus changed event in urwid?

Is it possible to track the change of the highlighted item in an urwid.ListBox object? Or even via a ListWalker object?
I would like to call a callback when the user moves from one item to another using the arrow keys [🠉], [🠋], not when the user hits [Enter] on one item.
After some research and experimentation, it's possible to do this by registering the modified signal with the ListWalker object.
import urwid
def callback():
index = str(listBox.get_focus()[1])
debug.set_text("Index of selected item: " + index)
debug = urwid.Text("Debug")
captions = "A B C D E F".split()
items = [urwid.Button(caption) for caption in captions]
walker = urwid.SimpleListWalker(items)
listBox = urwid.ListBox(walker)
urwid.connect_signal(walker, "modified", callback)
frame = urwid.Frame(body=listBox, header=debug)
urwid.MainLoop(frame).run()
Reference: Urwid > Signal functions > connect

Urwid horizontal menu - return to previous state or restart loop

I'm building an app based on the Horizontal Menu example from Urwid.
I have an item that I want to show some information. I have an "OK" button underneath, and I want it to either pop the menu back to the previous state, or maybe restart the entire screen/loop.
In the example, they use ExitMainLoop(), but I don't want to exit - I just want to restart it.
So, I've changed the callback to a different function - but all my attempts either do nothing or crash my program.
Here's the relevant bits:
Starting the menu:
if __name__ == "__main__":
top = HorizontalBoxes()
top.open_box(menu_top([]).menu)
urwid.MainLoop(urwid.Filler(top, 'middle', 40), palette).run()
Relevant menu class/functions. My problem is - what goes in the does_nothing function?
class Choice(urwid.WidgetWrap):
def __init__(self, caption, pid):
super(Choice, self).__init__(
MenuButton(caption, partial(self.item_chosen,pid)))
self.caption = caption
def item_chosen(self, pid, button):
if self.caption == (u"System status"):
showSystemStatus()
def showSystemStatus():
response = urwid.Text( "... some text ")
done = MenuButton(u'OK', does_nothing)
response_box = urwid.Filler(urwid.Pile([response,done]))
top.open_box(urwid.AttrMap(response_box, 'options'))
def does_nothing(key):
????????????????????
return
I found a solution!
I had to add a new method to the HorizontalBoxes class. Specifically, the close_box method:
class HorizontalBoxes(urwid.Columns):
def __init__(self):
super(HorizontalBoxes, self).__init__([], dividechars=1)
def open_box(self, box):
if self.contents:
del self.contents[self.focus_position + 1:]
self.contents.append((urwid.AttrMap(box, 'options', focus_map),
self.options('given', 40)))
self.focus_position = len(self.contents) - 1
def close_box(self):
if self.contents:
del self.contents[self.focus_position :]
self.focus_position = len(self.contents) - 1
And then, it was a simple matter of calling the close_box method from my does_nothing function from before:
def does_nothing(key):
top.close_box()
return

Right way to continuously update color of Tkinter Canvas item

I am trying to figure out how to change a rectangle's color continuously, with a second between each change. Right now I have this simple function which makes a window with a square above a button, that changes the color of the square after every button click:
def junk():
def random_color():
red = int(random.random()*256)
green = int(random.random()*256)
blue = int(random.random()*256)
return '#' + ('{:0>#02X}'*3).format(red,green,blue)
def change_color():
c.itemconfig(r, fill=random_color())
x = Tkinter.Tk()
c = Tkinter.Canvas(master=x)
c['width'] = 400; c['height'] = 400
r = c.create_rectangle(0,0,400,400)
b = Tkinter.Button(master=x, command=change_color)
b['text'] = 'change color'
c.pack(); b.pack(); x.mainloop()
What I want is to be able to click once, and then have the colors change automatically. I know I want to use a CheckButton instead of a Button for this, so that one click will start the loop, and and the next click will stop it.
Also, this is not how I am structuring my "real" code, this is how I am testing from the IDLE shell. Defining the helper functions inside the junk function makes it easy to get at all the relevant code at once, without having the bloat of a full class. So please don't give me comments on style, this is quick and dirty on purpose.
TL;DR I'm not sure how to get a continuous loop running to change the color, while being able to start and stop the loop with a button click.
I figured it out. Before I show my solution, I want to correct a mistaken statement I made above: I don't to use a Checkbutton to make this work. I can make a normal button into a toggle button by changing the 'relief' option of the button. Here is my solution:
def junk():
def color_poll():
global alarm
c.itemconfig(r, fill=random_color())
if keep_going:
alarm = c.after(1000, color_poll)
def change_color():
global keep_going, alarm
if not keep_going:
keep_going = True
b['text']='STOP';b['fg']='red';b['relief']=Tkinter.SUNKEN
color_poll()
else:
keep_going = False; c.after_cancel(alarm); alarm = None
b['text']='GO';b['fg']='green';b['relief']=Tkinter.RAISED
x = Tkinter.Tk()
c = Tkinter.Canvas(master=x)
c['width'] = 400; c['height'] = 400
r = c.create_rectangle(0,0,400,400)
global keep_going, alarm
keep_going = False; alarm = None
b = Tkinter.Button(master=x, command=change_color)
b['text'] = 'GO';b['fg']='green';b['font']='Arial 16';b['relief']=Tkinter.RAISED
c.pack(); b.pack(); x.mainloop()
I'm using the same random_color function, but I moved it out because it out of the junk function because it didn't need to be there.

Select unique value from list

What I am trying to do is, have a list of characters,each of which is a procedure, then I want to pick randomly (or pseudo randomly, it doesn't matter) from this list, and execute that procedure, then I want to be able to run the it again, and not get the same value,for example if I have five values I want to be able to run it 5 times, then the 6th time I run it, it returns nothing. Here is the code:
from Tkinter import*
from random import randint
Characters=[Percy,Annabeth,Leo,Chuck,Sarah]
Used = []
def callback():
end = len(Characters)-1
rando = randint(0,end)
Characters[rando]
for i in Characters:
if Characters[rando] in Used:
print 'This has already been used'
else:
Characters[rando]()
Used.append(Characters[rando])
break
game = Tk()
game.geometry('50x50+700+100')
Button1 = Button(game,text = '1',command =lambda:callback() )
Button1.pack(side=LEFT)
game.mainloop()
I am trying to get
callback()
to run properly, I have tried what you see but I have also tried
if Characters[rando] in Used:
print 'This has already been used'
else:
Characters[rando]
Used.append(Characters[rando])
in both cases it will run the same procedure multiple times, for example, 'Leo' might be executed 3 times in a row. I searched for hours for a way to do this, but I couldn't find one.
First, I would shuffle the Characters:
Characters = [Percy,Annabeth,Leo,Chuck,Sarah]
random.shuffle(Characters)
Now when you run your callback, you pop one character out:
def callback():
try:
C = Characters.pop() #popping the last one is more efficient than the first.
except IndexError:
return None
return C()
Since this destroys Characters, you may want to keep a copy of it around to reset if you need to:
random.shuffle(Characters)
Characters_save = Characters[:]
def reset_characters():
Characters[:] = Characters_save[:]
Completely untested - but you could implement a basic class:
from random import shuffle
class CallNext(object):
def __init__(self, vals):
self.vals = vals[:]
shuffle(self.vals)
self.iter = iter(self.vals)
def __call__(self):
try:
next(self.iter)()
except StopIteration as e:
pass # or do something smarter?
Another option instead of catching StopIteration would be to use:
next(self.iter, lambda: None)()
And then have:
Button1 = Button(game, text='1', command=CallNext(Characters) )

Categories

Resources