¿Cómo desplazar el texto en Python / Curses subwindow?

En mi secuencia de comandos de Python, que usa cursas, tengo un subwin al que se le asigna algún texto. Debido a que la longitud del texto puede ser más larga que el tamaño de la ventana, el texto debe ser desplazable.

No parece que haya ningún atributo similar a “desbordamiento” de CSS para las ventanas de cursas. Los documentos de Python / Curses también son bastante crípticos en este aspecto.

¿Alguien aquí tiene una idea de cómo puedo codificar una subventana de Curses desplazable utilizando Python y desplazarme por ella?

\ edit: pregunta mas precisa

De acuerdo con window.scroll, era demasiado complicado mover el contenido de la ventana. En cambio, maldes.newpad lo hizo por mí.

Crea un pad:

mypad = curses.newpad(40,60) mypad_pos = 0 mypad.refresh(mypad_pos, 0, 5, 5, 10, 60) 

Luego puede desplazarse aumentando / disminuyendo mypad_pos dependiendo de la entrada de window.getch () en cmd:

 if cmd == curses.KEY_DOWN: mypad_pos += 1 mypad.refresh(mypad_pos, 0, 5, 5, 10, 60) elif cmd == curses.KEY_UP: mypad_pos -= 1 mypad.refresh(mypad_pos, 0, 5, 5, 10, 60) 

Correcto, estaba un poco confundido sobre cómo utilizar las almohadillas (para desplazar el texto), y aún no pude entenderlo después de leer esta publicación; especialmente porque quería usarlo en un contexto en el que el contenido es un “conjunto de líneas” existente. Así que preparé un pequeño ejemplo, que muestra similitudes (y diferencias) entre newpad y subpad :

 #!/usr/bin/env python2.7 import curses # content - array of lines (list) mylines = ["Line {0} ".format(id)*3 for id in range(1,11)] import pprint pprint.pprint(mylines) def main(stdscr): hlines = begin_y = begin_x = 5 ; wcols = 10 # calculate total content size padhlines = len(mylines) padwcols = 0 for line in mylines: if len(line) > padwcols: padwcols = len(line) padhlines += 2 ; padwcols += 2 # allow border stdscr.addstr("padhlines "+str(padhlines)+" padwcols "+str(padwcols)+"; ") # both newpad and subpad are : mypadn = curses.newpad(padhlines, padwcols) mypads = stdscr.subpad(padhlines, padwcols, begin_y, begin_x+padwcols+4) stdscr.addstr(str(type(mypadn))+" "+str(type(mypads)) + "\n") mypadn.scrollok(1) mypadn.idlok(1) mypads.scrollok(1) mypads.idlok(1) mypadn.border(0) # first ... mypads.border(0) # ... border for line in mylines: mypadn.addstr(padhlines-1,1, line) mypadn.scroll(1) mypads.addstr(padhlines-1,1, line) mypads.scroll(1) mypadn.border(0) # second ... mypads.border(0) # ... border # refresh parent first, to render the texts on top #~ stdscr.refresh() # refresh the pads next mypadn.refresh(0,0, begin_y,begin_x, begin_y+hlines, begin_x+padwcols) mypads.refresh() mypads.touchwin() mypadn.touchwin() stdscr.touchwin() # no real effect here #stdscr.refresh() # not here! overwrites newpad! mypadn.getch() # even THIS command erases newpad! # (unless stdscr.refresh() previously): stdscr.getch() curses.wrapper(main) 

Cuando ejecute esto, al principio obtendrá algo como ( newpad izquierdo, subpad derecho):

  ┌────────────────────────┐ ┌────────────────────────┐ │Line 1 Line 1 Line 1 ───│ │Line 1 Line 1 Line 1 ───│ │Line 2 Line 2 Line 2 │ │Line 2 Line 2 Line 2 │ │Line 3 Line 3 Line 3 │ │Line 3 Line 3 Line 3 │ │Line 4 Line 4 Line 4 │ │Line 4 Line 4 Line 4 │ │Line 5 Line 5 Line 5 │ │Line 5 Line 5 Line 5 │ │Line 6 Line 6 Line 6 │ │Line 7 Line 7 Line 7 │ │Line 8 Line 8 Line 8 │ │Line 9 Line 9 Line 9 │ │Line 10 Line 10 Line 10 │ └────────────────────────┘ 

