Progress bar in python curses - python

I have created a progress bar which updates itself after getting a percentage from another function but I'm having issues getting it to trail like this ############. Instead, it just move the '#' to the right until 100% is reached. Below is my code. The reason why it's this way is because I need the percentage to come externally so that the code can be reusable. please help me.
import curses
import time
curses.initscr()
def percentage():
loading = 0
while loading < 100:
loading += 1
time.sleep(0.03)
update_progress(loading)
def update_progress(progress):
win = curses.newwin(3, 32, 3, 30)
win.border(0)
rangex = (30 / float(100)) * progress
pos = int(rangex)
display = '#'
if pos != 0:
win.addstr(1, pos, "{}".format(display))
win.refresh()
percentage()

The problem is that you call newwin() every time, discarding the old win and replacing it with a new one in the same place. That new window only gets one character added to it, with the background being blank, so you see an advancing cursor instead of a bar.
One possible solution:
import curses
import time
curses.initscr()
def percentage():
win = curses.newwin(3, 32, 3, 30)
win.border(0)
loading = 0
while loading < 100:
loading += 1
time.sleep(0.03)
update_progress(win, loading)
def update_progress(win, progress):
rangex = (30 / float(100)) * progress
pos = int(rangex)
display = '#'
if pos != 0:
win.addstr(1, pos, "{}".format(display))
win.refresh()
percentage()
curses.endwin()
(Note the addition of a call to endwin() to restore the terminal to its normal mode.)
As far as leaving it onscreen after the program finishes, that's kind of outside the scope of curses. You can't really depend on any interaction between curses and stdio, sorry.

you can just switch the pos to multiply the display #:
if pos != 0:
win.addstr(1, 1, "{}".format(display*pos))
win.refresh()

Related

Tkinter Gif issue: black becomes white and previous frame doesn't disappear

