简体   繁体   English

如何在 python-curses 中制作滚动菜单

[英]How to make a scrolling menu in python-curses

There is a way to make a scrolling menu in python-curses?有没有办法在 python-curses 中制作滚动菜单? I have a list of records that I got from a query in sqlite3 and I have to show them in a box but they are more than the max number of rows: can I make a little menu to show them all without making curses crashing?我有一个从 sqlite3 中的查询中获得的记录列表,我必须将它们显示在一个框中,但它们超过了最大行数:我可以制作一个小菜单来显示它们,而不会使curses 崩溃吗?

This code allows you to create a little menu in a box from a list of strings.此代码允许您从字符串列表的框中创建一个小菜单。
You can also use this code getting the list of strings from a sqlite query or from a csv file.您还可以使用此代码从 sqlite 查询或 csv 文件中获取字符串列表。
To edit the max number of rows of the menu you just have to edit max_row .要编辑菜单的最大行数,您只需编辑max_row
If you press enter the program will print the selected string value and its position.如果按 Enter,程序将打印选定的字符串值及其位置。

from __future__ import division  #You don't need this in Python3
import curses
from math import *



screen = curses.initscr()
curses.noecho()
curses.cbreak()
curses.start_color()
screen.keypad( 1 )
curses.init_pair(1,curses.COLOR_BLACK, curses.COLOR_CYAN)
highlightText = curses.color_pair( 1 )
normalText = curses.A_NORMAL
screen.border( 0 )
curses.curs_set( 0 )
max_row = 10 #max number of rows
box = curses.newwin( max_row + 2, 64, 1, 1 )
box.box()


strings = [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "l", "m", "n" ] #list of strings
row_num = len( strings )

pages = int( ceil( row_num / max_row ) )
position = 1
page = 1
for i in range( 1, max_row + 1 ):
    if row_num == 0:
        box.addstr( 1, 1, "There aren't strings", highlightText )
    else:
        if (i == position):
            box.addstr( i, 2, str( i ) + " - " + strings[ i - 1 ], highlightText )
        else:
            box.addstr( i, 2, str( i ) + " - " + strings[ i - 1 ], normalText )
        if i == row_num:
            break

screen.refresh()
box.refresh()

x = screen.getch()
while x != 27:
    if x == curses.KEY_DOWN:
        if page == 1:
            if position < i:
                position = position + 1
            else:
                if pages > 1:
                    page = page + 1
                    position = 1 + ( max_row * ( page - 1 ) )
        elif page == pages:
            if position < row_num:
                position = position + 1
        else:
            if position < max_row + ( max_row * ( page - 1 ) ):
                position = position + 1
            else:
                page = page + 1
                position = 1 + ( max_row * ( page - 1 ) )
    if x == curses.KEY_UP:
        if page == 1:
            if position > 1:
                position = position - 1
        else:
            if position > ( 1 + ( max_row * ( page - 1 ) ) ):
                position = position - 1
            else:
                page = page - 1
                position = max_row + ( max_row * ( page - 1 ) )
    if x == curses.KEY_LEFT:
        if page > 1:
            page = page - 1
            position = 1 + ( max_row * ( page - 1 ) )

    if x == curses.KEY_RIGHT:
        if page < pages:
            page = page + 1
            position = ( 1 + ( max_row * ( page - 1 ) ) )
    if x == ord( "\n" ) and row_num != 0:
        screen.erase()
        screen.border( 0 )
        screen.addstr( 14, 3, "YOU HAVE PRESSED '" + strings[ position - 1 ] + "' ON POSITION " + str( position ) )

    box.erase()
    screen.border( 0 )
    box.border( 0 )

    for i in range( 1 + ( max_row * ( page - 1 ) ), max_row + 1 + ( max_row * ( page - 1 ) ) ):
        if row_num == 0:
            box.addstr( 1, 1, "There aren't strings",  highlightText )
        else:
            if ( i + ( max_row * ( page - 1 ) ) == position + ( max_row * ( page - 1 ) ) ):
                box.addstr( i - ( max_row * ( page - 1 ) ), 2, str( i ) + " - " + strings[ i - 1 ], highlightText )
            else:
                box.addstr( i - ( max_row * ( page - 1 ) ), 2, str( i ) + " - " + strings[ i - 1 ], normalText )
            if i == row_num:
                break



    screen.refresh()
    box.refresh()
    x = screen.getch()

curses.endwin()
exit()

代码截图

To make a scrollable widget that can scroll through text that is larger than a screenful you will need to use curses.newpad要制作一个可以滚动大于一屏的文本的可滚动小部件,您需要使用curses.newpad

