Seite 1 von 1

Curses Problem

Verfasst: Dienstag 6. Oktober 2009, 11:00
von roschi
Hallo!

Ich habe mal wieder ein Problem bei einem Projekt.

Ich brauche ein Curses-Interface, und habe mir folgendes gebastelt:

Das ist meine Klasse fuer die Verwaltung des In- und Outputs, momentan erstmal der Output:

Code: Alles auswählen

import curses

class Screen(object):
  curses.setupterm()
  TERM_COLS = curses.tigetnum("cols")
  TERM_LINES = curses.tigetnum("lines")
  OUTWIN_COLS = TERM_COLS
  OUTWIN_LINES = TERM_LINES - 5
  OUTWIN_COLS_MAX = OUTWIN_COLS - 2
  OUTWIN_LINES_MAX = OUTWIN_LINES - 2
  OUTWIN_START_POS = [1, 2]
  INWIN_COLS = TERM_COLS
  INWIN_LINES = 5
  INWIN_COLS_MAX = TERM_COLS - 2
  INWIN_LINES_MAX = INWIN_LINES - 2

  def __init__(self):
    self.stdscr = curses.initscr()
    self.outwin = self.stdscr.subwin(self.OUTWIN_LINES,
                                     self.OUTWIN_COLS, 0, 0)
    self.outwin.box()
    self.outwin.refresh()
    self.inwin = self.stdscr.subwin(self.INWIN_LINES,
                                    self.INWIN_COLS,
                                    self.OUTWIN_LINES, 0)
    self.inwin.box()
    self.inwin.refresh()

    self._outwin_pos = self.OUTWIN_START_POS[:]
    self._outwin_scroll = 0
    self._outwin_content = ""

  def __del__(self):
    curses.endwin()

  def write_output(self, data, attr=0):
    data = data.replace("\r", "")
    for char in data:
      if char == "\n" or self._outwin_pos[1] == self.OUTWIN_COLS_MAX:
        if self._outwin_pos[0] == self.OUTWIN_LINES_MAX:
          self.scroll_outwin(1)
        self._outwin_pos[0] += 1
        self._outwin_content += "\n"
        self._outwin_pos[1] = self.OUTWIN_START_POS[1]
        if char == "\n":
          continue
      self.outwin.addstr(self._outwin_pos[0],
                         self._outwin_pos[1],
                         char, attr)
      self._outwin_content += char
      self._outwin_pos[1] += 1
    self.outwin.refresh()
    return True

  def scroll_outwin(self, lines):
    if lines != 0:
      data = self._outwin_content.split("\n")
      from_line = self._outwin_scroll + lines
      till_line = self._outwin_scroll + lines + self.OUTWIN_LINES_MAX
      data = data[from_line:till_line]
#      data += ["" for dummy in xrange(self.OUTWIN_LINES_MAX - len(data))]
      data = "\n".join(data)
      print "\a"
      self._outwin_pos[0] = self.OUTWIN_START_POS[0]
      self._outwin_pos[1] = self.OUTWIN_START_POS[1]
      self.outwin.clear()
      self.outwin.box()
      self.write_output(data)
      self._outwin_scroll = from_line
      self._outwin_pos[0] -= 1
      print >>file("data", "w"), from_line, till_line, self._outwin_pos[0], data
      return True
    else:
      return True
Nun will ich z.B. hiermit Text ausgeben:

Code: Alles auswählen

#!/usr/bin/env python

import random, sys, time

def main():
  scr = Screen()
  while True:
    scr.write_output(random.choice(["x\n", "xx\n", "xxx\n"]))
    time.sleep(0.5)

if __name__ == "__main__":
  try:
    main()
  except KeyboardInterrupt:
    sys.exit(1)
Das Problem ist, dass in manchen Zeilen auch mehr als 3 X-Zeichen ausgegeben werden, wieso das?

Ich waere euch sehr dankbar fuer Hilfe!

Liebe Grueße,
roschi

Verfasst: Dienstag 6. Oktober 2009, 11:12
von Rebecca
Kennst du urwid? Ist etwas komfortabler als direkt curses zu verwenden.

Verfasst: Dienstag 6. Oktober 2009, 11:27
von snafu
Warum geben deine Methoden am Ende `True` zurück?

Verfasst: Dienstag 6. Oktober 2009, 11:33
von BlackJack
@roschi: Irgenwo hakts bei Deiner Scrollmethode. Das erste mal wird die ausgelöst, wenn in der vorletzten Zeile ein '\n' ausgegeben wird. Danach scheint der Text aber auch bis zur letzten Zeile zu gehen!?

Was sollen diese überflüssigen ``return True``\s?

Verfasst: Dienstag 6. Oktober 2009, 11:40
von roschi
Rebecca hat geschrieben:Kennst du urwid? Ist etwas komfortabler als direkt curses zu verwenden.
Ja, kenne ich. Aber mir schien das hier einfacher. Vielleicht auch nur, weil ich kein gutes Tutorial gefunden habe :). Aber ich dachte, dass es so jetzt schneller geht.
BlackJack hat geschrieben:@roschi: Irgenwo hakts bei Deiner Scrollmethode. Das erste mal wird die ausgelöst, wenn in der vorletzten Zeile ein '\n' ausgegeben wird. Danach scheint der Text aber auch bis zur letzten Zeile zu gehen!?
Genau das ist das Problem. Das Scheint ja alles zusammenzuhaengen.
BlackJack hat geschrieben:Was sollen diese überflüssigen ``return True``\s?
Die werden spaeter notwendig sein, weil die Methoden noch was unterscheiden sollen, aber das ist hierbei irelevant.

