Пользовательский ввод Python/curses при обновлении экрана

В настоящее время я кодирую пользовательский интерфейс приложения с помощью python/curses, и мне было интересно, можно ли попросить пользователя нажимать клавиши (режим cbreak), чтобы скрыть или показать некоторые панели или окна, пока пользовательский интерфейс постоянно обновляется.

Я прочитал официальную документацию по python о curses и предпринял несколько попыток, но даже с использованием режима cbreak и активированного режима неблокирующего ввода (nodelay) я не могу заставить его работать должным образом (мне удается получить пользовательский ввод, но в расходы на блокировку пользовательского интерфейса это не то, что я хочу).

Итак, мой вопрос прост, возможно ли это? И если да, то как?

Возможно, я неправильно прочитал документы, но я не нашел альтернативных документов или примеров по этому поводу.

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

Спасибо за вашу помощь, советы или ссылку на подробный документ.

РЕДАКТИРОВАТЬ :

В конце концов я получил следующий многопоточный код, но он меня не удовлетворяет. Пользовательский интерфейс подается, как и должно быть, но после обновления дисплей не работает.

Я также не понимаю, почему curses.panel.hidden() возвращает False, в то время как рассматриваемая панель скрыта. Кажется, что обновление окна, связанного с панелью, показывает панель или что-то в этом роде. Я действительно потерялся в этот момент!

import threading
import curses, curses.panel
import random
import time

gui = None

class ui:
    def __init__(self):
        self.feeder = feeder(self)
        self.stdscr = curses.initscr()
        curses.noecho()
        curses.cbreak()
        curses.curs_set(0)
        self.stdscr.keypad(1)

        self.win1 = curses.newwin(10, 50, 0, 0)    
        self.win1.border(0)
        self.pan1 = curses.panel.new_panel(self.win1)
        self.win2 = curses.newwin(10, 50, 0, 0)    
        self.win2.border(0)
        self.pan2 = curses.panel.new_panel(self.win2)
        self.win3 = curses.newwin(10, 50, 12, 0)
        self.win3.border(0)
        self.pan3 = curses.panel.new_panel(self.win3)

        self.win1.addstr(1, 1, "Window 1")
        self.win2.addstr(1, 1, "Window 2")
        self.win3.addstr(1, 1, "Press 's' to switch windows or 'q' to quit.")


        self.pan1.hide()
        self.win1.refresh()

        curses.panel.update_panels()
        self.win2.refresh()
        self.feeder.start()


    def ask(self):
        while True:
            self.win3.addstr(5,1, "Hidden = win1: "+str(self.pan1.hidden())+\
                             "win2:"+str(self.pan2.hidden()), 0)
            self.win3.refresh()
            k = self.win3.getkey()
            if k == 's':
                if self.pan1.hidden():
                    self.pan2.hide()
                    self.pan1.show()
                    self.win1.refresh()
                    self.win3.addstr(2, 1, "Pan1 restored")
                else:
                    self.pan1.hide()
                    self.pan2.show()
                    self.win2.refresh()
                    self.win3.addstr(2, 1, "Pan2 restored")
                self.win3.addstr(5,1, "Hidden = win1: "+\
                                 str(self.pan1.hidden())+\
                                 " win2:"+str(self.pan2.hidden()), 0)

            elif  k == 'q':
                break        
        self.quit_ui()

    def quit_ui(self):
        self.feeder.stop()
        curses.nocbreak()
        self.stdscr.keypad(0)
        curses.curs_set(1)
        curses.echo()
        curses.endwin()
        exit(0)

    def display_data(self, window, data):
        window.addstr(3, 1, data, 0)



class feeder(threading.Thread):
    # Fake U.I feeder
    def __init__(self, ui):
        super(feeder, self).__init__()
        self.running = False
        self.ui = ui
        self.count = 0

    def stop(self):
        self.running = False

    def run(self):
        self.running = True
        self.feed()

    def feed(self):
        while self.running:
            self.ui.win1.addstr(3, 1, str(self.count)+\
                                ": "+str(int(round(random.random()*9999))))
            self.ui.win1.addstr(4, 1, str(self.running))
            self.ui.win2.addstr(3, 1, str(self.count)+\
                                ": "+str(int(round(random.random()*9999))))
            self.ui.win2.addstr(4, 1, str(self.running))
            time.sleep(0.5)
            self.count += 1


if __name__ == "__main__":
    gui = ui()
    gui.ask()

