Select unique value from list - python

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) )

Related

How to update the tkinter text label every half a second? How to avoid .mainloop so that rest of program can execute?

I'm using a Kvaser Leaf Professional and trying to read the can-bus data from a motorcycle through Python coding to be able to use the data to see where values change and which values stays constant.
At the moment I'm struggling to display the data on tkinter and to update it after each time the data is read/updated again, because it changes every few microseconds. Normal ways of using .after() doesn't seem to work 100%, or I'm just doing something wrong. Or is there a better way to display updated data than tkinter?
The other problem I have is when I display the tkinter it uses a root.mailoop() to run the window and when that happens it enters that main loop and doesn't exit it and run the rest of the program and also won't be able to update the existing data displayed on the window. Is there a way to bypass the root.mainloop() maybe?
Any insight will be helpful and/or small errors I've made in my code. I started using Python only 2 days ago and I'm still learning a lot and don't know all the in's and out's yet.
Thank you in advance.
Here is my whole program so far:
import keyboard
import time
import sys
import tkinter as tk
import binascii
from canlib import canlib, Frame
from canlib.canlib import ChannelData
root = tk.Tk()
def setUpChannel(channel,
openFlags=canlib.canOPEN_ACCEPT_VIRTUAL,
bitrate=canlib.canBITRATE_500K,
bitrateFlags=canlib.canDRIVER_NORMAL):
ch = canlib.openChannel(channel, openFlags)
print("Using channel: %s, EAN: %s" % (ChannelData(channel).channel_name,
ChannelData(channel).card_upc_no)
)
ch.setBusOutputControl(bitrateFlags)
ch.setBusParams(bitrate)
ch.busOn()
return ch
def tearDownChannel(ch):
ch.busOff()
ch.close()
def text(t):
tx = binascii.hexlify(t).decode('utf-8')
n = 2
txt = [tx[i:i+n] for i in range(0, len(tx), n)]
return txt
def counter():
while True:
try:
cnt = 1
frame = ch0.read()
firstID = frame.id
while True:
frame = ch0.read()
cnt += 1
if frame.id == firstID:
break
pass
except (canlib.canNoMsg):
break
except (canlib.canError):
print("Rerun")
pass
return cnt
print("canlib version:", canlib.dllversion())
ch0 = setUpChannel(channel=0)
ch1 = setUpChannel(channel=1)
frame = Frame(id_=100, data=[1, 2, 3, 4], flags=canlib.canMSG_EXT)
ch1.write(frame)
print(frame)
cnt = counter()
print("Counter: %d" %(cnt))
time.sleep(1)
while True:
while True:
try:
show = ""
i = 1
while i <= cnt:
frame = ch0.read()
show = show +("%s\t%s\n" %(frame.id, text(frame.data)))
i += 1
print(show)
T = tk.Text(root, height=6, width=80)
T.config(state="normal")
T.insert(tk.INSERT, show)
T.pack()
root.mainloop()
break
except (canlib.canNoMsg) as ex:
pass
except (canlib.canError) as ex:
print(ex)
pass
tearDownChannel(ch0)
tearDownChannel(ch1)
You can edited my code as you want, it will help a lot if I can see how to implement the code better.
You dont need to use multi-threading or anything convoluted. Just use the widget.after() method that calls another function/method of your choosing after the desired time. In the case below, the time is 1 second -> 1000 milliseconds. Just call the class before the mainloop and it will start working.
class UI:
def __init__(self, parent):
# variable storing time
self.seconds = 0
# label displaying time
self.label.configure(text="%i s" % self.seconds)
# start the timer
self.label.after(1000, self.refresh_label)
def refresh_label(self):
#refresh the content of the label every second
# increment the time
self.seconds += 1
# display the new time
self.label.configure(text="%i s" % self.seconds)
# request tkinter to call self.refresh after 1s (the delay is given in ms)
self.label.after(1000, self.refresh_label)

Tkinter fast reader