You can find a simple example here: https://stackoverflow.com/a/2523020/9205341你可以在这里找到一个简单的例子: https : //stackoverflow.com/a/2523020/9205341
And the Python 3 / Python 2 docs there.还有Python 3 / Python 2文档。

use this skeleton but add page-up / -down to display entries in groups of 10.使用这个骨架,但添加 page-up / -down 以显示 10 组的条目。

very crash-proof in python2 and python3 !在 python2 和 python3 中非常防撞!


#!/usr/bin/env python2
# -*- coding: UTF-8 -*-
#kate: syntax Python ;

# a skeleton menu with python2 & py3                  https://paste.cutelyst.org/

# https://0bin.net/paste/yauebNkDNbwgZqwy#a2YfWpSTHh0RV-0Yfz3FIypudIU6oi4DWvV9EGXo1Pv

'''function 1 on F1 1 and NUM-1'''
def funkt1():
    return 1

def funkt2():
    return 2

def funkt3():
    return 3

def funkt4():
    return 4

def funkt5():
    return 5

def funkt6():
    return 6

def funkt7():
    return 7

def funkt8():
    return 8

def funkt9():
    return 9




import sys,os
import curses

global menuE
global e
global noofmes
global keychar
e       = 0
menuE   = 1   # active entry
noofmes = 10  # number of menu entries +1
keychar = " " # like 1 or 2 ...

class switch(object):
    value = None
    def __new__(class_, value):
        class_.value = value
        return True

def case(*args):
    return any((arg == switch.value for arg in args))


