How to get rid of frame 'blinking' effect with standard libs? - python

I am currently playing with some cmd/prompt animations/graphics:
import os
import time
def printFrame(timeout, count):
os.system('cls')
l=0
while True:
for k in range(0,9):
for i in range(0,9):
for j in range(0,9):
if j == k and i != 4:
print("|", end="", flush=True)
elif j !=k and i == 4:
print("-", end="", flush=True)
elif j ==k and i == 4:
print("+", end="", flush=True)
else:
print("O", end="", flush=True)
print("")
time.sleep(timeout)
os.system('cls')
l += 1
if l > count:
break
if __name__ == "__main__":
printFrame(0.08, 2)
and i want to get rid of frame blinking - especialy visible in first line, my idea was to use second printing thread:
def printFrame(timeout, count):
#print from example1
def printFrameTwo(timeout, count):
#print from example1 without os.system('cls')
if __name__ == "__main__":
p1 = threading.Thread(target = printFrame, args = (0.08, 2))
p2 = threading.Thread(target = printFrameTwo, args = (0.08, 2))
p1.start()
p2.start()
but the effect was rather disappointing - problems with synchronization and first line still very blinky, second idea was to use 'predefined frames' - but its not very educating - the bonus here is that I can print whole line at once, but still effect is not as expected, third (most promising) idea is to only change necessary 'pixels'/chars in frame - but here I need to move in frame between lines! and curses is not working on windows (at least not in standard). Do you maybe have some ideas how to bite it? (windows, standard libraries) maybe how to speed up 'os.system('cls')'?

I figured it out... You can use ANSI codes to move the cursor then clear the lines without any BLINK!
print('\033[4A\033[2K', end='')
\033[4A Moves the cursor 4 lines up (\033[{lines}A you can replace lines with however many you need) \033[2K Clears all those lines without the screen blinking. You can use it in a simple typewrite function that needs a constant message or a box around it like this:
from time import sleep
def typewrite(text: str):
lines = text.split('\n')
for line in lines:
display = ''
for char in line:
display += char
print(f'╭─ SOME MESSAGE OR SOMEONES NAME ────────────────────────────────────────────╮')
print(f'│ {display:74} │') # :74 is the same as ' ' * 74
print(f'╰────────────────────────────────────────────────────────────────────────────╯')
sleep(0.05)
print('\033[3A\033[2K', end='')
The only problem with this is that the top line is blinking. To fix this all we need to do is to add a empty line that is blinking so the user cant see it. We also move the cursor up from 3 to 4 lines.
def typewrite(text: str):
lines = text.split('\n')
for line in lines:
display = ''
for char in line:
display += char
print('')
print(f'╭─ SOME MESSAGE OR SOMEONES NAME ────────────────────────────────────────────╮')
print(f'│ {display:74} │') # :74 is the same as ' ' * 74
print(f'╰────────────────────────────────────────────────────────────────────────────╯')
sleep(0.05)
print('\033[4A\033[2K', end='')
To make this into your code just print your text and add a print('') at the start. Then use this print('\033[4A\033[2K', end='') but change the 4 to however many lines that you printed including the print(''). Then it should work without blinking. You can put print('\033[4B', end='') at the end which just moves the cursor back up.
If you want to hide the cursor you can use this gibberish or make the cursor the same color as the background:
import ctypes
if os.name == 'nt':
class _CursorInfo(ctypes.Structure):
_fields_ = [("size", ctypes.c_int),
("visible", ctypes.c_byte)]
def hide_cursor() -> None:
if os.name == 'nt':
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11)
ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci))
ci.visible = False
ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci))
def show_cursor() -> None:
if os.name == 'nt':
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11)
ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci))
ci.visible = True
ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci))
Note: All of this is still new to me so I am still testing this out to fully understand it.

Related

How to make keystroke auto completion in Python?

I am working on making a function which does auto completion based on keystrokes.
Like, in google.com when I hit 'a' in search bar, it shows the related search words such as amazon, amazon prime, airbnb...
I am trying make this function using Python and my CSV file based on detecting keystrokes.
There are some words in the CSV file such as Behavior, Building, Chemistry, History, Manufacturing, Mathematics.
The structure of my code consists of
while # infinitely looping in order to keep detecting keystrokes
for word in data
for char in range(numOfKeyStrokes)
The problem is, when I hit 'c', it is supposed to print out chemistry and it works great.
But when I hit 'h' after I already hit 'c', there is no response.
I want to make this printing out "chemistry" again.
I think when I hit 'h', the compiler doesn't go into "for word in data" statement but I don't know why.
There are some junk print outs in order to detect where the problem is.
Here is my code. Can someone help my problem?
import keyboard
import csv
import time
global numOfKeyStrokes #In order to compare each elemtn's letter one by one with keystroke
numOfKeyStrokes = 0
def keyStroke():
temp = keyboard.read_key()
time.sleep(0.1)
global numOfKeyStrokes
numOfKeyStrokes += 1
return temp
def init():
f = open('./DataBase.csv')
data = csv.reader(f)
getKeyStroke = []
matchingFlag = False
while True: # looping in order to keep getting keystrokes
global numOfKeyStrokes
getKeyStroke.append(keyStroke())
print(getKeyStroke)
print("numOfKeyStrokes ", numOfKeyStrokes)
for word in data: # each row is an each cell in a csv file
matchingFlag = False
for char in range(numOfKeyStrokes): # each x is an each letter in an each cell
print("word[9][char]==getKeyStroke[numOfKeyStrokes-1]", word[9] [char].lower(),",",getKeyStroke[numOfKeyStrokes-1])
if word[9][char].lower() == getKeyStroke[numOfKeyStrokes-1]:
print("char matched")
matchingFlag = True
break
else:
matchingFlag = False
break
if matchingFlag == True : print(word[9])
f.close()
init()
The reason your code only works one time is because you open the csv file once at the beginning of your init function.
When you pass it to the csv.reader and iterate over the entire file, you consume the resource. When you go for the next iteration the file/reader is empty and so your for word in data loop will not run.
I've refactored your program rather significantly, so I don't necessarily know if this still achieves your desired behaviour, especially without knowing exactly how your csv is setup, but I think you were looking for something like:
import csv
import time
import keyboard
def key_stroke_gen():
num_of_key_strokes = 0
while True:
num_of_key_strokes += 1
yield num_of_key_strokes, keyboard.read_key()
time.sleep(0.1)
def init():
get_key_stroke = []
words = []
with open('./DataBase.csv') as f:
data = csv.reader(f)
for row in data:
for word in row:
words.append(word.strip().lower())
for num_of_key_strokes, key_stroke in key_stroke_gen():
get_key_stroke.append(key_stroke)
for word in words:
if word[:num_of_key_strokes] == ''.join(get_key_stroke):
print(word)
if __name__ == '__main__':
init()
Generating the search_str directly in the generator might be a better approach overall, since it's easier to manage the building of the search string in one place and have the processing code separate.
import csv
import time
import keyboard
def search_strs():
search_str = ""
while True:
time.sleep(0.1)
key_press = keyboard.read_key()
if key_press == "backspace" and search_str:
search_str = search_str[:-1]
elif len(key_press) == 1 and ('a' <= key_press <= 'z'):
search_str += keyboard.read_key()
else:
continue
yield search_str
def init():
words = []
with open('./DataBase.csv') as f:
data = csv.reader(f)
for row in data:
for word in row:
words.append(word.strip().lower())
for search_str in search_strs():
for word in words:
if word[:len(search_str)] == search_str:
print(word)
if __name__ == '__main__':
init()

What is the best way to stop a loop from outside it?