I'm trying to code a desktop pet in python. For that I mainly used tkinter. I managed to create a window with two gifs which follow one another, but I have an issue with the display of the frames. As you can see in the picture, the black colour became white and sometimes it completely disappeared... In addition, for one of the gifs, two frames appear at the same time, the previous one staying instead of disappearing. 
This is my code :
import random
import tkinter as tk
import pyautogui
impath = '.\\Assets\\'
x = 1400
cycle = 0
check = 0
probality=10
idle_num = list(range(0,7))
cry_num = list(range(7,11))
event_number = random.randrange(probality)
#transfer random no. to event
def event(cycle, check, event_number, x):
if event_number in idle_num:
check = 0
print('idle')
window.after(500, update, cycle, check, event_number, x)
elif event_number in cry_num:
check = 1
window.after(500, update, cycle, check, event_number, x)
# make the gif work
def gif_work(cycle, frames, event_number):
if cycle < len(frames) -1:
cycle += 1
else:
cycle = 0
event_number = random.randrange(probality)
return cycle, event_number
def update(cycle, check, event_number, x):
# idle
if check == 0:
frame = idle[cycle]
cycle, event_number = gif_work(cycle, idle, event_number)
# cry
elif check == 1:
frame = cry[cycle]
cycle, event_number = gif_work(cycle, cry, event_number)
window.geometry('842x842+' + str(x // 2) + '+50')
#window.geometry('100x100+' + str(x // 2) + '+930')
label.configure(image=frame)
window.after(1, event, cycle, check, event_number, x)
window = tk.Tk()
# call buddy's action .gif to an array
idle = [tk.PhotoImage(file=impath + 'existerGif.gif', format='gif -index %i' % (i)) for i in range(3)] # idle gif , 4 frames
cry = [tk.PhotoImage(file=impath + 'placeGif.gif' , format='gif -index %i' % (i)) for i in range(8)] # sleep gif, 8 frames
window.config(highlightbackground='white')
label = tk.Label(window)
window.overrideredirect(True)
window.wm_attributes('-transparentcolor','blue')
label.pack()
#loop the program
window.after(1,update,cycle,check,event_number,x)
window.mainloop()
Maybe it's a format problem, I did my gifs on photoshop with png images.
Anyway if someone has an idea I'll be glad to hear it ^^
Have a nice day!

Python Curses addstr() error when keys are not pressed

I'm recreating this game for terminal in python using the curses library. Whenever a key is not pressed, stdscr.addstr() returns an error.
This seems to be because the cursor is off screen (in the bottom-right corner) during those frames, but I can't figure out why it moves at all. The canvas that I'm printing is always the same size (It consists entirely of spaces which are replaced when Apple or Basket objects are rendered). I tried decreasing the canvas size, but the cursor still goes to that corner. I also tried setting curses.leaveok(True), and the cursor seemed to be following the basket, but my logging proves that it is still going to that corner.
The file that uses curses:
import random
import time
import curses
from apple_season.basket import Basket
from apple_season.apple import Apple
from apple_season.coords import Canvas
def main(stdscr):
curses.curs_set(1) # so i can see where the cursor is
dims = [curses.COLS - 1, curses.LINES - 1] # pylint: disable=no-member
stdscr.nodelay(True)
stdscr.leaveok(True)
key=""
stdscr.clear()
canvas = Canvas(*dims)
basket = Basket(canvas)
apples = []
i = 0
def finished_apples():
if len(apples) <= 100:
return False
else:
for apple in apples:
if not apple.has_fallen:
return False
return True
while not finished_apples():
if len(apples) <= 100: # don't make more if there are already 100
# decide whether or not to create new apple (1/100 chance per frame)
num = random.randint(0, 100)
if num == 25:
apples.append(Apple(canvas))
try:
key = stdscr.getkey()
stdscr.clear()
# pick up keyboard inputs
# quit option
if str(key) == "q":
break
# right arrow
elif str(key) == "KEY_RIGHT":
basket.move('right')
# left arrow
elif str(key) == "KEY_LEFT":
basket.move('left')
except Exception:
pass
# render objects - alters canvas to display them
for apple in apples:
if apple.has_fallen:
apple.render()
else:
if '.0' not in str(i / 2): # check if i is even (drop every other frame)
apple.fall()
apple.render()
basket.render()
try:
stdscr.addstr(canvas.display)
except Exception:
pass
stdscr.refresh()
i += 1
time.sleep(0.01)
if __name__ == "__main__":
curses.wrapper(main)
(The code above runs fine, but it doesn't do anything when stdscr.addstr(canvas.display) doesn't work, which is whenever there are no keys pressed)
What's interesting is that this doesn't happen when it's just the basket or just the apples.
To see all of the code: https://github.com/lol-cubes/Terminal-Apple-Season/tree/soErrorCode.
I put the stdscr.clear() in the try and except block, which made it so that that code was only executed when a key was pressed, therefore overflowing the terminal because I was attempting to display multiple frames at once.

Python - Detect input within a terminal screensaver

I'm writing a code that basically runs without a loop for the sake of mobility and functionality. Let's say that when I run the program I use the -i option to continue using the python interpreter as it loads every function written. The thing is that I'm using a screensaver like function after the program print some basic information to make it look not so boring.
My question is: How can I maintain the screen saver running without blocking the interpreter prompt. I already wrote how to handle keyboard input to stop it using curses "getch()". I tried using threading but with no avail as it doesn't detect any keyboard input so it keeps running.
Is there any way I can get around this? How can I detect any input without blocking therefore retaining the interpreter prompt?
Right know it detects an input and raise a KeyboardException captured to make the screensaver stop.
Thanks in advance
I'm using this code with some added modifications:
Matrix-Curses
What I have so far:
Completed Code
This are the modifications done to the code:
def clear(int=None):
""" Clear Terminal Screen """
from subprocess import call
call('clear')
if int == 0:
exit()
def Matrix():
steps = 0
global scr
curses.curs_set(0)
curses.noecho()
if USE_COLORS:
curses.start_color()
curses.use_default_colors()
curses.init_pair(COLOR_CHAR_NORMAL, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(COLOR_CHAR_HIGHLIGHT, curses.COLOR_WHITE, curses.COLOR_GREEN)
curses.init_pair(COLOR_WINDOW, curses.COLOR_GREEN, curses.COLOR_GREEN)
height, width = scr.getmaxyx()
window_animation = None
lines = []
for i in range(DROPPING_CHARS):
l = FallingChar(width, MIN_SPEED, MAX_SPEED)
l.y = randint(0, height-2)
lines.append(l)
scr.refresh()
while True:
height, width = scr.getmaxyx()
for line in lines:
line.tick(scr, steps)
for i in range(RANDOM_CLEANUP):
x = randint(0, width-1)
y = randint(0, height-1)
scr.addstr(y, x, ' ')
if randint(0, WINDOW_CHANCE) == 1:
if window_animation is None:
#start window animation
line = random.choice(lines)
window_animation = WindowAnimation(line.x, line.y)
if not window_animation is None:
still_active = window_animation.tick(scr, steps)
if not still_active:
window_animation = None
scr.refresh()
time.sleep(SLEEP_MILLIS)
if SCREENSAVER_MODE:
key_pressed = scr.getch() != -1
if key_pressed:
raise KeyboardInterrupt
steps += 1
def ScreenSaver():
from time import sleep
from datetime import datetime as dt
global scr
TimeLimit = 10
StartTime = dt.now()
while True:
try:
sleep(1)
StopTime = (dt.now() - StartTime)
LastTime = StopTime.days*86400000 + StopTime.seconds*1000 + StopTime.microseconds/1000
if LastTime >= TimeLimit:
GetLocaleStatus = locale.getlocale()
locale.setlocale(locale.LC_ALL, '')
scr = curses.initscr()
scr.nodelay(1)
key_being_pressed = scr.getch() != -1
if not key_being_pressed:
try:
Matrix()
except KeyboardInterrupt:
TimeLimit = 30000
StartTime = dt.now()
raise KeyboardInterrupt
except KeyboardInterrupt:
curses.endwin()
curses.curs_set(1)
curses.reset_shell_mode()
curses.echo()
clear()
# return
main()

Individual keypress bug in python

The following code is a python sprinting game. It was posted as an answer to my previous post, by #mango You have to tap 'a' and 'd' as fast as you can to run 100 meters. However, there are a few bugs...
1) If you hold down 'a' and 'd' at the same time, the game is completed as fast as possible and defeats the point of the game.
2) Like in my previous post, I would like to incorporate a scoring system into this code, however, due to my level of skill and experience with python, unfortunately, I am unable to do so, and would appreciate and suggestions.
Many Thanks
Previous code:
import msvcrt
import time
high_score = 50
name = "no-one"
while True:
distance = int(0)
print("\n--------------------------------------------------------------")
print('\n\nWelcome to the 100m sprint, tap a and d rapidly to move!')
print('* = 10m')
print("\n**Current record: " + str(high_score) + "s, by: " + name)
print('\nPress enter to start')
input()
print('Ready...')
time.sleep(1)
print('GO!')
start_time = time.time()
while distance < 100:
k1 = msvcrt.getch().decode('ASCII')
if k1 == 'a':
k2 = msvcrt.getch().decode('ASCII')
if k2 == 'd':
distance += 1
if distance == 50:
print("* You're halfway there!")
elif distance % 10 == 0:
print('*')
fin_time = time.time() - start_time
fin_time = round(fin_time,2)
print('Well done you did it in...'+str(fin_time))
if fin_time < high_score:
print("Well done you've got a new high score ")
name = input("Please enter your name : ")
This is the code that I recieved as feedback, I have made a few changes, but I am struggling witht the bugs that I listed before.
1) If you hold down 'a' and 'd' at the same time, the game is completed as fast as possible and defeats the point of the game.
2) Like in my previous post, I would like to incorporate a scoring system into this code, however, due to my level of skill and experience with python, unfortunately, I am unable to do so, and would appreciate and suggestions.
Many Thanks
# these are the modules we'll need
import sys
import tkinter as tk
class SprintGame(object):
# this represents the distance we'll allow our player to run
# NOTE: the distance is in meters
distance = 100
# this represents the stride length of the players legs
# NOTE: basically how far the player can travel in one footstep
stride = 1.5
# this represents the last key the user has pressed
lastKey = ""
# this represents wether or not the race has been completed
completed = False
# this function initiates as soon as the "sprint" variable is defined
def __init__(self):
# create the tk window
self.root = tk.Tk()
# set the tk window size
self.root.geometry('600x400')
# set the tk window title
self.root.title("Sprinting Game")
# bind the keypress event to the self.keypress handler
self.root.bind('<KeyPress>', self.keypress)
# center the window
self.centerWindow(self.root)
# insert the components
self.insertComponents()
# initial distance notice
self.output("{0}m left to go!".format(self.distance))
# start out wonderful game
self.start()
# this function centers the window
def centerWindow(self, window):
window.update_idletasks()
# get the screen width
width = window.winfo_screenwidth()
# get the screen height
height = window.winfo_screenheight()
# get the screen size
size = tuple(int(_) for _ in window.geometry().split('+') [0].split('x'))
# get the screen's dimensions
x = (width / 2) - (size[0] / 2)
y = (height / 2) - (size[1] / 2)
# set the geometry
window.geometry("%dx%d+%d+%d" % (size + (x, y)))
# this function replaces the old text in the textbox with new text
def output(self, text = ""):
self.text.delete('1.0', tk.END)
self.text.insert("end", text)
# this function handles key presses inside the tkinter window
def keypress(self, event):
# get the key and pass it over to self.logic
self.logic(event.char)
# this function handles game logic
def logic(self, key):
# convert key to a lower case string
key = str(key).lower()
# let us know how far we've got left
if key == "l":
self.output("{0}m left to go!".format(self.distance))
# restart the race
if key == "r":
# clear the output box
self.text.delete('1.0', tk.END)
# reset the distance
self.distance = 100
# reset the stride
self.stride = 1.5
# reset the last key
self.lastKey = ""
# set race completed to false
self.completed = False
# output restart notice
self.output("The Race Has Been Restarted.")
# don't bother with logic if race is completed
if self.completed == True:
return False
# check if distance is less than or equal to zero (meaning the race is over)
if self.distance <= 0:
# set the "self.completed" variable to True
self.completed = True
# let us know we've completed the race
self.output("Well done, you've completed the race!")
# return true to stop the rest of the logic
return True
# convert the key to lower case
key = key.lower()
# this is the quit function
if key == "q":
# lights out...
sys.exit(0)
# check if the key is a
if key == "a":
# set the last key to a so that we can decrement the "distance"
# variable if it is pressed next
self.lastKey = "a"
# only bother with "d" keypresses if the last key was "a"
if self.lastKey == "a":
# capture the "d" keypress
if key == "d":
# decrement the "distance" variable
self.distance -= self.stride
# let us know how far into the game we are
self.output("{0}m left to go!".format(self.distance))
# this function inserts the components into the window
def insertComponents(self):
# this component contains all of our output
self.text = tk.Text(self.root, background='#d6d167', foreground='#222222', font=('Comic Sans MS', 12))
# lets insert out text component
self.text.pack()
# this function opens the window and starts the game
def start(self):
self.root.mainloop()
# create a new instance of the game
Game = SprintGame()