I have a problem in implementing a fast reader program with tkinter, and that is because it hangs after ~5 seconds.
I know that is because of the while loop but I didn't figured out a way to solve it. Maybe looping outside of the root mainloop?
Here is the code:
import tkinter
import tkinter.ttk
import tkinter.filedialog
import tkinter.messagebox
class FastReader(object):
SLEEP_TIME = 250
def __init__(self):
self.filename = self.getFile()
self.running = True
def window(self):
self.root = tkinter.Tk()
self.root.wm_title("Fast Reader.")
self.text = tkinter.ttk.Label(self.root, text = "word")
self.text.grid(row = 0, column = 0)
# Stop button
self.stop = tkinter.ttk.Button(self.root, text = "Stop", command = lambda: self.close())
self.stop.grid(row = 1, column = 1, columnspan = 2)
while self.running:
for word in self.getWords():
self.text.after(FastReader.SLEEP_TIME)
self.text.config(text = word)
self.text.update_idletasks()
self.root.update_idletasks()
self.root.mainloop()
def close(self):
self.running = False
def getFile(self):
file_ = tkinter.filedialog.askopenfilename()
return file_
def getWords(self):
with open(self.filename) as file_:
for line in file_:
for word in line.strip("\n").split(" "):
yield word
if __name__ == "__main__":
fr = FastReader()
fr.window()
Your program appears to hang because you process all of the words before you give the event loop a chance to update the display, then you put a call to the event loop in an infinite loop.
A good rule of thumb is to never have your own infinite loop in a Tkinter GUI. You already have an infinite loop running, so take advantage of it.
What you should do is write a function that puts the next word in the label, and then calls itself again in the future. It looks somewhat recursive, but it isn't quite. You're simply adding something on to the list of things that the event loop must do. Because it's not strictly recursive, you don't have to worry about running out of stack space.
A simple first step is to first read in all of the words at once. Then, your program removes the first word in the list and displays it in the label. If the list is not empty after that, schedule the command to run again after a delay. It looks something like this:
def showNextWord(self):
word = self.words.pop(0)
self.text.configure(text=word)
if len(self.words) > 0:
self.root.after(self.SLEEP_TIME, self.showNextWord)
You can then replace your while statement with two statements: one to get the complete list of words, and one to display the first word:
self.words = self.getWords()
self.showNextWord()
Of course, you need to modify your getWords() to return the whole list at once. You can use a generator, but it adds a tiny bit of complexity that doesn't seem necessary, unless you're planning on displaying millions of words (which, at four per second, would run for more than several days).

array not saving input

hey back again with the same code, well edited so it works better. anyway trying to add the button input into the array and that works. what doesn't work is the fact every time i call the function do() the values reset due to them being local. i tried to fix this by making it global(within the class) using the self.store array. this didn't seem to fix the problem so if someone could help would be much appreciated here is the relevant code
def __init__(self,master):#is the master for the button widgets
self.count=0
self.store=["0"]
frame=Frame(master)
frame.pack()
self.addition = Button(frame, text="+", command=self.add)#when clicked sends a call back for a +
self.addition.pack()
self.subtraction = Button(frame, text="-", command=self.sub)#when clicked sends a call back for a -
self.subtraction.pack()
self.equate = Button(frame, text="=", command=self.equate)#when clicked sends a call back for a =
self.equate.pack()
self.one = Button(frame, text="1", command=self.one)#when clicked sends a call back for a -
self.one.pack()
def add(self):
self.do("+")
self.count=self.count+1
def sub(self):
self.do("-")
self.count=self.count+1
def equate(self):
self.do("=")
def one(self):
self.do("1")
self.count=self.count+1
def do(self, X):#will hopefully colaborate all of the inputs
cont, num = True, 0
strstore="3 + 8"#temporarily used to make sure the calculating works
self.store=["2","1","+","2","3","4"]#holds the numbers used to calculate.
for num in range(1):
if X == "=":
cont = False
self.store[self.count]=X
print(self.store[self.count])
print(self.store[:])#test code
if cont == False:
print(self.eval_binary_expr(*(strstore.split())))
self.store=["2","1","+","2","3","4"]
If you initialize it this way in the do function, self.store will be reset to ["2","1","+","2","3","4"] every time you call do(X)
Initialize it outside of the do function if you don't want it to be overwritten.
But if you want to add a value to a list, use append(), extend, or += operator:
self.store+=["2","1","+","2","3","4"]
(if you want it to be done only once, do it in the constructor, __init__ function!)
also,
self.store[self.count]=X
If you try to append to the END of the list self.store, you should just do:
self.store.append(X)
This way, you won't need to count anything, with the risk of forgetting an incrementation and replacing a value by X instead of appending X.
As said above, range(1), it's 0...
Let's do it another way:
def do(self, X):
cont, num = True, 0
liststore=['3', '+', '8']#splitting your string.
#initialize str.store elsewhere!
if X == "=":
cont = False
self.store.append("X")
print(self.store[-1])#getting the last
print(self.store)#the same as self.store[:]
if cont == False:
print(self.eval_binary_expr(*(liststore)))
Simpler, and probably better.
Last thing: in Python, you are usually working with lists (like self.store), not arrays (which come from the module array)

Extract value from a list in another function in 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()

beginners program designed using threads and classes

