Can you use curses syntax with normal python functions? - python

I want to create a software program that has a main menu with options that are navigable with arrow keys (I have the code for this). When the user clicks on the option they want, it loads in the associated function. Can I use normal python syntax as the function and have the curses syntax call the function? How can I do this?
import curses
menu = ['Set Wallet Address', 'Start BTC Miner', 'Currency', 'Exit']
def clr():
print("\n"*60)
#center everything
def print_menu(stdscr, selected_row_idx):
stdscr.clear()
h, w = stdscr.getmaxyx()
for idx, row in enumerate(menu):
x = w//2 - len(row)//2
y = h//2 - len(menu)//2 + idx
if idx == selected_row_idx:
stdscr.attron(curses.color_pair(1))
stdscr.addstr(y, x, row)
stdscr.attroff(curses.color_pair(1))
else:
stdscr.addstr(y, x, row)
stdscr.refresh()
#print everything in center
def print_center(stdscr, text):
stdscr.clear()
h, w = stdscr.getmaxyx()
x = w//2 - len(text)//2
y = h//2
stdscr.addstr(y, x, text)
stdscr.refresh()
def test(stdscr):
clr()
print_center(stdscr, 'Hello')
def main(stdscr):
# turn off cursor blinking
curses.curs_set(0)
# color scheme for selected row
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
# specify the current selected row
current_row = 0
# print the menu
print_menu(stdscr, current_row)
while 1:
key = stdscr.getch()
if key == curses.KEY_UP and current_row > 0:
current_row -= 1
elif key == curses.KEY_DOWN and current_row < len(menu)-1:
current_row += 1
elif key == curses.KEY_ENTER or key in [10, 13]:
test(stdscr)
stdscr.getch()
# if user selected last row, exit the program
if current_row == len(menu)-1:
break
print_menu(stdscr, current_row)
curses.wrapper(main)

Lots of ways to do this but the statements "can you use curses syntax" and "have the curses syntax call the function" doesn't make sense? There's only Python syntax and some no-so-magic from the curses module's wrapper function that you pass main to which the wrapped calls by passing it a scr that curses handles/wraps IOT cleanly exit. curses and _curses are just modules based on C's ncurses.
I think you're on the right track though so your goal is achievable and while I didn't run your code it looks close as well. Yes you can write a basic python function. You could use the same default scr or you could also spawn a new initscr or derived scr (see the Python curses docs). I would start small and bind a single key to each option to ensure your callbacks work first. So if you bind 'h' to a help screen, just read getch and compare ord/char 'h', and if matches call a help() function (or create a callback of sorts by passing the function help to some other function to execute/wrap) so as to redraw the scr with help info. Then later work in cursors as you'll need to remember the cursor state and integrate with a list of callback functions.

Related

Is there a focus changed event in urwid?

Is it possible to track the change of the highlighted item in an urwid.ListBox object? Or even via a ListWalker object?
I would like to call a callback when the user moves from one item to another using the arrow keys [🠉], [🠋], not when the user hits [Enter] on one item.
After some research and experimentation, it's possible to do this by registering the modified signal with the ListWalker object.
import urwid
def callback():
index = str(listBox.get_focus()[1])
debug.set_text("Index of selected item: " + index)
debug = urwid.Text("Debug")
captions = "A B C D E F".split()
items = [urwid.Button(caption) for caption in captions]
walker = urwid.SimpleListWalker(items)
listBox = urwid.ListBox(walker)
urwid.connect_signal(walker, "modified", callback)
frame = urwid.Frame(body=listBox, header=debug)
urwid.MainLoop(frame).run()
Reference: Urwid > Signal functions > connect

PyUserInput - stopping PyMouseEvent

I am trying to build a program whose first part is saving user clicks for calibration purposes.
I am using PyUserInput for this (on Python 3.4.3) and following their tutorial on GitHub. Currently, this is my code:
from pymouse import PyMouseEvent
positions = []
class Click(PyMouseEvent):
def __init__(self):
PyMouseEvent.__init__(self)
def click(self, x, y, button, press):
if button == 1:
if press:
print('Click! X=%d - Y=%d' % (x, y))
positions.append((x, y))
print(positions)
if len(positions) >= 4:
self.stop()
C = Click()
C.run()
print('Calibrated')
The problem I'm running into is that, even after the PyMouseEvent stops (when position reaches 4 values), I cannot run anything after it (i.e. 'Calibrated' does not print).
How can I continue to run scripts after calling stop() on PyMouseEvent?
I appreciate any help.
This is more of a workaround than a "legal" solution. The idea is to replace stop() method calls with raising exception and wrapping run() method call inside try: except: statements. I recommend making your own exception by subclassing Exception for better understanding of the code.
Here:
from pymouse import PyMouseEvent
positions = []
class ListenInterrupt(Exception):
pass
class Click(PyMouseEvent):
def __init__(self):
PyMouseEvent.__init__(self)
return
def click(self, x, y, button, press):
if button == 1:
if press:
print('Click! X=%d - Y=%d' % (x, y))
positions.append((x, y))
print(positions)
if len(positions) >= 4:
raise ListenInterrupt("Calibrated.")
return
C = Click()
try:
C.run()
except ListenInterrupt as e:
print(e.args[0])
This should stop event listener. If you wish to recalibrate using this approach, simply reruning same instance of listener won't work. The solution is deleting current instance of class Click and instantiating new one each time.
del C
C = Click()