Update turtle/gui while waiting for input python

I'm making a chat program, but I have run across a problem: the screen only updates after input. I'm using turtle to show the chat (I know, turtle isn't really that good for this purpose, but it's very simple.)
This is the code in my loop:
while True:
ind = userlist.index(user)
if statlist[ind] == 'banned':
print('You have been banned.')
break
word = input('>>> ')
command(word)
if word != '':
chat = user + '(' + status + '): ' + word
update_room(chat)
refresh()
Pretty much everything can be ignored here, except the
word = input('>>> ')
and
refresh()
The refresh() is what updates the turtle room.
How could I make it so that it would print out new chat, even as the user is typing? Would 2 side-by-side while loops work?
I acknowledge that my program isn't that well organized and that to fix this I will probably have to rewrite this loop.
Note: I'd rather not import anything, but if an import is needed then it would be great if that module came preloaded with python.
Or another question: Is it possible to have 2 infinite while loops running side by side at the same time?
So I'm pretty new at python but I have an idea that will just be extremely repetitive. You need to first remove the input part and make a ton of functions like this:
def key_a:
global key_in
key_in = key_in + 'a'
def key_b:
global key_in
key_in = key_in + 'b'
def key_c:
global key_in
key_in = key_in + 'c'
Have it so if your input is enter, then it will set it to the word and reset the input variable.
def key_enter:
global key_in
global word
word = key_in
key_in = ''
Then bind your inputs (think of "win" as your window variable.)
win.listen()
win.onkeypress(key_a, 'a')
Also do the same for capital letters.
win.onkeypress(caps_key_a, 'A')
Please tell me if this helps.
Here's an improvement on TaCo's answer suggesting onkeypress which enables real-time typed user input so you can re-render even if the user is holding down keys.
My contribution is to use a loop that calls a general function so there's no need to manually create a separate function per key. I've also provided a minimal, runnable example which should be easily adaptable to a chat interface, typing game or other context.
I haven't attempted to handle every key and edge case, so you might want to dig into the list of Tk keys and make sure it works for your needs. Feel free to suggest an improvement if you encounter any odd behavior.
import turtle
from datetime import datetime
def tick():
current_time = datetime.now().strftime("%H:%M:%S")
turtle.clear()
turtle.goto(0, 50)
turtle.write(current_time, align="center", font=font)
turtle.goto(0, -50)
turtle.write(f"'{text}'", align="center", font=font)
turtle.update()
turtle.Screen().ontimer(tick, 1000 // 30)
def handle_keypress(key):
global text
text += key
def handle_backspace():
global text
text = text[:-1]
def add_key_handler(key):
turtle.Screen().onkeypress(lambda: handle_keypress(key), key)
font = "Courier New", 18, "normal"
text = ""
turtle.tracer(0)
turtle.penup()
turtle.hideturtle()
for i in range(33, 122):
if i != 45:
add_key_handler(chr(i))
turtle.Screen().onkeypress(handle_backspace, "BackSpace")
turtle.Screen().onkeypress(lambda: handle_keypress(" "), "space")
turtle.Screen().onkeypress(lambda: handle_keypress("-"), "minus")
turtle.listen()
tick()
turtle.exitonclick()
Reference: Is there a complete list of key event names used by turtle-graphics?

Categories