Liebe Grueße,
roschi

Verfasst: Dienstag 6. Oktober 2009, 14:31
von roschi
So, ich habe jetzt noch einiges ausprobiert, aber nie habe ich es gescahfft, zu verhindern, dass mehrere Strings, die mit \n enden, in einer Zeile ausgegeben werden.

Ich schildere mal mein Vorhaben, vielleicht kennt jemand ja eine andere, eventuell bessere Loesung.

Ich moechte ein Programm schreiben, das das Terminal in zwei Teile teilt. Im oberen, großen Teil soll durch den Aufruf einer schreib-Funktion Text ausgegeben werden. Das kann am Ende sehr viel Text werden, mit unter bis zu 5000 Zeilen. Dieser Bereich soll nach unten scrollen, wenn Text laenger als das Terminal wird. Ich moechte aber trotzdem den alten Text speichern, damit ich spaeter manuell wieder hochscrollen kann.
Im unteren Teil soll Texteingabe moeglich sein, und die eingegebenen Kommandos sollen dann verarbeitet werden.

Vielleicht hilft das ja beim Finden einer Problemloesung.

Vielen Dank schonmal!

roschi

Verfasst: Dienstag 6. Oktober 2009, 14:35
von DasIch
Schau dir mal das cmd Modul aus der stdlib an. Ein gutes Beispiel für die Nutzung ist die sqlalchemy_console.

Verfasst: Dienstag 6. Oktober 2009, 16:00
von roschi
Mit dem cmd-Modul kann man zwar eine Art Shell bauen, aber ich brauche einen geteilten Bildschirm, bei dem ich beide Teile separat schreiben und lesen kann.

Gibt es da keine Moeglichkeiten?

Verfasst: Dienstag 6. Oktober 2009, 17:05
von BlackJack
@roschi: Also für mich klingt es nach einem Fall für `urwid`.

Verfasst: Dienstag 6. Oktober 2009, 19:10
von roschi
BlackJack hat geschrieben:@roschi: Also für mich klingt es nach einem Fall für `urwid`.
Koenntest du mir dann ein kleines Beispiel fuer sowas geben? oder ein gutes Tutorial, was diese Schwerpunkte behandelt? Das waere sehr nett.

Verfasst: Mittwoch 7. Oktober 2009, 07:25
von BlackJack
@roschi: Bei den Beispielen, die von anderen Benutzern beigesteuert wurden, gibt es einen IRC-Clienten, mit der typischen Aufteilung "letzte Zeile == Eingabe, darüber scrollende Ausgabe". Schau Dir da mal die `Screen`-Klasse an: inyyssonen_twisted_irc.Screen.

Verfasst: Mittwoch 7. Oktober 2009, 08:27
von roschi
BlackJack hat geschrieben:@roschi: Bei den Beispielen, die von anderen Benutzern beigesteuert wurden, gibt es einen IRC-Clienten, mit der typischen Aufteilung "letzte Zeile == Eingabe, darüber scrollende Ausgabe". Schau Dir da mal die `Screen`-Klasse an: inyyssonen_twisted_irc.Screen.
Vielen dank! Das sieht sehr gut aus.
Kannst du mir nun noch sagen, was fuer ein Wrapper am Ende des Scripts aufgerufen wird? wie muss ich das handhaben?

Ich habe mir jetzt die ``Screen``-Klasse etwas angepasst, und selber das ``tui`` instanzieren lassen.

Das ganze sieht jetzt so aus:

Code: Alles auswählen

import urwid.curses_display
import urwid

class Screen(object):
  def __init__(self):
    self.tui = urwid.curses_display.Screen()
    self.size = self.tui.get_cols_rows()
    self.lines = [urwid.Text('Hello')]
    self.listbox = urwid.ListBox(self.lines)
    self.input = urwid.Edit()
    self.frame = urwid.Frame(self.listbox, footer = self.input)
    self.frame.set_focus('footer')
    self.redisplay()

  def addLine(self, text):
    self.lines.append(urwid.Text(text))
    self.listbox.set_focus(len(self.lines) - 1)
    self.redisplay()

  def redisplay(self):
    canvas = self.frame.render(self.size, focus = True)
    self.tui.draw_screen(self.size, canvas)

  def doRead(self):
    keys = self.tui.get_input()
    for key in keys:
      if key == 'window resize':
        self.size = self.tui.get_cols_rows()
      elif key == 'enter':
        text = self.input.get_edit_text()
        self.input.set_edit_text('')
        self.addLine(text)
        #self.irc.sendLine(text)
      elif key in ('up', 'down', 'page up', 'page down'):
        self.listbox.keypress(self.size, key)
      else:
        self.frame.keypress(self.size, key)
    self.redisplay()
Wie muss ich das nun mit dieser ``run_wrapper``-Methode machen? Wie und wo muss ich die aufrufen?

Vielen dank und liebe Grueße,
roschi

Verfasst: Mittwoch 7. Oktober 2009, 21:43
von roschi
Alles klar, ich habe es geschafft!

Kann mir nun noch jemand sagen, wie ich mit urwid in einem ListBox-Item mehrere Textattribute setze? Ich moechte zum Beispiel erst ein Wort in gruen, dann eins ohne Faerbung, dann eines in rot.

Ich muesste alsp praktisch Text-Widgets zusammenfassen koennen.

Es waere genial, wenn mir dabei noch jemand helfen koennte.

Vielen herzlichen Dank!

roschi