I have a python script that prints a spinner. This spinner, hopefully, will last until stopped.
spinnerFrames = [
"/",
"-",
"\\",
"|",
]
def spinner():
i = 0
while True:
clearScreen() #function to clear the screen
print(spinnerFrames[i])
i = i + 1
if (i == 3):
i = 0
sleep(0.15)
spinner()
sleep(3)
# break out here
print("Done!")
I know you can do sys.stdout.write() and then only delete that line, but that's beside the point.
I can't figure out the best way to stop the loop and exit the function. (To continue on in my code)I'd like to be able to break from the loop down where you call it, as I hope to make this a Pip package.
This, I assume is possible, though I don't know how to do it. Thanks for your help!
You need to run it asynchronously, like how the multiprocessing library allows you to do. When you create a separate thread, you'll be left with a handle on it that you can use to kill it when you want it to stop.
from multiprocessing import Process
from time import sleep
spinnerFrames = [
"/",
"-",
"\\",
"|",
]
def spinner():
i = 0
while True:
print(spinnerFrames[i], end='\r')
i = i + 1
if (i == 3):
i = 0
sleep(0.15)
if __name__ == '__main__':
p = Process(target=spinner)
p.start()
sleep(3)
p.terminate()
print("Done!")
Here is a reference implementation from one of my projects. It prints dots instead of a spinner, but it is trivial to change:
import threading
import time
def indicate_wait(func):
active = threading.Lock()
def dot_printer():
while active.locked():
print('.', end='', flush=True)
time.sleep(1)
def wrapper(*args, **kwargs):
t = threading.Thread(target=dot_printer)
active.acquire()
t.start()
res = func(*args, **kwargs)
active.release()
return res
return wrapper
Example:
#indicate_wait
def test():
time.sleep(5)
record when it started, then break loop if current time - start time >
duration.
import time
spinnerFrames = [
"/",
"-",
"\\",
"|",
]
def spinner():
i = 0
startTime = time.time()# record starting time
duration = 3
while True:
clearScreen() #function to clear the screen
print(spinnerFrames[i])
i = i + 1
if (i == 3):
i = 0
if time.time() - startTime > duration:
break
sleep(0.15)
spinner()
print("Done!")

Progress dots with a Thread in Python

I am trying to create a thread in Python that will poll some server as long as it won't get proper answer (HTTP GET). In order to provide convenient text UI I want to print progress dots. Another dot with every connection attempt until it finish (or just another dot with every another second of waiting).
I have found something like this: http://code.activestate.com/recipes/535141-console-progress-dots-using-threads-and-a-context-/
In this example we have context manager:
with Ticker("A test"):
time.sleep(10)
I am not sure if I understand that properly. I would like to do something like:
with Ticker("A test: "):
result = -1
while result != 0:
result = poll_server()
print "Finished."
But this does not work. Any ideas?
Cheers
Python buffers your output, so many dots will appear at once. One way around that is to import sys and use that: whenever you want to print a dot, say:
sys.stdout.write(".")
sys.stdout.flush()
The flush makes the dot appear immediately.
#! /usr/bin/python3
import sys
import time
def progress(message):
i = 0
while True:
dots = ""
i = (i % 3) + 1
dots += "." * i + " " * (3 - i)
sys.stdout.write("\r{}".format(message + dots))
sys.stdout.flush()
i += 1
time.sleep(0.3)
if __name__ == "__main__":
progress("Waiting")
More useful example:
#! /usr/bin/python3
import sys
import time
def progress_gen(message):
i = 0
while True:
for x in range(0, 4):
dots = "." * x
sys.stdout.write("{}\r".format(message + dots))
i += 1
time.sleep(0.5)
sys.stdout.write("\033[K")
yield
if __name__ == "__main__":
p = progress_gen("Waiting")
for x in range(1, 100):
next(p)
if x == 3:
break
print("Finished")
You can test it online: https://repl.it/#binbrayer/DotsProgress

Python3: print(somestring,end='\r', flush=True) shows nothing

