How to make a scrolling menu in python-curses

前端 未结 4 567
走了就别回头了
走了就别回头了 2020-12-06 07:29

There is a way to make a scrolling menu in 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

相关标签:
4条回答
  • 2020-12-06 07:56

    To make a scrollable widget that can scroll through text that is larger than a screenful you will need to use curses.newpad

    You can find a simple example here: https://stackoverflow.com/a/2523020/9205341
    And the Python 3/Python 2 docs there.

    0 讨论(0)
  • 2020-12-06 08:05

    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.
    To edit the max number of rows of the menu you just have to edit max_row.
    If you press enter the program will print the selected string value and its position.

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

    Screenshot of the code

    0 讨论(0)
  • 2020-12-06 08:06

    use this skeleton but add page-up / -down to display entries in groups of 10.

    very crash-proof in python2 and 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()
    
    
    
    0 讨论(0)
  • 2020-12-06 08:11

    I used 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). After you chose stuff it returns the values and their indeces:

    [('Java', 0), ('C++', 4)]
    
    0 讨论(0)
提交回复
热议问题