It seems that this question is too long for anyone to comment on... I'm trying to print out some text and a progress bar in a module called 'laulau.py'. Here's a test piece of code that shows a simple version. My goal is to have only one thread, and send information to it. My question is what is the best way to do this ?
file1 (test.py)
#!/usr/bin/env python
from laulau import laulau
import time
print "FIRST WAY"
total=107
t=laulau()
t.echo('this is text')
t.setbartotal(total)
for a in range(1,total):
t.updatebar(a)
time.sleep(0.01)
time.sleep(1)
print
print "\ndone loop\n"
t.stop()
time.sleep(1)
print "SECOND WAY"
with laulau().echo("this is text"):
time.sleep(1)
print "\nyes this is working\n"
time.sleep(2)
file2: laulau.py
#!/usr/bin/env python
# vim:fileencoding=utf8
from __future__ import division
import time
import string
import threading
from sys import stdout
class laulau(threading.Thread):
def __init__(self, arg=None):
super(laulau,self).__init__()
self._stop = False
self.block='█'
self.empty='□'
self.TEMPLATE = ('%(progress)s%(empty)s %(percent)3s%%')
self.progress = None
self.percent = 0
self.bar_width=30
self.bartotal=None
def run (self):
# start thread for text
while not self._stop:
if self.bartotal is None:
print self.arg,
stdout.flush()
time.sleep(0.3)
else:
self.progress = int((self.bar_width * self.percent) / 100)
self.data = self.TEMPLATE % {
'percent': self.percent,
'progress': self.block * self.progress,
'empty': self.empty * (self.bar_width - self.progress),
}
stdout.write('\033[%dG'%1 + self.data + self.arg)
stdout.flush()
time.sleep(0.1)
def setbartotal(self,total):
# set progress bar total
if self.bartotal is None:
self.bartotal = total
self.updatebar(0)
def updatebar (self,num):
self.num=num
self.percent = self.percentage(self.num)
def percentage (self,numagain):
return int((numagain/self.bartotal)*100+1)
def echo (self,arg="Default"):
#self.thread_debug()
self.arg=arg
self._stop = False
self.start()
return self
def thread_debug(self):
print "threading enumerate :%s"%threading.enumerate()
print "current thread :%s"%threading.currentThread()
print "thread count (including main thread):%s"%threading.activeCount()
def stop(self):
self._stop = True
def stopped(self):
return self._stop == True
def __enter__(self):
print "\nwe have come through the enter function\n"
return self
def __exit__(self, type, value, traceback):
self._stop = True
print "\nwe have exited through the exit function\n"
return isinstance(value, TypeError)
In some cases the second way could work. e.g., when I am printing some text, and just need the thread to die at the end of it, but not in the case of a progress bar when it needs updates sending to it. While this all sort of works, and I learned a lot, I still can't figure out how to encapsulate this class in the way I want. As I only want one thread I don't really need to keep instantiating the class, I just need to do this once.
so e.g. my ideal way would be having three functions only:
1 to control text, turn on progress bar etc (from within one parsed string)
2 to set the progress bar total
3 to set the progress bar iteration
I need to change two variables in the class (for the progress bar)
one for the total
one for the iteration
...and it works out percentage from that.
First I thought I should start the thread by inheriting the class stuff from threading, then after looking at threading.Thread(target=blah,etc) at first I couldn't see how to use more than one function, then I discovered I could just put the class name in there threading.Thread(target=laulau) and that would start a thread with the class in, but then I was stumped on how to send that thread information seeing as I hadn't assigned it to a 'name' as in t=laulau()
My second thought was to have functions outside of the class in my module, but because I need more than one function I got a bit confused there too by adding this to the beginning of laulau.py:
def eko (arg):
t=laulau()
t.echo(arg)
def barupate(iteration):
t.updatebar(a)
def bartotal():
t.setbartotal(a)
the first function made an instance of the class but the preceding functions could not change any variables within that. and then i came across function attributes such as this.
class Foo:
#webmethod
def bar(self, arg1, arg2):
...
def webmethod(func):
func.is_webmethod = True
return func
I then started thinking maybe I could use this somehow but have never come across it before.
Ideally id like something like this:
echo.total(107)
echo('[progressbar] this is text') # starts progress bar and instance of thread if not already there...
for a in range(1,total):
echo.updatebar(a)
time.sleep(0.01)
time.sleep(1)
echo.stop() # bar would stop at the end of iterations but text animations (blinking etc) may still be going at this point...
print
print "\ndone loop\n"
if you know about python you are probably looking at me funny now, but bear in mind that I'm a total beginner non-professional and am learning every day, a lot of it thanks to this site. cheers for any help!
edit: should add that I'm aware of the progress bar module and various other recipes, but I'm making this for learning and fun purposes :)
If you just need to print out a progress bar, use the sys module, like so:
import sys
import time
progress = "0" #this is an int meant to represent 0-100 percent as 0-100
old = "0" #this represents the last updates progress, so that this update only adds the difference and not the full progress
def updatebar(progress,old):
for item in range((progress-old)/2) #this takes the range of progress but divides it by 2, making the progress bar 50 characters long
sys.stdout.write("-") #to change the character used to fill the progress bar change the "-" to something else
sys.stdout.flush()
#you may not want to use a while loop here, this just has an example of how to use the update function as it adds one to the progress bar every second
while True:
progress += 1
updatebar(progress,old)
old = progress #sets the old progress as the current one, because next iteration of the while loop the previous progress will be this one's current progress.
time.sleep(1)

Categories

Resources