NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

table.py (14739B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\eveui\data_display\table\table.py
      2 from __future__ import print_function
      3 import __builtin__
      4 import blue
      5 from carbon.common.script.util.commonutils import StripTags
      6 from carbonui.uicore import uicore
      7 import itertoolsext
      8 import localization
      9 import signals
     10 import eveui
     11 from eveui.keyboard import Key
     12 from .row import TableRow, TableHeader
     13 from .constants import COLUMN_LINE_COLOR
     14 BORDER_COLOR = (1, 1, 1, 0.3)
     15 
     16 class WIPTable(eveui.Container):
     17     default_name = 'Table'
     18     default_clipChildren = True
     19     default_bgColor = (0, 0, 0, 0.2)
     20 
     21     def __init__(self, columns, data = None, entry_signal_name = None, row_height = 36, row_select = False, row_multi_select = False, row_menu_callback = None, no_data_message = None, id = None, **kwargs):
     22         super(WIPTable, self).__init__(**kwargs)
     23         self.on_row_click = signals.Signal(signalName='on_row_click')
     24         self.on_row_double_click = signals.Signal(signalName='on_row_double_click')
     25         self.on_row_enter = signals.Signal(signalName='on_row_enter')
     26         self.on_row_exit = signals.Signal(signalName='on_row_exit')
     27         self.on_key_down = signals.Signal(signalName='on_key_down')
     28         self._columns = columns
     29         self._entry_signal_name = entry_signal_name
     30         self._row_height = row_height
     31         self._row_multi_select = row_multi_select
     32         self._row_select = row_select or row_multi_select
     33         self._row_menu_callback = row_menu_callback
     34         self.no_data_message = no_data_message or localization.GetByLabel('UI/Common/NoDataAvailable')
     35         self._id = 'Table_{}_{}'.format(uicore.registry.GetTopLevelWindowAboveItem(self).name, id) if id else None
     36         self._selected_entries = []
     37         self._data = []
     38         self._sorted_data = []
     39         self._table_rows = []
     40         self._column_lines = []
     41         self._sorted_by = None
     42         self._sort_ascending = True
     43         self._total_height = 0
     44         self._settings = __builtin__.settings
     45         self._loaded_data = None
     46         self._load_settings()
     47         self._subscribe_to_columns()
     48         self._layout()
     49         self.data = data
     50         self.body.Copy = self._copy
     51         self.body.OnKeyDown = self._on_key_down
     52         self._select_focus_entry = None
     53 
     54     @property
     55     def data(self):
     56         return self._data
     57 
     58     @data.setter
     59     def data(self, data):
     60         if not data:
     61             self._show_no_data()
     62             return
     63         self.no_data_label.Hide()
     64         old_data = self._data
     65         self._data = data
     66         self._total_height = len(data) * self._row_height
     67         if old_data:
     68             self._update_selected_entries()
     69         if self._sorted_by:
     70             self._sort(self._sorted_by)
     71         else:
     72             self._initial_sort()
     73 
     74     def reset(self):
     75         del self._selected_entries[:]
     76         self._initial_sort()
     77         self.scroll_to(0, 0)
     78 
     79     def refresh_all(self):
     80         for row in self._table_rows:
     81             row.refresh()
     82 
     83     def refresh_entry(self, entry):
     84         row = self._get_entry_row(entry)
     85         if row:
     86             row.refresh()
     87 
     88     def refresh_column(self, id):
     89         column = self._get_column(id)
     90         if column:
     91             column.refresh()
     92 
     93     @property
     94     def selected_entries(self):
     95         return self._selected_entries
     96 
     97     def scroll_to(self, vertical = None, horizontal = None):
     98         if vertical is not None:
     99             self.body.ScrollToVertical(vertical)
    100         if horizontal is not None:
    101             self.body.ScrollToHorizontal(horizontal)
    102 
    103     def _layout(self):
    104         self.content_container = eveui.Container(name='ContentContainer', parent=self, padding=1, clipChildren=True)
    105         self._construct_border()
    106         self._construct_column_lines()
    107         self._construct_header()
    108         self._construct_no_data()
    109         self._construct_body()
    110 
    111     def _construct_border(self):
    112         container = eveui.Container(name='BorderContainer', parent=self)
    113         eveui.Line(parent=container, color=BORDER_COLOR, align=eveui.Align.to_top)
    114         eveui.Line(parent=container, color=BORDER_COLOR, align=eveui.Align.to_bottom)
    115         eveui.Line(parent=container, color=BORDER_COLOR, align=eveui.Align.to_left)
    116         eveui.Line(parent=container, color=BORDER_COLOR, align=eveui.Align.to_right)
    117 
    118     def _construct_column_lines(self):
    119         container = eveui.Container(name='ColumnLinesContainer', parent=self.content_container)
    120         for _ in self._columns:
    121             column_line = eveui.Line(parent=container, align=eveui.Align.to_left_no_push, color=COLUMN_LINE_COLOR)
    122             self._column_lines.append(column_line)
    123 
    124     def _construct_header(self):
    125         self.header = TableHeader(parent=self.content_container, columns=self._columns)
    126 
    127     def _construct_no_data(self):
    128         self.no_data_label = eveui.EveCaptionSmall(parent=self.content_container, align=eveui.Align.to_top, state=eveui.State.hidden, top=20, opacity=0.5)
    129 
    130     def _construct_body(self):
    131         self.body = eveui.ScrollContainer(name='TableBody', parent=self.content_container)
    132         self.body.clipCont._OnSizeChange_NoBlock = self._on_container_size_change
    133         for _ in range(20):
    134             row = TableRow(columns=self._columns, on_click=self._handle_row_click, on_double_click=self._handle_row_double_click, row_menu_callback=self._row_menu_callback, on_row_enter=self._handle_row_enter, on_row_exit=self._handle_row_exit, entry_signal_name=self._entry_signal_name, parent=self.body, height=self._row_height)
    135             self._table_rows.append(row)
    136 
    137     def _copy(self, *args):
    138         data = []
    139         for entry in self.selected_entries:
    140             entry_data = []
    141             for column in self._columns:
    142                 entry_data.append(str(column.get_copy_data(entry)))
    143 
    144             data.append('\t'.join(entry_data))
    145 
    146         if data:
    147             text = StripTags('\n'.join(data))
    148             blue.pyos.SetClipboardData(text)
    149 
    150     def _on_key_down(self, key, flag):
    151         if key == Key.a and Key.ctrl_or_cmd().is_down:
    152             self._select_all()
    153         elif key == Key.down:
    154             self._select_down()
    155         elif key == Key.up:
    156             self._select_up()
    157         elif key == Key.page_down:
    158             self.body.ScrollByPage(up=False)
    159         elif key == Key.page_up:
    160             self.body.ScrollByPage(up=True)
    161         elif key == Key.home:
    162             print('scroll to top')
    163         elif key == Key.end:
    164             print('scroll to bot')
    165         self.on_key_down(key, flag)
    166 
    167     def Close(self):
    168         super(WIPTable, self).Close()
    169         for column in self._columns:
    170             column.unbind(custom_width=self._adjust_column_width)
    171             column.on_sort.disconnect(self._on_sort)
    172             column.on_save.disconnect(self._save_settings)
    173             column.close()
    174 
    175     def _subscribe_to_columns(self):
    176         for column in self._columns:
    177             column.bind(custom_width=self._adjust_column_width)
    178             column.on_sort.connect(self._on_sort)
    179             column.on_save.connect(self._save_settings)
    180 
    181     def _on_container_size_change(self, *args):
    182         self._adjust_column_width()
    183 
    184     def _adjust_column_width(self, *args):
    185         available_width = self.body.clipCont.GetAbsoluteSize()[0]
    186         col_span = 0
    187         for column in self._columns:
    188             width = column.assigned_width
    189             if width:
    190                 available_width -= width
    191             else:
    192                 col_span += column.col_span
    193 
    194         col_span_width = int(round(available_width / float(col_span))) if col_span else 0
    195         for column in self._columns:
    196             column.update_width(col_span_width)
    197 
    198         self._adjust_lines()
    199 
    200     def _adjust_lines(self):
    201         current_pos = 0
    202         for index, column in enumerate(self._columns):
    203             current_pos += column.current_width
    204             self._column_lines[index].left = current_pos
    205 
    206     def _on_sort(self, column):
    207         if self._sorted_by == column:
    208             self._sort_ascending = not self._sort_ascending
    209         else:
    210             self._sort_ascending = column.default_sort_ascending
    211         self._sort(column)
    212         self._save_settings()
    213 
    214     def _sort(self, column):
    215         self._sorted_by = column
    216         self._sorted_data = sorted(self._data, key=lambda entry: column.get_sort_value(entry))
    217         if not self._sort_ascending:
    218             self._sorted_data.reverse()
    219         self.header.sorting_changed(column, self._sort_ascending)
    220         self.scroll_to(vertical=0)
    221         self._update_rows()
    222 
    223     def _initial_sort(self):
    224         sortable = None
    225         for column in self._columns:
    226             if column.sort_initially:
    227                 sortable = column
    228                 break
    229             if not sortable and column.is_sortable:
    230                 sortable = column
    231 
    232         if sortable:
    233             self._sort_ascending = sortable.default_sort_ascending
    234             self._sort(sortable)
    235         else:
    236             self._sorted_data = list(self._data)
    237             self._update_rows()
    238 
    239     def _handle_row_click(self, row):
    240         if self._row_select:
    241             self._handle_selection(row)
    242         self.on_row_click(row.entry)
    243 
    244     def _handle_row_double_click(self, row):
    245         self.on_row_double_click(row.entry)
    246 
    247     def _handle_row_enter(self, row):
    248         self.on_row_enter(row.entry)
    249 
    250     def _handle_row_exit(self, row):
    251         self.on_row_exit(row.entry)
    252 
    253     def _handle_selection(self, row):
    254         entry = row.entry
    255         if not self._row_multi_select:
    256             self._clear_selected_entries()
    257             self._select_row(row)
    258         elif Key.shift.is_down:
    259             start_index = 0
    260             end_index = self._sorted_data.index(entry)
    261             if len(self._selected_entries) > 0:
    262                 if not self._select_focus_entry:
    263                     self._select_focus_entry = self._selected_entries[len(self._selected_entries) - 1]
    264                 self._clear_selected_entries()
    265                 start_index = self._sorted_data.index(self._select_focus_entry)
    266             if start_index > end_index:
    267                 start_index, end_index = end_index, start_index
    268             self._selected_entries.extend(self._sorted_data[start_index:end_index + 1])
    269             self._refresh_selection()
    270         elif Key.control.is_down:
    271             self._select_focus_entry = entry
    272             if self._is_entry_selected(entry):
    273                 self._deselect_row(row)
    274             else:
    275                 self._select_row(row)
    276         else:
    277             self._select_focus_entry = entry
    278             self._clear_selected_entries()
    279             self._select_row(row)
    280 
    281     def _select_all(self):
    282         del self._selected_entries[:]
    283         for entry in self.data:
    284             self._selected_entries.append(entry)
    285 
    286         for row in self._table_rows:
    287             row.select()
    288 
    289     def _select_down(self):
    290         select_index = 0
    291         if not self._selected_entries:
    292             self.scroll_to(0)
    293         else:
    294             for index, row in enumerate(reversed(self._table_rows)):
    295                 if row.is_selected:
    296                     select_index = len(self._table_rows) - index
    297                     break
    298 
    299         if select_index < len(self._table_rows):
    300             self._handle_selection(self._table_rows[select_index])
    301 
    302     def _select_up(self):
    303         select_index = 0
    304         if not self._selected_entries:
    305             self.scroll_to(0)
    306         else:
    307             for index, row in enumerate(self._table_rows):
    308                 if row.is_selected:
    309                     select_index = index - 1
    310                     break
    311 
    312         if select_index >= 0:
    313             self._handle_selection(self._table_rows[select_index])
    314 
    315     def _refresh_selection(self):
    316         for row in self._table_rows:
    317             if self._is_entry_selected(row.entry):
    318                 row.select()
    319 
    320     def _select_row(self, row):
    321         self._selected_entries.append(row.entry)
    322         row.select()
    323 
    324     def _deselect_row(self, row):
    325         self._selected_entries.remove(row.entry)
    326         row.deselect()
    327 
    328     def _is_entry_selected(self, entry):
    329         return entry in self._selected_entries
    330 
    331     def _clear_selected_entries(self):
    332         for row in self._table_rows:
    333             row.deselect()
    334 
    335         del self._selected_entries[:]
    336 
    337     def _get_entry_row(self, entry):
    338         return itertoolsext.first_or_default(self._table_rows, lambda x: x.entry == entry)
    339 
    340     def _update_selected_entries(self):
    341         for entry in list(self._selected_entries):
    342             if entry not in self._sorted_data:
    343                 self._selected_entries.remove(entry)
    344 
    345     def _update_rows(self):
    346         for index, row in enumerate(self._table_rows):
    347             entry = self._sorted_data[index] if index < len(self._sorted_data) else None
    348             is_selected = entry in self._selected_entries
    349             row.set_entry(entry, is_selected)
    350 
    351     def _show_no_data(self):
    352         self.no_data_label.text = u'<center>{}</center>'.format(self.no_data_message)
    353         self.no_data_label.Show()
    354 
    355     def _load_settings(self):
    356         if not self._id:
    357             return
    358         self._loaded_data = self._settings.char.ui.Get(self._id, None)
    359         if not self._loaded_data:
    360             return
    361         self._load_sort()
    362         self._load_columns()
    363 
    364     def _load_sort(self):
    365         sorted_by = self._loaded_data.get('sorted_by', None)
    366         if not sorted_by:
    367             return
    368         column = self._get_column(sorted_by)
    369         if column and column.is_sortable:
    370             self._sort_ascending = self._loaded_data.get('sort_ascending', column.default_sort_ascending)
    371             self._sorted_by = column
    372 
    373     def _load_columns(self):
    374         saved_columns = self._loaded_data.get('columns', None)
    375         if not saved_columns:
    376             return
    377         for saved_column in saved_columns:
    378             column_id = saved_column.get('id', None)
    379             if not column_id:
    380                 continue
    381             column = self._get_column(column_id)
    382             if column:
    383                 column.custom_width = saved_column.get('custom_width', None)
    384 
    385     def _get_column(self, id):
    386         return itertoolsext.first_or_default(self._columns, lambda x: x.id == id)
    387 
    388     def _save_settings(self):
    389         if not self._id:
    390             return
    391         data = dict()
    392         data['sorted_by'] = self._sorted_by.id if self._sorted_by else None
    393         data['sort_ascending'] = self._sort_ascending
    394         data['columns'] = [ self._save_column(column) for column in self._columns ]
    395         self._settings.char.ui.Set(self._id, data)
    396 
    397     def _save_column(self, column):
    398         data = dict()
    399         data['id'] = column.id
    400         data['custom_width'] = column.custom_width
    401         return data