How do I make rolling text for a loading screen? - python

I want to make a loading screen that does this, but replaces the current line:
LOADING
OADINGL
ADINGLO
DINGLOA
INGLOAD
...
I want to be able to control the number of letters it prints at once. What I tried:
from itertools import cycle
from time import sleep
itr = cycle('LOADING')
for i in range(10):
sleep(0.3)
print('\r', ''.join(next(itr)))
But the output is:
L
O
A
D
I
N
G
L
O
A

You will need to use the end keyword in print to avoid printing each message to a new line. I recommend the approach where you build the string to be displayed on each iteration and then display it. I don't like the cycle approach because you're unable to index into the cycle object very easily. Instead, we can use regular string indexing along with the modulus operator to ensure we don't go out of bounds and can still "loop" over the string over and over.
import time
def scrolling_text(msg, n_chars):
"""
Displays scrolling text of whatever 'msg' and ensures that only n_chars
are shown at a time. If n_chars is greater than the length of msg, then
the string displayed may "loop" around.
Inputs:
msg: str - The message to display
n_chars: int - The number of characters to display at a time
Outputs: Returns None, displays the scrolling text indefinitely.
"""
len_msg = len(msg)
counter = 0
while True:
displayed = ""
for char in range(n_chars):
displayed += msg[(counter+char)%len_msg]
print(f"\r{displayed}", end="")
time.sleep(0.05)
counter = (counter + 1) % len_msg
return
scrolling_text("LOADING", 25)
Each iteration of the while True loop will build the string to be displayed (inside the nested for loop) and then display it. At the end of the while loop, it would be enough to have counter += 1 however for a long-running script, you might end up with counter being unnecessarily large. By letting counter = (counter + 1) % len_msg, we can ensure counter never gets higher than the length of the message.

Straightforwardly with Python slicing:
from time import sleep
from sys import stdout
s = 'LOADING'
for i in range(10):
sleep(0.3)
if i > len(s): i = i - len(s)
stdout.write('\r' + ''.join(s[i:] + s[:i]))

Related

tkinter after calling function with parameters causing function to hang

