LINUX.ORG.RU

Python, urwid (TUI) и композитные виджеты

 ,


1

2

Чет я туплю и никак не осилю эту штуку.

Вообщем мне нужен выделяемый (selectable) композитный виджет (тобеш состоящий из других виджетов) в котором некоторые его компоненты будут раскрашены в иные цвета - но при этом при выделении корректно сменять цвет. Вот какраз с последним я и застрял. Если все вложенные виджеты в единой раскраске, все нормально. Но если какойнибуть из них раскрасить иначе, то при выделении у него цвет не сменяется.
Уже как только ни пробовал, нифига не выходит.
В примерах подобной штуки нигде нету, такчто глянуть некуда. но поидеи штукаж простая.

Подскажите ктонить как это сделать?

widgets.py

#!/usr/bin/python3

import urwid
from urwid import WidgetWrap, Pile, Columns, Text, Padding, AttrWrap, SelectableIcon, ListBox, Divider
from urwid.command_map import ACTIVATE
from urwid.util import is_mouse_press
import urwid.raw_display

urwid.set_encoding("UTF-8")

class FilterItem(WidgetWrap):
   def __init__(self, name, descr, count):
      self._w_name=Text(f'{name}')
      self._w_count=Text(f'{count}', align='right')
      self._w_descr=Text(f'{descr}')

      #! если убрать эту строку, все работает коректно, если оставить - описание элемента перестает получать фокус
      # self._w_descr=AttrWrap(self._w_descr, 'style2', 'style2-focus')

      w=Pile([
         Columns([
            self._w_name, self._w_count
         ], 1),
         self._w_descr
      ])

      w=AttrWrap(w, 'style1', 'style1-focus')
      self.__super.__init__(w)

   def selectable(self):
      return True

class FiltersList(urwid.SimpleFocusListWalker):
   def __init__(self):
      grp=[]
      data=[
         FilterItem('Item name', 'Descreption text', 1),
         FilterItem('Item name', 'Descreption text', 1),
         Divider('⎼'),
         FilterItem('Item name', 'Descreption text', 1),
         FilterItem('Item name', 'Descreption text', 1),
         FilterItem('Item name', 'Descreption text', 1),
     ]
      super().__init__(data)

class DialogList(urwid.SimpleFocusListWalker):
   def __init__(self):
      grp=[]
      data=[
         Dialog('d1', [
            {'id':'m1', 'isIncoming':True, 'from':'user1@mail.ru', 'to':['byaka.life@gmail.com', 'user2@mail.ru'], 'subject':'Some message 1', 'timestamp':datetime_now()-datetime_delta(days=1), 'bodyPlain':'Some text 1', 'bodyHtml':''},
         ]),
         Dialog('d2', [
            {'id':'m1', 'isIncoming':True, 'from':'user1@mail.ru', 'to':['byaka.life@gmail.com', 'user2@mail.ru'], 'subject':'Some message 1', 'timestamp':datetime_now()-datetime_delta(days=1), 'bodyPlain':'Some text 1', 'bodyHtml':''},
         ]),

      ]
      self.__super.__init__(data)

class ScreenMain(object):
   palette = [
      ('body', 'white', 'dark blue'),
      ('style1', 'white', 'dark magenta'),
      ('style1-focus', 'black', 'brown'),
      ('style2', 'white', 'light magenta'),
      ('style2-focus', 'black', 'yellow'),
   ]

   def __init__(self):
      self.screen=urwid.raw_display.Screen()
      self.screen.set_terminal_properties(colors=16)
      self.screen.reset_default_terminal_palette()
      self.screen.register_palette(self.palette)

      self.layout=AttrWrap(Columns([
         ('weight', 2, AttrWrap(Pile([
            Padding(ListBox(FiltersList()), left=1, right=1),
         ]), 'style1')),  # sidebar

         ('weight', 8, Pile([

         ])),  # wrapper

      ], 1), 'body')
      self.layout.set_focus_column(0)
      self.layout=urwid.Frame(self.layout)

   def run(self):
      self.loop=urwid.MainLoop(self.layout, screen=self.screen, unhandled_input=self.input)
      self.loop.run()

   def input(self, input, raw_input=None):
      if 'q' in input or 'Q' in input: raise urwid.ExitMainLoop()
      return []

if __name__ == '__main__':
   ScreenMain().run()


собственно 17 строка.

Где ошибка?

Вощем если кому интересно - нормального решения я не нашел, но вот такой костыль вполне работает и вродебы более-менее универсально.