I'm writing a progress bar as this How to animate the command line? suggests. I use Pycharm and run this file in Run Tool Window.
import time
def show_Remaining_Time(time_delta):
print('Time Remaining: %d' % time_delta, end='\r', flush=True)
if __name__ == '__main__':
count = 0
while True:
show_Remaining_Time(count)
count += 1
time.sleep(1)
However, the code displays nothing if I run this .py file. What am I doing wrong?
I tried Jogger's suggest but it's still not working if I use print function.
However the following script works as expected.
import time
import sys
def show_Remaining_Time(time_delta):
sys.stdout.write('\rtime: %d' % time_delta) # Doesn't work if I use 'time: %d\r'
sys.stdout.flush()
if __name__ == '__main__':
count = 0
while True:
show_Remaining_Time(count)
count += 1
time.sleep(1)
I have 2 questions now:
Why stdout works but print() not.
Why the How to animate the command line? suggests append \r to the end while I have to write it at the start in my case?
The problem is that the '\r' at the end clears the line that you just printed, what about?
import time
def show_Remaining_Time(time_delta):
print("\r", end='')
print('Time Remaining: %d' % time_delta, end='', flush=True)
if __name__ == '__main__':
count = 0
while True:
show_Remaining_Time(count)
count += 1
time.sleep(1)
In this way, you clear the line first, and then print the desired display, keeping it in screen for the duration of the sleep.
NOTE: The code above was modified to add the end='' as suggested in the comments for the code to work properly in some platforms. Thanks to other readers for helping to craft a more complete answer.
This method can print in the same command line:
import time
def show_Remaining_Time(time_delta):
print(' \r%d:Time Remaining' % time_delta, end = '',flush=False)
if __name__ == '__main__':
count = 0
while True and count < 10:
show_Remaining_Time(count)
count += 1
time.sleep(1)

raw_input and timeout [duplicate]

This question already has answers here:
Keyboard input with timeout?
(28 answers)
Closed 8 years ago.
I want to do a raw_input('Enter something: .'). I want it to sleep for 3 seconds and if there's no input, then cancel the prompt and run the rest of the code. Then the code loops and implements the raw_input again. I also want it to break if the user inputs something like 'q'.
There's an easy solution that doesn't use threads (at least not explicitly): use select to know when there's something to be read from stdin:
import sys
from select import select
timeout = 10
print "Enter something:",
rlist, _, _ = select([sys.stdin], [], [], timeout)
if rlist:
s = sys.stdin.readline()
print s
else:
print "No input. Moving on..."
Edit[0]: apparently this won't work on Windows, since the underlying implementation of select() requires a socket, and sys.stdin isn't. Thanks for the heads-up, #Fookatchu.
If you're working on Windows you can try the following:
import sys, time, msvcrt
def readInput( caption, default, timeout = 5):
start_time = time.time()
sys.stdout.write('%s(%s):'%(caption, default));
input = ''
while True:
if msvcrt.kbhit():
chr = msvcrt.getche()
if ord(chr) == 13: # enter_key
break
elif ord(chr) >= 32: #space_char
input += chr
if len(input) == 0 and (time.time() - start_time) > timeout:
break
print '' # needed to move to next line
if len(input) > 0:
return input
else:
return default
# and some examples of usage
ans = readInput('Please type a name', 'john')
print 'The name is %s' % ans
ans = readInput('Please enter a number', 10 )
print 'The number is %s' % ans
I have some code which makes a countdown app with a tkinter entry box and button so they can enter something and hit the button, if the timer runs out the tkinter window closes and tells them they ran out of time.
I think most other solutions to this problem don't have a window which pops up so thought id add to the list :)
with raw_input() or input(), it isn't possible as it stops at the input section, until it receives input, then it carries on...
I have taken some code from the following link:
Making a countdown timer with Python and Tkinter?
I used Brian Oakley's answer to this problem and added the entrybox etc.
import tkinter as tk
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
def well():
whatis = entrybox.get()
if whatis == "": # Here you can check for what the input should be, e.g. letters only etc.
print ("You didn't enter anything...")
else:
print ("AWESOME WORK DUDE")
app.destroy()
global label2
label2 = tk.Button(text = "quick, enter something and click here (the countdown timer is below)", command = well)
label2.pack()
entrybox = tk.Entry()
entrybox.pack()
self.label = tk.Label(self, text="", width=10)
self.label.pack()
self.remaining = 0
self.countdown(10)
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
app.destroy()
print ("OUT OF TIME")
else:
self.label.configure(text="%d" % self.remaining)
self.remaining = self.remaining - 1
self.after(1000, self.countdown)
if __name__ == "__main__":
app = ExampleApp()
app.mainloop()
I know what I added was a bit lazy but it works and it is an example only
This code works for Windows with Pyscripter 3.3
For rbp's answer:
To account for input equal to a Carriage Return simply add a nested condition:
if rlist:
s = sys.stdin.readline()
print s
if s == '':
s = pycreatordefaultvalue

Categories