Algunas notas:

  • Tanto el newpad como el subpad deben tener su ancho / alto subpad al contenido (número de líneas / ancho máximo de línea de la matriz de líneas) + espacio de borde eventual
  • En ambos casos, podría permitir líneas adicionales con scrollok() , pero no ancho adicional
  • En ambos casos, básicamente “presionas” una línea en la parte inferior de la almohadilla; y luego scroll() hacia arriba para dejar espacio para la próxima
  • El método de refresh especial que tiene el newpad permite que solo se muestre en pantalla una región de este “contenido completo”; subpad more-less debe mostrarse en el tamaño en el que se creó la instancia.
  • Si dibuja los bordes de las almohadillas antes de agregar cadenas de contenido, entonces los bordes también se desplazarán hacia arriba (esa es la pieza que se muestra en la parte ...Line 1 ───│ ).

Enlaces útiles:

Establecer el window.scrollok (verdadero).

Documentación

Esta es la respuesta a esta pregunta: cómo hacer un menú de desplazamiento en python-curses

Este código le permite crear un pequeño menú de desplazamiento en un cuadro de una lista de cadenas.
También puede usar este código para obtener la lista de cadenas de una consulta sqlite o de un archivo csv.
Para editar el número máximo de filas del menú, solo tiene que editar max_row .
Si pulsa Intro, el progtwig imprimirá el valor de la cadena seleccionada y su posición.

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

Quería usar un panel de desplazamiento para mostrar el contenido de algunos archivos de texto grandes, pero esto no funcionó bien porque los textos pueden tener saltos de línea y fue bastante difícil averiguar cuántos caracteres se mostrarán a la vez para adaptarse al buen número de caracteres. Columnas y filas.

Así que decidí dividir primero mis archivos de texto en líneas de exactamente COLUMNAS, rellenando con espacios cuando las líneas eran demasiado cortas. Luego, desplazar el texto se vuelve más fácil.

Aquí hay un código de ejemplo para mostrar cualquier archivo de texto:

 #!/usr/bin/python # -*- coding: utf-8 -*- import curses import locale import sys def main(filename, filecontent, encoding="utf-8"): try: stdscr = curses.initscr() curses.noecho() curses.cbreak() curses.curs_set(0) stdscr.keypad(1) rows, columns = stdscr.getmaxyx() stdscr.border() bottom_menu = u"(↓) Next line | (↑) Previous line | (→) Next page | (←) Previous page | (q) Quit".encode(encoding).center(columns - 4) stdscr.addstr(rows - 1, 2, bottom_menu, curses.A_REVERSE) out = stdscr.subwin(rows - 2, columns - 2, 1, 1) out_rows, out_columns = out.getmaxyx() out_rows -= 1 lines = map(lambda x: x + " " * (out_columns - len(x)), reduce(lambda x, y: x + y, [[x[i:i+out_columns] for i in xrange(0, len(x), out_columns)] for x in filecontent.expandtabs(4).splitlines()])) stdscr.refresh() line = 0 while 1: top_menu = (u"Lines %d to %d of %d of %s" % (line + 1, min(len(lines), line + out_rows), len(lines), filename)).encode(encoding).center(columns - 4) stdscr.addstr(0, 2, top_menu, curses.A_REVERSE) out.addstr(0, 0, "".join(lines[line:line+out_rows])) stdscr.refresh() out.refresh() c = stdscr.getch() if c == ord("q"): break elif c == curses.KEY_DOWN: if len(lines) - line > out_rows: line += 1 elif c == curses.KEY_UP: if line > 0: line -= 1 elif c == curses.KEY_RIGHT: if len(lines) - line >= 2 * out_rows: line += out_rows elif c == curses.KEY_LEFT: if line >= out_rows: line -= out_rows finally: curses.nocbreak(); stdscr.keypad(0); curses.echo(); curses.curs_set(1) curses.endwin() if __name__ == '__main__': locale.setlocale(locale.LC_ALL, '') encoding = locale.getpreferredencoding() try: filename = sys.argv[1] except: print "Usage: python %s FILENAME" % __file__ else: try: with open(filename) as f: filecontent = f.read() except: print "Unable to open file %s" % filename else: main(filename, filecontent, encoding) 

El truco principal es la línea:

 lines = map(lambda x: x + " " * (out_columns - len(x)), reduce(lambda x, y: x + y, [[x[i:i+out_columns] for i in xrange(0, len(x), out_columns)] for x in filecontent.expandtabs(4).splitlines()])) 

Primero, las tabulaciones en el texto se convierten en espacios, luego usé el método splitlines () para convertir mi texto en una matriz de líneas. Pero algunas líneas pueden ser más largas que nuestro número de COLUMNAS, por lo que dividí cada línea en una porción de caracteres de COLUMNAS y luego usé Reducir para transformar la lista resultante en una lista de líneas. Finalmente, utilicé el mapa para rellenar cada línea con espacios al final, de modo que su longitud sea exactamente de los caracteres de COLUMNAS.

Espero que esto ayude.