#!/usr/bin/python3

import urwid
from urwid import WidgetWrap, Pile, Columns, Text, Padding, AttrWrap, SelectableIcon, ListBox, Divider
from urwid.command_map import ACTIVATE
from urwid.util import is_mouse_press
import urwid.raw_display

urwid.set_encoding("UTF-8")

class AttrWrapEx(AttrWrap):
   def __init__(self, w, attr, focus_attr=None):
      self._original_nonfocus=attr
      self._original_focus=focus_attr
      self.__super.__init__(w, attr, focus_attr=None)

class FilterItem(WidgetWrap):
   def __init__(self, name, descr, count):
      self._w_name=Text(f'{name}')
      self._w_count=Text(f'{count}', align='right')
      self._w_descr=Text(f'{descr}')

      #~ если заменить на AttrWrap - описание элемента перестает получать фокус
      self._w_descr=AttrWrapEx(self._w_descr, 'style2', 'style2-focus')

      w=Pile([
         Columns([
            self._w_name, self._w_count
         ], 1),
         self._w_descr
      ])

      w=AttrWrap(w, 'style1', 'style1-focus')
      self.__super.__init__(w)

   def selectable(self):
      return True

   def render(self, size, focus=False):
      #~ this code forces all children `AttrWrapEx` to swith focus
      tArr=[self._w]
      while tArr:
         w=tArr.pop()
         if isinstance(w, tuple): w=w[0]
         if isinstance(w, AttrWrapEx):
            new_attr=w._original_focus if focus else w._original_nonfocus
            if w.attr!=new_attr:
               w.attr=new_attr
               w._invalidate()
         if hasattr(w, '_original_widget'):  # for AttrWrap, AttrMap etc
            tArr.append(w._original_widget)
         if hasattr(w, 'contents'):  # for Pile, Columns etc
            tArr.extend(w.contents)
      return self.__super.render(size, focus)

class FiltersList(urwid.SimpleFocusListWalker):
   def __init__(self):
      grp=[]
      data=[
         FilterItem('Item name', 'Descreption text', 1),
         FilterItem('Item name', 'Descreption text', 1),
         Divider('⎼'),
         FilterItem('Item name', 'Descreption text', 1),
         FilterItem('Item name', 'Descreption text', 1),
         FilterItem('Item name', 'Descreption text', 1),
     ]
      super().__init__(data)

class DialogList(urwid.SimpleFocusListWalker):
   def __init__(self):
      grp=[]
      data=[
         Dialog('d1', [
            {'id':'m1', 'isIncoming':True, 'from':'user1@mail.ru', 'to':['byaka.life@gmail.com', 'user2@mail.ru'], 'subject':'Some message 1', 'timestamp':datetime_now()-datetime_delta(days=1), 'bodyPlain':'Some text 1', 'bodyHtml':''},
         ]),
         Dialog('d2', [
            {'id':'m1', 'isIncoming':True, 'from':'user1@mail.ru', 'to':['byaka.life@gmail.com', 'user2@mail.ru'], 'subject':'Some message 1', 'timestamp':datetime_now()-datetime_delta(days=1), 'bodyPlain':'Some text 1', 'bodyHtml':''},
         ]),

      ]
      self.__super.__init__(data)

class ScreenMain(object):
   palette = [
      ('body', 'white', 'dark blue'),
      ('style1', 'white', 'dark magenta'),
      ('style1-focus', 'black', 'brown'),
      ('style2', 'white', 'light magenta'),
      ('style2-focus', 'black', 'yellow'),
   ]

   def __init__(self):
      self.screen=urwid.raw_display.Screen()
      self.screen.set_terminal_properties(colors=16)
      self.screen.reset_default_terminal_palette()
      self.screen.register_palette(self.palette)

      self.layout=AttrWrap(Columns([
         ('weight', 2, AttrWrap(Pile([
            Padding(ListBox(FiltersList()), left=1, right=1),
         ]), 'style1')),  # sidebar

         ('weight', 8, Pile([

         ])),  # wrapper

      ], 1), 'body')
      self.layout.set_focus_column(0)
      self.layout=urwid.Frame(self.layout)

   def run(self):
      self.loop=urwid.MainLoop(self.layout, screen=self.screen, unhandled_input=self.input)
      self.loop.run()

   def input(self, input, raw_input=None):
      if 'q' in input or 'Q' in input: raise urwid.ExitMainLoop()
      return []

if __name__ == '__main__':
   ScreenMain().run()

genryRar ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.