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