person MCO System    schedule 02.10.2014    source источник
comment
У вас может быть поток экрана, обновляющий ваш экран с некоторой частотой обновления, и другой поток, читающий ввод ваших пользователей, который может вносить изменения во внутренние объекты (например, переключение окна).   -  person Chiel ten Brinke    schedule 02.10.2014
comment
Не уверен точно, что такое установка с Python, но проклятия обычно плохо работают с несколькими потоками. Обычным способом сделать это было бы зацикливание при вызове select() с тайм-аутом (или при наличии сигнала таймера для его прерывания) и вызове getch(), если ввод готов, или обновлении пользовательского интерфейса, если это не так.   -  person Crowman    schedule 03.10.2014
comment
Я ничего не нашел о методе или функции select в модуле python curses. Единственная ссылка на select касается константы с именем KEY_SELECT. И с ключевым словом select я нашел этот блог ссылка, объясняющая, как решить эту проблему, но с помощью C++. Спасибо за помощь. Я все еще ищу решение.   -  person MCO System    schedule 03.10.2014
comment
Я только что понял, что вы имели в виду под выбором. Вы имели в виду напрямую получать пользовательские данные, перехватывая sys.stdin ? Звучит неплохо, как заставить его работать.   -  person MCO System    schedule 03.10.2014


Ответы (1)


Наконец мне удалось заставить его работать, прочитав один байт из sys.stdin в двойном цикле while, а затем избегая использования другого потока. Следующий код может не работать в MS Windows, и, поскольку я не профессиональный разработчик, он может быть неоптимизированным или выдавать необработанные ошибки, но это всего лишь черновик, сделанный для того, чтобы я понял, как все работает (хотя комментарии приветствуются). ). Особая благодарность Полу Гриффитсу, который привел меня к sys.stdin.

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import curses, curses.panel
import random
import time
import sys
import select

gui = None

class ui:
    def __init__(self):
        self.stdscr = curses.initscr()
        curses.noecho()
        curses.cbreak()
        curses.curs_set(0)
        self.stdscr.keypad(1)

        self.win1 = curses.newwin(10, 50, 0, 0)    
        self.win1.border(0)
        self.pan1 = curses.panel.new_panel(self.win1)
        self.win2 = curses.newwin(10, 50, 0, 0)    
        self.win2.border(0)
        self.pan2 = curses.panel.new_panel(self.win2)
        self.win3 = curses.newwin(10, 50, 12, 0)
        self.win3.border(0)
        self.pan3 = curses.panel.new_panel(self.win3)

        self.win1.addstr(1, 1, "Window 1")
        self.win2.addstr(1, 1, "Window 2")
        self.win3.addstr(1, 1, "Press 's' to switch windows or 'q' to quit.")

        self.pan1.hide()

    def refresh(self):
        curses.panel.update_panels()
        self.win2.refresh()
        self.win1.refresh()

    def switch_pan(self):
        if self.pan1.hidden():
            self.pan2.bottom()
            self.pan2.hide()
            self.pan1.top()
            self.pan1.show()
        else:
            self.pan1.bottom()
            self.pan1.hide()
            self.pan2.top()
            self.pan2.show()

        self.refresh()

    def quit_ui(self):
        curses.nocbreak()
        self.stdscr.keypad(0)
        curses.curs_set(1)
        curses.echo()
        curses.endwin()
        print "UI quitted"
        exit(0)


class feeder:
    # Fake U.I feeder
    def __init__(self):
        self.running = False
        self.ui = ui()
        self.count = 0

    def stop(self):
        self.running = False

    def run(self):
        self.running = True
        self.feed()

    def feed(self):
        while self.running :
            while sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
                line = sys.stdin.read(1)
                if line.strip() == "q":
                    self.stop()
                    self.ui.quit_ui()
                    break
                elif line.strip() == "s":
                    self.ui.switch_pan()

            self.ui.win1.addstr(3, 1, str(self.count)+\
                                ": "+str(int(round(random.random()*999))))
            self.ui.win1.addstr(4, 1, str(self.running))
            self.ui.win2.addstr(3, 1, str(self.count)+\
                                ": "+str(int(round(random.random()*999))))
            self.ui.win2.addstr(4, 1, str(self.running))
            self.ui.refresh()
            time.sleep(0.1)
            self.count += 1

if __name__ == "__main__":
    f = feeder()
    f.run()
person MCO System    schedule 03.10.2014