Right way to continuously update color of Tkinter Canvas item

I am trying to figure out how to change a rectangle's color continuously, with a second between each change. Right now I have this simple function which makes a window with a square above a button, that changes the color of the square after every button click:
def junk():
def random_color():
red = int(random.random()*256)
green = int(random.random()*256)
blue = int(random.random()*256)
return '#' + ('{:0>#02X}'*3).format(red,green,blue)
def change_color():
c.itemconfig(r, fill=random_color())
x = Tkinter.Tk()
c = Tkinter.Canvas(master=x)
c['width'] = 400; c['height'] = 400
r = c.create_rectangle(0,0,400,400)
b = Tkinter.Button(master=x, command=change_color)
b['text'] = 'change color'
c.pack(); b.pack(); x.mainloop()
What I want is to be able to click once, and then have the colors change automatically. I know I want to use a CheckButton instead of a Button for this, so that one click will start the loop, and and the next click will stop it.
Also, this is not how I am structuring my "real" code, this is how I am testing from the IDLE shell. Defining the helper functions inside the junk function makes it easy to get at all the relevant code at once, without having the bloat of a full class. So please don't give me comments on style, this is quick and dirty on purpose.
TL;DR I'm not sure how to get a continuous loop running to change the color, while being able to start and stop the loop with a button click.
I figured it out. Before I show my solution, I want to correct a mistaken statement I made above: I don't to use a Checkbutton to make this work. I can make a normal button into a toggle button by changing the 'relief' option of the button. Here is my solution:
def junk():
def color_poll():
global alarm
c.itemconfig(r, fill=random_color())
if keep_going:
alarm = c.after(1000, color_poll)
def change_color():
global keep_going, alarm
if not keep_going:
keep_going = True
b['text']='STOP';b['fg']='red';b['relief']=Tkinter.SUNKEN
color_poll()
else:
keep_going = False; c.after_cancel(alarm); alarm = None
b['text']='GO';b['fg']='green';b['relief']=Tkinter.RAISED
x = Tkinter.Tk()
c = Tkinter.Canvas(master=x)
c['width'] = 400; c['height'] = 400
r = c.create_rectangle(0,0,400,400)
global keep_going, alarm
keep_going = False; alarm = None
b = Tkinter.Button(master=x, command=change_color)
b['text'] = 'GO';b['fg']='green';b['font']='Arial 16';b['relief']=Tkinter.RAISED
c.pack(); b.pack(); x.mainloop()
I'm using the same random_color function, but I moved it out because it out of the junk function because it didn't need to be there.

How do I accept input from arrow keys, or accept directional input?

