Вопрос

В моем скрипте Python, который использует Curses, у меня есть субвин, которому назначен некоторый текст.Поскольку длина текста может превышать размер окна, текст должен иметь возможность прокрутки.

Кажется, что для окон Curses не существует какого-либо атрибута CSS-"переполнения".Документация Python/Curses также довольно загадочна в этом аспекте.

Есть ли у кого-нибудь здесь идеи, как я могу запрограммировать прокручиваемое подокно Curses с помощью Python и фактически прокручивать его?

\редактировать:более точный вопрос

Это было полезно?

Решение

Хорошо, с window.scroll было слишком сложно перемещать содержимое окна.Вместо этого Curss.newpad сделал это за меня.

Создайте площадку:

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

Затем вы можете прокручивать, увеличивая/уменьшая mypad_pos в зависимости от ввода из window.getch() в 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)

Другие советы

Да, я был немного озадачен тем, как использовать панели (для прокрутки текста), и до сих пор не мог понять этого после прочтения этого поста;тем более, что я хотел использовать его в контексте существующего «массива строк».Поэтому я подготовил небольшой пример, показывающий сходства (и различия) между newpad и 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)

Когда вы запустите это, сначала вы получите что-то вроде (newpad левый, subpad верно):

 ┌────────────────────────┐    ┌────────────────────────┐
 │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 │
                               └────────────────────────┘

Некоторые примечания:

  • Оба newpad и subpad ширина/высота должна соответствовать содержимому (количество строк/максимальная ширина строки массива строк) + возможное пространство границы
  • В обоих случаях вы можете разрешить дополнительные строки с помощью scrollok() - но не дополнительная ширина
  • В обоих случаях вы по сути «нажимаете» линию внизу пэда;а потом scroll() вверх, чтобы освободить место для следующего
  • Специальный refresh метод, который newpad имеет, затем позволяет отображать на экране только часть этого «всего контента»; subpad «больше-меньше» должно отображаться в том размере, в котором он был создан.
  • Если вы нарисуете границы контактных площадок перед добавлением строк содержимого, то границы тоже прокрутятся вверх (то есть ─── кусок, показанный на ...Line 1 ───│ часть).

Полезные ссылки:

Установите window.scrollok(True).

Документация

Я хотел использовать панель прокрутки для отображения содержимого некоторых больших текстовых файлов, но это не сработало, потому что тексты могут иметь разрывы строк, и было довольно сложно определить, сколько символов отображать одновременно, чтобы уместить достаточное количество символов. столбцы и строки.

Поэтому я решил сначала разбить свои текстовые файлы на строки, состоящие ровно из СТОЛБЦОВ символов, дополняя их пробелами, когда строки были слишком короткими.Тогда прокрутка текста станет более простой.

Вот пример кода для отображения любого текстового файла:

#!/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)

Основная хитрость – это строка:

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

Сначала табуляции в тексте преобразуются в пробелы, затем я использовал метод Splitlines() для преобразования текста в массив строк.Но некоторые строки могут быть длиннее, чем наш номер COLUMNS, поэтому я разделил каждую строку на фрагменты символов COLUMNS, а затем использовал сокращение, чтобы преобразовать полученный список в список строк.Наконец, я использовал карту, чтобы заполнить каждую строку конечными пробелами, чтобы ее длина составляла ровно СТОЛБЦЫ символов.

Надеюсь это поможет.

Это ответ на этот вопрос:Как сделать прокручиваемое меню в Python-curses

Этот код позволяет создать небольшое прокручиваемое меню в поле из списка строк.
Вы также можете использовать этот код для получения списка строк из запроса sqlite или из файла csv.
Чтобы отредактировать максимальное количество строк меню, вам просто нужно отредактировать max_row.
Если вы нажмете 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()
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top