2010-03-25 12 views
17

En mi script de Python que usa Curses, tengo un subwin al que se le asigna un 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.¿Cómo se desplaza el texto en la ventana secundaria Python/Curses?

Parece que no hay ningún atributo CSS "de desbordamiento" para las ventanas de Curses. 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 Curses desplazable usando Python y realmente desplazarme por ella?

\ edición: pregunta más precisa

Respuesta

22

bien con window.scroll era demasiado complicado para mover el contenido de la ventana. En cambio, curses.newpad lo hizo por mí.

Crear una almohadilla:

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

A continuación, puede desplazarse por el aumento/disminución mypad_pos en función del aporte 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) 
1

Ajuste el window.scrollok (Verdadero).

Documentation

+4

Eso hace que la ventana acepte textos que superan su propio tamaño. Es posible desplazarse usando window.scroll (1). Pero luego las líneas desplazadas tienen que volver a dibujarse, lo que no se explica en los documentos. Así que obtener el desplazamiento de Windows Curses requiere varios pasos, algunos de los cuales aún me faltan ... – lecodesportif

4

derecho, que estaba un poco confundido sobre cómo utilizar los pads (con el fin de desplazar el texto), y todavía no podía entenderlo después de leer esta publicación; especialmente porque quería usarlo en un contexto en el que el contenido es una "matriz de líneas" existente. Así que he preparado 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 <class '_curses.curses window'>: 
    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) 

Al ejecutar esto, en un primer momento obtendrá algo así como (newpad izquierda, subpad derecha):

┌────────────────────────┐ ┌────────────────────────┐ 
│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 newpad y subpad deben tener su tamaño de la anchura/altura d para el contenido (líneas num/ancho de línea máxima de la matriz de líneas) + espacio fronterizo eventual
  • En ambos casos, que podrían permitir líneas adicionales con scrollok() - pero no anchura adicional
  • En ambos casos, es básicamente " presione "una línea en la parte inferior de la plataforma; y luego scroll() hasta dejar espacio para el siguiente
  • El método especial refresh que tiene newpad, permite que solo una región de este "contenido completo" se muestre en la pantalla; subpad más-menos tiene que ser mostrado en el tamaño que se crea una instancia de
  • Si dibuja los bordes de las almohadillas antes de añadir cadenas de contenido - a continuación, las fronteras se desplazará hacia arriba también (es decir la pieza ─── se muestra en la parte ...Line 1 ───│) .

Enlaces de interés:

0

Ésta es la respuesta de esta pregunta: How to make a scrolling menu in 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 presiona intro, el programa 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() 
0

quería usar una almohadilla de desplazamiento para mostrar el contenido de algunos archivos de texto grandes, pero esto no funcionaba bien porque los textos pueden tener saltos de línea y era bastante difícil de averiguar el número de caracteres para visualizar en una tiempo para adaptarse al buen número de columnas y filas.

Así que decidí dividir primero mis archivos de texto en líneas de caracteres exactamente COLUMNAS, rellenando con espacios cuando las líneas eran demasiado cortas. Luego, desplazarse por 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()])) 

En primer lugar, las tabulaciones en el texto se convierten en espacios, entonces utilicé método de 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 fragmentos de caracteres COLUMNAS y luego utilicé reducir para transformar la lista resultante en una lista de líneas. Finalmente, utilicé el mapa para rellenar cada línea con espacios finales para que su longitud sea exactamente caracteres COLUMNAS.

Espero que esto ayude.

Cuestiones relacionadas