This may be an xy problem, but I'm trying to to build a kernel based text editor, similar to vim or nano, and I know how to use the escape chars to clear the screen, then reprint, I can have it accept characters, but I'm not sure how to get it to accept arrow inputs for navigation. I thought there were ASCII values for them, but apparently not. Is there a way to use the arrows, or do I have to make a navigation mode and insert mode like vim?
I've also briefly played with curses, but that was prohibitive because, as I understood, a whole new window had to be opened for it and this is not compatible with the vision of a single terminal window that I had.
Please note that curses does not work because it cleares the window, which I don't want.
curses is exactly what you want. In fact I believe vim implements its interface with curses.
Try to put the following code into a file called test_curses.py:
import curses
screen = curses.initscr()
screen.addstr("Hello World!!!")
screen.refresh()
screen.getch()
curses.endwin()
Now open a terminal (not IDLE! a real terminal!) and run it via:
python test_curses.py
You should see that the terminal was cleared and an Hello World!!! writing appeared. Press any key and the program will stop, restoring the old terminal contents.
Note that the curses library isn't as easy and "user-friendly" as you may be accustomed to. I suggest reading the tutorial (unfortunately for the C language, but the python interface is mostly the same)
I wound up using the code from this question, and modifying the __init__ statement so that it accepted up to 3 characters in a list.
import sys
class _Getch:
"""Gets a single character from standard input. Does not echo to the
screen."""
def __init__(self):
self.impl = _GetchUnix()
def __call__(self):# return self.impl()
charlist = []
counter = 0
for i in range(3):
try:charlist.append(self.impl())
except:pass
if charlist[i] not in [chr(27),chr(91)]:#TODO sort out escape vs arrow duh use len()
break
if len(charlist) > 1:
if charlist == [chr(27),chr(27)]:
break
if len(charlist) == 3:
if charlist[2] == 'a'
return 'u-arr'
if charlist[2] == 'b'
return 'd-arr'
if charlist[2] == 'c'
return 'r-arr'
if charlist[2] == 'd'
return 'l-arr'
if len(charlist == 2):
if charlist == [chr(27),chr(27)]
return chr(27)
if len(charlist == 1)
return charlist[0]
return ''
class _GetchUnix:
def __init__(self):
import tty, sys
def __call__(self):
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
This allowed me to get arrow keys as well as all other characters and escape sequences from the keyboard for the editor. It made the "Getch" class not strictly a get char clone because it returns a string, but it wound up being much more useful.
I know that I'm late to the party, but I really liked click package mentioned by #elbaschid. I don't know why he wasn't upvoted - maybe because his example doesn't show how to handle specifically cursor keys.
Here is my $0.02 on that:
#!/usr/bin/python
import click
printable = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?#[\\]^_`{|}~'
while True:
click.echo('Continue? [yn] ', nl=False)
c = click.getchar()
click.echo()
if c == 'y':
click.echo('We will go on')
elif c == 'n':
click.echo('Abort!')
break
elif c == '\x1b[D':
click.echo('Left arrow <-')
elif c == '\x1b[C':
click.echo('Right arrow ->')
else:
click.echo('Invalid input :(')
click.echo('You pressed: "' + ''.join([ '\\'+hex(ord(i))[1:] if i not in printable else i for i in c ]) +'"' )
This handles cursor keys and as a bonus prints py-string representation of any keyboard shortcut it doesn't yet recognize. For example Ctrl-s is "\x13". You can later use it inside another
elif c == ??
I've tried to add edit to #elbaschid answer but it was rejected ¯\_(ツ)_/¯. Please give him credit if you also like my answer
Awesome library for quick command-line prototyping.
The Python package click used for building commandline clients also comes with an implementation that allows you to get the key press events:
import click
key = click.getchar()
It returns the key representation as Unicode character and "things like arrow keys will show up in the platform’s native escape format.".
This is the example taken straight from the click documentation on getchar:
import click
click.echo('Continue? [yn] ', nl=False)
c = click.getchar()
click.echo()
if c == 'y':
click.echo('We will go on')
elif c == 'n':
click.echo('Abort!')
else:
click.echo('Invalid input :(')
To Perform desired Action on Arrow key or Any other key as it pressed
# key_event_handler.py
import sys
import select
import pty
import os
import time
import fcntl
import tty
import termios
def __select( iwtd, owtd, ewtd, timeout=None):
'''This is a wrapper around select.select() that ignores signals. If
select.select raises a select.error exception and errno is an EINTR
error then it is ignored. Mainly this is used to ignore sigwinch
(terminal resize). '''
# if select() is interrupted by a signal (errno==EINTR) then
# we loop back and enter the select() again.
if timeout is not None:
end_time = time.time() + timeout
while True:
try:
return select.select(iwtd, owtd, ewtd, timeout)
except select.error:
err = sys.exc_info()[1]
if err.args[0] == errno.EINTR:
# if we loop back we have to subtract the
# amount of time we already waited.
if timeout is not None:
timeout = end_time - time.time()
if timeout < 0:
return([], [], [])
else:
# something else caused the select.error, so
# this actually is an exception.
raise
STDIN_FILENO=pty.STDIN_FILENO
STDOUT_FILENO=pty.STDOUT_FILENO
string_type=bytes
sys.stdout.write(string_type())
sys.stdout.flush()
buffer = string_type()
mode = tty.tcgetattr(STDIN_FILENO)
tty.setraw(STDIN_FILENO)
try:
while True:
r, w, e = __select([STDIN_FILENO], [], [],timeout=1)
if STDIN_FILENO in r:
#It accepts all keys from keyboard
data=os.read(STDIN_FILENO, 1)
#Bellow line returns ASCII value of a charector
ascii_value=ord(data[0])
##########################################################################
## Your code goes here ##
## ##
# Do some action here by matching the ASCII value #
# you can handle your program by making use of special keys like #
# Backspace, Ctrl, Ctrl+A,Ctrl+B, Ctrl+C, ...Ctrl+Z, Esc,F1, ...,F12 ....#
# Tab,Enter,Arrow keys,Alphabetic and Numeric keys are also supported #
##########################################################################
# #
#To Print use bellow line rather than print or sys.stdout.write(data) #
#os.write(STDOUT_FILENO,data) #
## #
##########################################################################
finally:
tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)
Then open terminal and run key_event_handler.py
This program is mainly to capture key pressed and get ascii of key pressed, This program can also be used for non-Blocking I/O in multy threaded aplications

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

Categories

Resources