Using Python, I am trying to write a script which will convert all typed characters into 'a' whenever you pressed space-bar. For example, i typed "python" and then space, then "python" will convert into "aaaaaa".
import argparse
import curses
import time
# Main Function
def main():
screen=curses.initscr()
curses.cbreak()
screen.keypad(1)
curses.echo()
str_txt=''
count = 0
while True:
s=screen.getch()
if s != ord(' ') and s != ord('\x1b') and s != curses.KEY_BACKSPACE and s != curses.KEY_ENTER:
str_txt += chr(int(s))
count+=1
if s == ord(' '):
dim = screen.getyx()
h = 'a'*len(str_txt)+' '
screen.addstr(dim[0],dim[1]-count-1, h)
count=0
str_txt=''
screen.refresh()
if s == curses.KEY_ENTER or s==10 or s==13:
dim = screen.getyx()
screen.move(dim[0]+1,0)
screen.refresh()
#if s == curses.KEY_BACKSPACE:
# dim = screen.getyx()
# screen.move(dim[0],dim[1])
# screen.refresh()
if s == ord('\x1b'):
curses.endwin()
break
if __name__ == "__main__":
main()
The above code works fine for 1st line, however, in the second line whenever i press spacebar, i am getting an error on line 22 saying "_curses.error: addstr() returned ERR"
EDITED:
When I change screen.addstr(dim[0],dim1-count-1, h) to screen.addstr(dim[0],dim1-count, h), the error is eliminated but the output is not what I want. I have attached to output for your reference.
if s != ord(' ') and s != ord('\x1b') and s != curses.KEY_BACKSPACE:
str_txt += chr(int(s))
count+=1
That if statement executes for the carriage return and\or newline char too I think, so I think your count is 1 over what you expected, due to the first line.
The addstr() returned ERR exception is because the cursor is being moved off screen (out of bound) due to:
screen.addstr(dim[0],dim[1]-count-1, h)
Since your count is +1 due to the carriage return (\r) at the end of your first line. The first if should check this and not increase the count. Try to add these checks s!=curses.KEY_ENTER and s!=10 and s!=13 to the first if and see if that helps. s!=10 will check the new line char (\n) (which may not be necessary in this case, but I am OCD). s!=13 will check the carriage return character (\r).
There was more than one place to improve the given example. Here is a revised version:
import curses
import time
# Main Function
def main():
screen=curses.initscr()
curses.cbreak()
screen.keypad(1)
curses.echo()
screen.scrollok(1)
str_txt=''
count = 0
while True:
dim = screen.getyx()
s=screen.getch()
if s != ord(' ') and s != ord('\x1b') and s != curses.KEY_BACKSPACE and s != curses.KEY_ENTER and s != 10 and s != 13:
str_txt += chr(int(s))
count+=1
if s == ord(' '):
if count > 0:
h = 'a'*len(str_txt)+' '
screen.addstr(dim[0],dim[1]-count, h)
count=0
str_txt=''
if s == curses.KEY_ENTER or s==10 or s==13:
if count > 0:
h = 'a'*len(str_txt)
screen.addstr(dim[0],dim[1]-count, h)
count=0
str_txt=''
screen.move(dim[0]+1,0)
count=0
str_txt=''
#if s == curses.KEY_BACKSPACE:
# dim = screen.getyx()
# screen.move(dim[0],dim[1])
# screen.refresh()
if s == ord('\x1b'):
curses.endwin()
break
if __name__ == "__main__":
main()
For example:
the screen.refresh calls are not needed, since screen.getch does that.
no check was made to ensure that the count was nonzero
pressing enter (or return) did not behave the "same" as space.
the movement after enter/return moved two lines rather than one.
the screen would not scroll at the end (partial fix in this example)
Related
I am building a Hangman game. The code that I wrote prints the 'updated target word' multiple times if a letter occurs multiple times.
Example: the target word is 'celebrate'. If I guess e then it prints
*e******
*e*e****
*e*e***e
I would like to avoid printing the first two printouts and only print the third and most updated version.
import random
import re
word_list = ["fireboard", "identical", "chocolate", "christmas", "beautiful", "happiness", "wednesday", "challenge", "celebrate"]
random_pick = random.choice(word_list)
random_pick_a = re.sub("[a-z]","*", random_pick)
random_pick_list_a = list(random_pick_a)
print(random_pick)
count = 0
def main_function():
global count
while count <= 9:
user_input = str(input("type a letter:"))
for i, c in enumerate(random_pick):
if c == user_input.casefold():
random_pick_list_a[i] = user_input.casefold()
random_pick_list_b = ''.join(random_pick_list_a)
print(random_pick_list_b)
if random_pick_list_b == random_pick:
print("done")
exit()
else:
continue
else:
if user_input.casefold() not in random_pick:
count = count+1
print(count)
if count == 10:
print("sorry")
exit()
main_function()
Disclaimer: I am in my first weeks of coding!
No need to str() the input(), it's already a string. So strip str(input("type a letter:")) to input("type a letter:").
No need in
else:
continue
it will continue even without it. Don't use globals, just move your count into main_function().
Don't do if count == 10, you're already doing it in while count <= 9.
As for your question - move the block
print(random_pick_list_b)
if random_pick_list_b == random_pick:
print("done")
exit()
out of the for-loop. So the whole thing would look like this:
def main_function():
count = 0
while count <= 4:
user_input = input("type a letter:")
for i, c in enumerate(random_pick):
if c == user_input.casefold():
random_pick_list_a[i] = user_input.casefold()
random_pick_list_b = ''.join(random_pick_list_a)
print(random_pick_list_b)
if random_pick_list_b == random_pick:
print("done")
exit()
else:
if user_input.casefold() not in random_pick:
count = count+1
print(count)
print("sorry")
You have:
print(random_pick_list_b)
Inside the for loop that is checking each character for the chosen letter. So, it prints out random_pick_list_b every time it finds a match.
Move it to right after the for loop if you want to do it one time when the checking is complete.
I would do this check once before the for loop.
Capturing the return key causes my loop to run twice, other keys only return once. Code below captures arrow keys and return key, with a counter to visualise how many times the code is run. Upon hitting the return key, the counter will advance by 2 instead of 1.
Using pycharm on mac os, with pycharm's environment set to emulate the terminal in the output console.
Thanks for any help.
import curses
from curses import wrapper
def main(stdscr):
# Clear screen
stdscr.clear()
stdscr.addstr("test")
stdscr.addstr(3, 3, "x")
stdscr.addstr(6, 6, "x")
stdscr.clear()
n = 0
while True:
c = stdscr.getch()
stdscr.clear()
stdscr.addstr(str(n))
stdscr.move(2, 0)
if c == curses.KEY_UP:
stdscr.addstr("up")
elif c == curses.KEY_DOWN:
stdscr.addstr("down")
elif c == curses.KEY_LEFT:
stdscr.addstr("left")
elif c == curses.KEY_RIGHT:
stdscr.addstr("right")
elif c == curses.KEY_ENTER or c == 10 or c == 13:
stdscr.addstr("enter")
n +=1
wrapper(main)
I'm trying to make a note application in Python using curses.
To the bottom left, should be a clock that updates every second.
The issue I now have is that it either has to sleep 1 second, or wait for input.
Is it possible to wait for input for 1 second and continue if no input it registered?
The reason I want to do this, is to prevent delay when moving around in the application.
I was thinking something like multi-threading would do the job, but got some issues there too.
This is the code I have so far:
#!/usr/bin/env python3
import curses
import os
import time
import datetime
import threading
def updateclock(stdscr):
while True:
height, width = stdscr.getmaxyx()
statusbarstr = datetime.datetime.now().strftime(' %A')[:4] + datetime.datetime.now().strftime(' %Y-%m-%d | %H:%M:%S')
stdscr.addstr(height-1, 0, statusbarstr)
time.sleep(1)
def draw_menu(stdscr):
k = 0
stdscr.clear()
stdscr.refresh()
threading.Thread(target=updateclock, args=stdscr).start()
cursor_y = 0
cursor_x = 0
while (k != ord('q')):
#while True:
stdscr.clear()
height, width = stdscr.getmaxyx()
stdscr.addstr(height//2, width//2, "Some text in the middle")
if k == curses.KEY_DOWN:
cursor_y = cursor_y + 1
elif k == curses.KEY_UP:
cursor_y = cursor_y - 1
elif k == curses.KEY_RIGHT:
cursor_x = cursor_x + 1
elif k == curses.KEY_LEFT:
cursor_x = cursor_x - 1
stdscr.refresh()
#time.sleep(1)
# Wait for next input
k = stdscr.getch()
curses.wrapper(draw_menu)
The code looks pretty messy, and it's the first time I've mainly focused on the curses function.
Is it possible to only wait for input k = stdscr.getch() for 1 second?
By default getch will block until you have a character input ready. If nodelay mode is True, then you will either get the character value (0-255) of the character that is ready, or you will get a -1 indicating that no character value is ready.
stdscr.nodelay(True) #Set nodelay to be True, it won't block anymore
k = stdscr.getch() #Either the next character of input, or -1
So I'm working on a dorky little algorithm-- the point of which is very dull. All I want to know is why I'm the letter "D" is attached to the output on the terminal.
When the answer is 1, it gives me back 1D. When the answer is 2, it spits out 2D, etc. Why?
I don't think it has much to do with the code. The code is below if you think so. It might have to do with the way I'm ending the input stream, which is by pressing Ctrl + D (mac). It's not giving me 1^D, it's giving me 1D. Why?
if __name__ == '__main__':
input = sys.stdin.read()
n, *data = map(int, input.split())
segments = list(map(lambda x: Segment(x[0], x[1]), zip(data[::2], data[1::2])))
points = optimal_points(segments)
print(int(len(points)))
for p in points:
print(p, end=' ')
It basically says, blah blah blah, get a list of numbers from the input stream / terminal, like this:
3
1 3
2 5
3 6
and do this to it:
def is_between(num_to_check, start, end):
return num_to_check >= start and num_to_check <= end
def optimal_points(segments):
end_first_segements = sorted(segments, key=attrgetter('end'))
count = 1
i = 1
current_end = end_first_segements[i -1 ].end
next_seg = end_first_segements[i]
end_points=[current_end]
while i <len(end_first_segements):
s = next_seg.start
e = next_seg.end
if(is_between(current_end, s, e)):
try:
i += 1
next_seg = end_first_segements[i]
except IndexError:
break;
else:
try:
count +=1
end_points.append(next_seg.end)
next_seg = end_first_segements[i+1]
current_end = next_seg.end
except IndexError:
break;
return end_points
See, there's nothing in the code that says, "hey, you should attach the letter D to the output for no reason".
I keep seeing this in other little programs I've done as well. So I think it has to do with the terminal verse any code that I write. Thoughts?
The entire .py file:
# Uses python3
import sys
from collections import namedtuple
from operator import attrgetter
Segment = namedtuple('Segment', 'start end')
def is_between(num_to_check, start, end):
return num_to_check >= start and num_to_check <= end
def optimal_points(segments):
end_first_segements = sorted(segments, key=attrgetter('end'))
count = 1
i = 1
current_end = end_first_segements[i -1 ].end
next_seg = end_first_segements[i]
end_points=[current_end]
while i <len(end_first_segements):
s = next_seg.start
e = next_seg.end
if(is_between(current_end, s, e)):
try:
i += 1
next_seg = end_first_segements[i]
except IndexError:
break;
else:
try:
count +=1
end_points.append(next_seg.end)
next_seg = end_first_segements[i+1]
current_end = next_seg.end
except IndexError:
break;
return end_points
if __name__ == '__main__':
input = sys.stdin.read()
n, *data = map(int, input.split())
segments = list(map(lambda x: Segment(x[0], x[1]), zip(data[::2], data[1::2])))
points = optimal_points(segments)
print(int(len(points)))
for p in points:
print(p, end=' ')
I cannot exactly reproduce your exact issue, but I do see something strange where the last line of output ends with a % sign in linux.
So my minimal working example to reproduce this behaviour is simply:
print('1 2', end=' ')
If I add an empty print statement at the end of the program this removes the behaviour, ie by adding print() at the very end.
def menurender():
global pos
global menulist
line1=menulist[pos]
if pos == len(menulist):
line2="back"
else:
line2=menulist[pos+1]
lcd.clear()
lcd.message(str(pos)+' ' +line1+ "\n"+str(pos+1)+' '+ line2)
In my block of code, I have a conditional in the menurender() function that checks to make sure that the list menulist has a valid index before referencing it, but i receive IndexError: list index out of range. I understand that the else statement is causing it, but I am confused because python shouldn't be executing it.
Full code
#!/usr/bin/python
#################################################
#IMPORTS#########################################
#################################################
from Adafruit_CharLCDPlate import Adafruit_CharLCDPlate
from Adafruit_I2C import Adafruit_I2C
#################################################
#OBJECTS#########################################
#################################################
lcd = Adafruit_CharLCDPlate()
#################################################
#VARIABLES#######################################
#################################################
#current button value
prevbutton = "NULL"
#for SELECT key and determining clicks
action = False
#variable for menu position
pos = 0
#on screen cursor 0 for top line, 1 for bottom line
cursor = 0
#Handles list structure and action when clicked
menulist= []
menulist.append("CPU")
menulist.append("RAM")
menulist.append("STORAGE")
menulist.append("NETWORK")
#get input from keys and return the currently pressed key
def buttonstatus():
bstatus = "Null"
if lcd.buttonPressed(lcd.SELECT) == True:
bstatus="SELECT"
elif lcd.buttonPressed(lcd.UP) == True:
bstatus="UP"
elif lcd.buttonPressed(lcd.DOWN) == True:
bstatus="DOWN"
elif lcd.buttonPressed(lcd.LEFT) == True:
bstatus="LEFT"
elif lcd.buttonPressed(lcd.RIGHT) == True:
bstatus="RIGHT"
return bstatus
#checks buttons pressed and converts that into action for top menu
def getinput():
global prevbutton
global pos
if buttonstatus() != prevbutton:
prevbutton = buttonstatus()
if buttonstatus() == "SELECT":
print "select"
elif buttonstatus() == "DOWN":
pos = pos + 1
elif buttonstatus() == "UP":
pos = pos -1
#elif buttonstatus() == "LEFT":
#print "left"
#elif buttonstatus() == "RIGHT":
#print "right"
#defines bounds for the position of the cursor
def posbounds():
global pos
global menulist
if pos < 0:
pos = 0
if pos == len(menulist):
pos = len(menulist)
#code renders the menu on the LCD
def menurender():
global pos
global menulist
line1=menulist[pos]
if pos == len(menulist):
line2="back"
else:
line2=menulist[pos+1]
lcd.clear()
lcd.message(str(pos)+' ' +line1+ "\n"+str(pos+1)+' '+ line2)
while True:
getinput()
posbounds()
menurender()
There are lots if values for pos != len(menulist) for which menulist[pos+1] gives an IndexError (including pos == len(menulist) - 1). You should check
if pos > (len(menulist) - 2):
You should probably change if pos == len(menulist): to if pos == len(menulist) - 1: if you want to check if pos is the index of the last element, or to if pos == len(menulist) - 2: if you want to check for the second to last element.
A better way of doing this may be by using a try ... except block.
try:
line2 = menulist[pos+1]
except IndexError:
# Out of range -> pos is greater than len(menulist)-1
line2 = 'back'
However - this doesn't seem lika a Pythonic way of doing anything at all in Python. Maybe you could tell us what you are trying to achieve, and someone here may propose a better way of doing it.