This is for a game of life app and using after to slow the animation of cells through the stages of birth, life, death, etc.
Couple issues with after:
1.I've been researching Tkinter extensively and this post captures the most clear instructions I have found regarding after with parameters, however, my application of Tkinter is not working - based on comments already received it's probably not the after but that's where the symptoms are appearing?
2.Basically the after does not appear to work at all when I don't put the parameters into parentheses inside the after (ex. widget.after(200, self.my_function, parameter 1, parameter 2, ....) doesn't iterate. However, when I do the same but enclose the parameters it iterates as intended (ex. widget.after(200, self.my_function(parameter 1, parameter 2, ....)).
3.However, when run with the parameters in parentheses, the after hangs. The list in the code below contains 81 items and not coincidentally, the function hangs for 16.2 seconds...
Code is as follows:
def color_cells(
self,
cells,
repeater,
temp_cells=0,
counter=0,
after_id=None
):
global paused
if temp_cells != 0:
self.repeat_colors(temp_cells)
temp_cells.clear()
temp_cells = 0
for key in cells:
if cells[key][0] == 'emerging':
cells[key] = 'active', colors['active'][1]
if cells[key][0] == 'dying':
cells[key] = 'inactive', colors['inactive'][1]
counter = 0
if repeater and not paused:
print("repeater is ", repeater, " and paused is ",paused)
self.id_changes(cells)
else:
self.closeoutGame()
else:
try:
print("made it to the else in color_cells and repeater is ",repeater, " and length of temp cells list is ", len(temp_cells))
except:
print("made it to the else in color_cells and repeater is ",repeater, " and temp cells is empty")
for key in cells:
color_rectangle = self.canvas_manager.find_withtag(key)
self.canvas_manager.itemconfigure(color_rectangle, fill = cells[key][1])
self.canvas_manager.update()
def repeat_colors(self, temp_cells, counter=0):
print("starting repeat colors and the temps cells len is ", len(temp_cells), " and the counter is ",counter)
if counter < len(temp_cells):
color_rectangle = self.canvas_manager.find_withtag(temp_cells[counter][0])
self.canvas_manager.itemconfigure(color_rectangle, fill = temp_cells[counter][1])
self.canvas_manager.update()
counter = counter + 1
root.after(200, self.repeat_colors(temp_cells, counter))
Bryan, you had asked for an example of the error. To show the error I added some print statements to the calling function and then explained where the 16.2 second period of inactivity starts:
starting repeat colors and the temps cells len is 81 and the counter is 0
starting repeat colors and the temps cells len is 81 and the counter is 1
starting repeat colors and the temps cells len is 81 and the counter is 2
...
starting repeat colors and the temps cells len is 81 and the counter is 79
starting repeat colors and the temps cells len is 81 and the counter is 80
starting repeat colors and the temps cells len is 81 and the counter is 81
...hangs for the 16.2 seconds, equal to 200 ms x 81 iterations
I'm a hobbyist and have no formal training and so I'm sure I'm overlooking something basic here, including how to best research on my own. But I appreciate any advice.
I would suggest neither:
root.after(200, self.repeat_colors(temp_cells, counter))
nor:
root.after(200, lambda x=counter: self.repeat_colors(temp_cells, x))
but rather:
root.after(200, self.repeat_colors, temp_cells, counter)
This is a misunderstanding of after() I see often and had myself. The definition of after():
after(ms, func=None, *args)
Call function once after given time.
MS specifies the time in milliseconds. FUNC gives the
function which shall be called. Additional parameters
are given as parameters to the function call. Return
identifier to cancel scheduling with after_cancel.
The code you posted with the above fix and other tweaking -- unfortunately hard to do well without a MCVE:
def color_cells(self, cells, repeater, temp_cells=None, counter=0):
if temp_cells is not None:
self.repeat_colors(temp_cells)
temp_cells.clear()
temp_cells = None
for key in cells:
if cells[key][0] == 'emerging':
cells[key] = 'active', colors['active'][1]
if cells[key][0] == 'dying':
cells[key] = 'inactive', colors['inactive'][1]
counter = 0
if repeater and not paused:
print("repeater is", repeater, "and paused is", paused)
self.id_changes(cells)
else:
self.closeoutGame()
else:
print("Made it to the else in color_cells and repeater is", repeater, "and temp cells is empty")
for key in cells:
color_rectangle = self.canvas_manager.find_withtag(key)
self.canvas_manager.itemconfigure(color_rectangle, fill=cells[key][1])
self.canvas_manager.update()
def repeat_colors(self, temp_cells, counter=0):
print("Starting repeat colors and the temps cells len is", len(temp_cells), "and the counter is", counter)
if counter < len(temp_cells):
color_rectangle = self.canvas_manager.find_withtag(temp_cells[counter][0])
self.canvas_manager.itemconfigure(color_rectangle, fill=temp_cells[counter][1])
self.canvas_manager.update()
counter += 1
root.after(200, self.repeat_colors, temp_cells, counter)
You problem would be the infinite loop caused by root.after(200, self.repeat_colors(temp_cells, counter)). Instead you need to pass your self.repeat_colors as a lambda.
So what is happening is the self.repeat_colors(temp_cells, counter) is being called instantly instead of waiting the 200 seconds. So instead create a lambda function that will wait until the set time to activate.
Keep in mind for lambda expressions if you have a value that changes you need to define it in the lambda. So for the counter you need to do something like x=counter so the lambda is sure to use the correct updated value instead. This normally affects things like loops that create lambda expressions and probably does not matter in this particular case but is a good habit to practice for when it does matter.
Change this:
root.after(200, self.repeat_colors(temp_cells, counter))
To this:
root.after(200, lambda x=counter: self.repeat_colors(temp_cells, x))

Counter in BruteForce while-loop; Out of memory with yield?

I'm starting to learn basic programming with python, and as I encountered permutations, I wanted to see if I could (for fun) brute force a userinput..
It seems to work for short PW's, but my counter seems to have a flaw, or do I miss something about permutations nature? ...the counter shows the same number each time, for any PW of the same length...
Also, there's a memory error for longer PW's, but I thought I ruled that out by using a generator(yield) instead of a list.. or didn't I?
Thanks for your Help
import string
from itertools import permutations
characters = string.ascii_letters
user_pw = input('Set your PW')
attempts = 0
done = False
while True:
def Generator():
guess_storage = [''.join(p) for p in permutations(characters, len(user_pw))]
for current in guess_storage:
yield current
for guess in Generator():
attempts += 1
if guess == user_pw:
print('SUCESS: ')
print('Password: ', guess)
done = True
if done == True:
break
print('Attempts: ', attempts)
I'll start with the memory error. By doing:
guess_storage = [''.join(p) for p in permutations(characters, len(user_pw))]
you are 'gluing' all the generated permutations back into a single list before then iterating over that list. this is where you run out of memory. Instead, try making a generator to be iterated over:
guess_storage = (''.join(p) for p in permutations(characters, len(user_pw)))
Or don't use a generator, and just join each permutation as it is yielded (see below).
Next, you are doing this all in a while loop (including the generator), but this is unnecessary and inefficient. Just build the Generator once, then when you are looping over every guess from the generator, just break out when it finds a match.
A (shorter) way of writing what I think you intended is:
def Generator():
for current in permutations(characters, len(user_pw)):
yield ''.join(current)
for guess in Generator():
print("Guessing", guess) # To show the guesses in action
attempts += 1
if guess == user_pw:
print('SUCCESS: ')
print('Password: ', guess)
print('Attempts: ', attempts)
break
You can also avoid manually tracking the attempts counter by using:
for attempts, guess in enumerate(Generator()):

Python multi-processing

I have a large list containing binary encoded strings that I used to process in a single function before, like so:
""" just included this to demonstrate the 'data' structure """
data=np.zeros(250,dtype='float32, (250000,2)float32')
def func numpy_array(data, peaks):
rt_counter=0
for x in peaks:
if rt_counter %(len(peaks)/20) == 0:
update_progress()
peak_counter=0
data_buff=base64.b64decode(x)
buff_size=len(data_buff)/4
unpack_format=">%dL" % buff_size
index=0
for y in struct.unpack(unpack_format,data_buff):
buff1=struct.pack("I",y)
buff2=struct.unpack("f",buff1)[0]
if (index % 2 == 0):
data[rt_counter][1][peak_counter][0]=float(buff2)
else:
data[rt_counter][1][peak_counter][1]=float(buff2)
peak_counter+=1
index+=1
rt_counter+=1
I have been reading up on multiprocessing and figured that I wanted to try that to see if I could get a big increase in performance, I rewrote my function into 2 (helper and 'caller') like so:
def numpy_array(data, peaks):
processors=mp.cpu_count #Might as well throw this directly in the mp.Pool (just for clarity for now)
pool = mp.Pool(processes=processors)
chunk_size=len(peaks)/processors
for i in range(processors):
counter = i*chunk_size
chunk=peaks[i*chunk_size:(i+1)*chunk_size-1]
pool.map(decode(data,chunk,counter))
def decode(data,chunk,counter):
for x in chunk:
peak_counter=0
data_buff=base64.b64decode(x)
buff_size=len(data_buff)/4
unpack_format=">%dL" % buff_size
index=0
for y in struct.unpack(unpack_format,data_buff):
buff1=struct.pack("I",y)
buff2=struct.unpack("f",buff1)[0]
if (index % 2 == 0):
data[counter][1][peak_counter][0]=float(buff2)
else:
data[counter][1][peak_counter][1]=float(buff2)
peak_counter+=1
index+=1
print data[counter][1][10][0]
counter+=1
The program runs but only uses 100-110% of CPU (according to top) and once it should be finished it throws TypeError: map() takes at least 3 arguments (2 given) at me, could anyone with some more experience with multiprocess give me a hint as to what things to look out for (that could cause the TypeError)? What might be causing my low cpu usage?
-- Code after incorporating answers --
def decode((data,chunk,counter)):
print len(chunk), counter
for x in chunk:
peak_counter=0
data_buff=base64.b64decode(x)
buff_size=len(data_buff)/4
unpack_format=">%dL" % buff_size
index=0
for y in struct.unpack(unpack_format,data_buff):
buff1=struct.pack("I",y)
buff2=struct.unpack("f",buff1)[0]
if (index % 2 == 0):
data[counter][1][peak_counter][0]=float(buff2)
else:
data[counter][1][peak_counter][1]=float(buff2)
peak_counter+=1
index+=1
counter+=1
def numpy_array(data, peaks):
"""Fills the NumPy array 'data' with m/z-intensity values acquired
from b64 decoding and unpacking the binary string read from the
mzXML file, which is stored in the list 'peaks'.
The m/z values are assumed to be ordered without validating this
assumption.
Note: This function uses multi-processing
"""
processors=mp.cpu_count()
pool = mp.Pool(processes=processors)
chunk_size=int(len(peaks)/processors)
map_parameters=[]
for i in range(processors):
counter = i*chunk_size
chunk=peaks[i*chunk_size:(i+1)*chunk_size-1]
map_parameters.append((data,chunk,counter))
pool.map(decode,map_parameters)
This latest version 'works' so far that it fills the array in the processes (where the array contains values) but once all processes are done accessing the array yields zero values only because each process gets a local copy of the array.
Something like this should work
Note that pool.map takes a function and a list of parameters for that function for each call. In your original example you are just calling it in the numpy_array function.
The function must only have one argument, hence the packing of the arguments into a tuple and the rather odd looking double brackets in decode (which is called tuple unpacking).
def numpy_array(data, peaks):
processors=4
pool = mp.Pool(processes=processors)
chunk_size=len(data)/processors
print range(processors)
map_parameters = [] # new
for i in range(processors):
counter = i*chunk_size
chunk=peaks[i*chunk_size:(i+1)*chunk_size-1]
map_parameters.append((data,chunk,counter)) # new
pool.map(decode, map_parameters) # new
def decode((data,chunk,counter)): # changed
for x in chunk:
peak_counter=0
data_buff=base64.b64decode(x)
buff_size=len(data_buff)/4
unpack_format=">%dL" % buff_size
index=0
for y in struct.unpack(unpack_format,data_buff):
buff1=struct.pack("I",y)
buff2=struct.unpack("f",buff1)[0]
if (index % 2 == 0):
data[counter][1][peak_counter][0]=float(buff2)
else:
data[counter][1][peak_counter][1]=float(buff2)
peak_counter+=1
index+=1
print data[counter][1][10][0]
counter+=1
The bug is in your numpy_array function:
for i in range(processors):
counter = i*chunk_size
chunk=peaks[i*chunk_size:(i+1)*chunk_size-1]
pool.map(decode(data,chunk,counter))
The problem is that you're calling map sequentially so you're only running one process at a time. Also, I don't think you're calling map correctly as you're doing pool.map(f(*args)) when the signature is map(f, ['list', 'of', 'data']).
I would use a partial so that you don't create copies of data as I assume that array is quite large or could be larger in the future.
This should be:
import functools
decode_with_data = functools.partial(decode, data)
args = []
for i in range(processors):
counter = i * chunk_size
chunk = peaks[1*chunk_size:(i+1)*chunk_size-1]
args.append(chunk, counter)
pool.map(decode_with_data, args)

Continue creating list of the possible strings in python [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Using itertools.product and want to seed a value
I have this code, which generates a consistent list of strings.
import itertools
choices = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
for length in range(0,20):
for entry in itertools.product(choices, repeat = length):
string = ''.join(entry)
print string
I want to be able to continue running this script from the last known string. How is this possible to do?
import itertools
def choices():
choices = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
for length in range(0,20):
for entry in itertools.product(choices, repeat = length):
string = ''.join(entry)
yield string
choice_seq = choices()
print next(choice_seq)
print next(choice_seq)
The point of generators is that they carry their state with them.
Assuming you have the variable string set as the last known string (or '' to start at beginning):
import itertools
choices = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
for length in range(len(string), 20):
itr = itertools.product(choices, repeat = length)
if string != '' and length == len(string):
itr = itertools.dropwhile(tuple(string).__ne__, itr)
for entry in itr:
string = ''.join(entry)
print string
Note that the first element this will print is the last known string. If you want to skip the last known and start by printing the next string, you could do next(itr) inside of the if statement.
This assumes that you are trying to resume where you left off on multiple executions of a script, or other scenarios where a generator solution isn't applicable. If you can use a generator, you should.
Your "saved state" is just the current length, and the current state of the itertools.product. Both of those things can be pickled. So, here's some pseudocode:
import itertools
import pickle
choices = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
def tryWithStateSave(currentlength, currentproduct):
try:
for entry in currentproduct:
string = ''.join(entry)
print string
except KeyboardInterrupt:
pickle.dump((currentlength, currentproduct), <saved state file>)
raise
if <a saved state file exists>:
currentlength, currentproduct = pickle.load(<the saved state file>)
tryWithStateSave(currentlength, currentproduct)
currentlength += 1
else:
currentlength = 0
for length in range(currentlength+1,20):
tryWithStateSave(length, itertools.product(choices, repeat = length))

Python: Clever ways at string manipulation

I'm new to Python and am currently reading a chapter on String manipulation in "Dive into Python."
I was wondering what are some of the best (or most clever/creative) ways to do the following:
1) Extract from this string: "stackoverflow.com/questions/ask" the word 'questions.' I did string.split(/)[0]-- but that isn't very clever.
2) Find the longest palindrome in a given number or string
3) Starting with a given word (i.e. "cat")-- find all possible ways to get from that to another three- letter word ("dog"), changing one letter at a time such that each change in letters forms a new, valid word.
For example-- cat, cot, dot, dog
As personal exercise, here's to you, (hopefully) well commented code with some hints.
#!/usr/bin/env python2
# Let's take this string:
a = "palindnilddafa"
# I surround with a try/catch block, explanation following
try:
# In this loop I go from length of a minus 1 to 0.
# range can take 3 params: start, end, increment
# This way I start from the thow longest subsring,
# the one without the first char and without the last
# and go on this way
for i in range(len(a)-1, 0, -1):
# In this loop I want to know how many
# Palidnrome of i length I can do, that
# is len(a) - i, and I take all
# I start from the end to find the largest first
for j in range(len(a) - i):
# this is a little triky.
# string[start:end] is the slice operator
# as string are like arrays (but unmutable).
# So I take from j to j+i, all the offsets
# The result of "foo"[1:3] is "oo", to be clear.
# with string[::-1] you take all elements but in the
# reverse order
# The check string1 in string2 checks if string1 is a
# substring of string2
if a[j:j+i][::-1] in a:
# If it is I cannot break, 'couse I'll go on on the first
# cycle, so I rise an exception passing as argument the substring
# found
raise Exception(a[j:j+i][::-1])
# And then I catch the exception, carrying the message
# Which is the palindrome, and I print some info
except Exception as e:
# You can pass many things comma-separated to print (this is python2!)
print e, "is the longest palindrome of", a
# Or you can use printf formatting style
print "It's %d long and start from %d" % (len(str(e)), a.index(str(e)))
After the discussion, and I'm little sorry if it goes ot. I've written another implementation of palindrome-searcher, and if sberry2A can, I'd like to know the result of some benchmark tests!
Be aware, there are a lot of bugs (i guess) about pointers and the hard "+1 -1"-problem, but the idea is clear. Start from the middle and then expand until you can.
Here's the code:
#!/usr/bin/env python2
def check(s, i):
mid = s[i]
j = 1
try:
while s[i-j] == s[i+j]:
j += 1
except:
pass
return s[i-j+1:i+j]
def do_all(a):
pals = []
mlen = 0
for i in range(len(a)/2):
#print "check for", i
left = check(a, len(a)/2 + i)
mlen = max(mlen, len(left))
pals.append(left)
right = check(a, len(a)/2 - i)
mlen = max(mlen, len(right))
pals.append(right)
if mlen > max(2, i*2-1):
return left if len(left) > len(right) else right
string = "palindnilddafa"
print do_all(string)
No 3:
If your string is s:
max((j-i,s[i:j]) for i in range(len(s)-1) for j in range(i+2,len(s)+1) if s[i:j]==s[j-1:i-1:-1])[1]
will return the answer.
For #2 - How to find the longest palindrome in a given string?

Categories

Resources