def draw_menu(stdscr):
    global menuE
    global e
    global noofmes
    global keychar
    k        = 0
    cursor_x = 0
    cursor_y = 0

    # Clear and refresh the screen for a blank canvas
    stdscr.clear()
    stdscr.refresh()

    # Start colors in curses
    curses.start_color()
    curses.init_pair(1, curses.COLOR_CYAN,  curses.COLOR_BLACK)
    curses.init_pair(2, curses.COLOR_RED,   curses.COLOR_BLACK)
    curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE)

    # Loop where k is the last character pressed
    while (k != ord('q')):

        # Initialization
        stdscr.clear()
        height, width = stdscr.getmaxyx()

        ki              = int(k)
        try:    keychar = chr(k)
        except: keychar = 0
        callfunc        = False


        while switch(k):
            if case(49, 265, 360): # allows keys   1  or  NUM-1  or  F1     to be  Entry1    for  0..9
                menuE = 1
                break
            if case(50, 266     ): # 258   mouse u
                menuE = 2
                break
            if case(51, 267, 338):
                menuE = 3
                break
            if case(52, 268     ): #260
                menuE = 4
                break
            if case(53, 269, 69):
                menuE = 5
                break
            if case(54, 270     ): #261
                menuE = 6
                break
            if case(55, 271, 262):
                menuE = 7
                break
            if case(56, 272     ): #259   mouse d
                menuE = 8
                break
            if case(57, 273, 339):
                menuE = 9
                break
            if case(10, 32 , 83 ):   # SPACE  ,   ENTER  ,   83 mouse middle
                callfunc = True
                break
            pass
            break

        if callfunc:
            if   menuE == 1: e = funkt1()
            elif menuE == 2: e = funkt2()
            elif menuE == 3: e = funkt3()
            elif menuE == 4: e = funkt4()
            elif menuE == 5: e = funkt5()
            elif menuE == 6: e = funkt6()
            elif menuE == 7: e = funkt7()
            elif menuE == 8: e = funkt8()
            elif menuE == 9: e = funkt9()


        if k == curses.KEY_DOWN:
            cursor_y = cursor_y + 1
            menuE = (menuE + 1) % noofmes


        elif k == curses.KEY_UP:
            cursor_y = cursor_y - 1
            menuE = (menuE - 1) % noofmes

        elif k == curses.KEY_RIGHT:
            cursor_x = cursor_x + 1

        elif k == curses.KEY_LEFT:
            cursor_x = cursor_x - 1


        cursor_x = max(0, cursor_x)
        cursor_x = min(width-1, cursor_x)

        cursor_y = max(0, cursor_y)
        cursor_y = min(height-1, cursor_y)

        # Declaration of strings
        title        = "Python2 , py3  menu demo with lib ´curses´ "[:width-1]
        subtitle     = "because py2 will never die!"[:width-1]
        keystr       = str(e) + " <-- funkt  " + "Last key pressed: {}".format(k)[:width-1]
        statusbarstr = "Press 'q' to exit | STATUS BAR | Pos: {}, {}".format(cursor_x, cursor_y)
        if k == 0:
            keystr = "No key press detected..."[:width-1]

        # Centering calculations
        start_x_title    = int((  width // 2) - (len(title)    // 2) - len(title)    % 2)
        start_x_subtitle = int((  width // 2) - (len(subtitle) // 2) - len(subtitle) % 2)
        start_x_keystr   = int((  width // 2) - (len(keystr)   // 2) - len(keystr)   % 2)
        start_y          = int(( height // 2) -                   2)

        # Rendering some text
        whstr = "Width: {}, Height: {}".format(width, height)
        stdscr.addstr(0, 0, whstr, curses.color_pair(1))

        # Render status bar
        stdscr.attron(curses.color_pair(3))
        stdscr.addstr(height-1,  0, statusbarstr)
        stdscr.addstr(height-1, len(statusbarstr), " " * (width - len(statusbarstr) - 1))
        stdscr.attroff(curses.color_pair(3))

        # Turning on attributes for title
        stdscr.attron(curses.color_pair(2))
        stdscr.attron(curses.A_BOLD)

        # Rendering title
        stdscr.addstr(start_y -6, start_x_title, title)


        # Turning off attributes for title
        stdscr.attroff(curses.color_pair(2))
        stdscr.attroff(curses.A_BOLD)

        # Print rest of text
        stdscr.addstr( start_y - 4, start_x_subtitle, subtitle)

        #stdscr.addstr(start_y - 3, (width // 2) - 2, '-' * 4)
        stdscr.addstr( start_y - 2, start_x_keystr, keystr)


        # menu
        stdscr.addstr(start_y + 0,  start_x_subtitle, "~~~  Menu  ~~~")


        if menuE == 1 :
            stdscr.attron( curses.A_BOLD)
        else          :  stdscr.attroff(curses.A_BOLD)
        stdscr.addstr(start_y + 1,  start_x_subtitle, "1 Entry1")
        if menuE == 2 :
            stdscr.attron( curses.A_BOLD)
        else          :  stdscr.attroff(curses.A_BOLD)
        stdscr.addstr(start_y + 2,  start_x_subtitle, "2 Entry2")
        if menuE == 3 :
            stdscr.attron( curses.A_BOLD)
        else          :  stdscr.attroff(curses.A_BOLD)
        stdscr.addstr(start_y + 3 , start_x_subtitle, "3 Entry3")
        if menuE == 4 :
            stdscr.attron( curses.A_BOLD)
        else          :  stdscr.attroff(curses.A_BOLD)
        stdscr.addstr(start_y + 4 , start_x_subtitle, "4 Entry4")
        if menuE == 5 :
            stdscr.attron( curses.A_BOLD)
        else          :  stdscr.attroff(curses.A_BOLD)
        stdscr.addstr(start_y + 5 , start_x_subtitle, "5 Entry5")
        if menuE == 6 :
            stdscr.attron( curses.A_BOLD)
        else          :  stdscr.attroff(curses.A_BOLD)
        stdscr.addstr(start_y + 6 , start_x_subtitle, "6 Entry6")
        if menuE == 7 :
            stdscr.attron( curses.A_BOLD)
        else          :  stdscr.attroff(curses.A_BOLD)
        stdscr.addstr(start_y + 7 , start_x_subtitle, "7 Entry7")
        if menuE == 8 :
            stdscr.attron( curses.A_BOLD)
        else          :  stdscr.attroff(curses.A_BOLD)
        stdscr.addstr(start_y + 8 , start_x_subtitle, "8 Entry8")
        if menuE == 9 :
            stdscr.attron( curses.A_BOLD)
        else          :  stdscr.attroff(curses.A_BOLD)
        stdscr.addstr(start_y + 9 , start_x_subtitle, "9 Entry9")


        stdscr.move(cursor_y, cursor_x)

        # Refresh the screen
        stdscr.refresh()

        # Wait for next input
        k = stdscr.getch()

def main():
    curses.wrapper(draw_menu)

if __name__ == "__main__":
    main()


I used https://github.com/wong2/pick我用过https://github.com/wong2/pick

>>> title = 'Please choose your favorite programming language (press SPACE to mark, ENTER to continue): '
>>> options = ['Java', 'JavaScript', 'Python', 'PHP', 'C++', 'Erlang', 'Haskell']
>>> pick(options, title, multi_select=True, min_selection_count=1)

It creates an ncurses-based picker that takes up the entire terminal window and lets you select multiple options (it'll scroll the options if they don't all fit on the page).它创建了一个基于 ncurses 的选择器,占据整个终端窗口并让您选择多个选项(如果选项不适合页面,它将滚动选项)。 After you chose stuff it returns the values and their indeces:选择内容后,它会返回值及其索引:

[('Java', 0), ('C++', 4)]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM