Source code for kvalikirstu2.header_panel

"""A module for a panel in the main gui window that contains header related controls.
"""
from bisect import bisect
from copy import deepcopy
from pubsub import pub
import wx
from wx import ribbon as RB
from wx.lib.mixins import listctrl
from kvalikirstu2 import gui_model
from kvalikirstu2.gui_model import HeaderStatus
from kvalikirstu2 import gui_utils
from kvalikirstu2 import study
from kvalikirstu2.localization import _

ALIGN = study.Align
ALIGN_STR = {ALIGN.LEFT: _("left"),
             ALIGN.CENTER: _("center"),
             ALIGN.RIGHT: _("right")}


[docs]class StatusError(Exception): """An exception class to report issues with setting the status for a header."""
[docs]class ValueEditDlg(wx.Dialog): """A Dialog window for editing values associated with a header in a subject. """ def __init__(self, parent, value_map, title): super().__init__(parent, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) self.unsaved_changes = False self.values = {val_pair[1]: new_value for val_pair, new_value in value_map.items()} self.title = title self.padding = parent.padding self._init_frame() def _init_frame(self): self.panel = wx.Panel(self) self.sizer = wx.BoxSizer(wx.VERTICAL) self.list_ctrl = EditableListCtrl(self.panel, editable_cols=[0], style=wx.LC_REPORT | wx.BORDER_SUNKEN, size=(400, 200)) self.init_list_ctrl() self.init_btns() self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_popup_menu) self.Bind(wx.EVT_CLOSE, lambda evt: self.on_close()) gui_utils.add_border_and_fit(self.panel, self.sizer, self) self.SetTitle(_("Edit values")) self.Center()
[docs] def init_list_ctrl(self): """Inits list ctrl elements.""" self.list_ctrl.InsertColumn(0, _("Value"), width=200) self.list_ctrl.InsertColumn(1, _("Original value")) self.list_ctrl.resizeLastColumn(100) index = 0 for orig_value, value in self.values.items(): self.list_ctrl.Append([value]) self.list_ctrl.SetItem(index, 1, orig_value) self.list_ctrl.editable_rows.append(index) index += 1 self.list_ctrl.Bind(wx.EVT_LIST_END_LABEL_EDIT, lambda evt: self.remap_edited_values()) self.sizer.Add(wx.StaticText(self.panel, label=_("Header: %s") % self.title), 0, wx.ALL | wx.EXPAND, self.padding) h_sizer = wx.BoxSizer(wx.HORIZONTAL) h_sizer.Add(self.list_ctrl, 1, wx.ALL | wx.EXPAND, self.padding) self.sizer.Add(h_sizer, 1, wx.ALL | wx.EXPAND, self.padding)
[docs] def init_btns(self): """Inits the button sizer. """ btn_sizer = wx.BoxSizer(wx.HORIZONTAL) ok_btn = wx.Button(self.panel, label=_("OK")) cancel_btn = wx.Button(self.panel, label=_("Cancel")) btn_sizer.Add(ok_btn, 0, wx.ALL, self.padding) btn_sizer.Add(cancel_btn, 0, wx.ALL, self.padding) self.sizer.Add(btn_sizer, 0, self.padding) ok_btn.Bind(wx.EVT_BUTTON, lambda evt: self.on_close(wx.ID_OK)) cancel_btn.Bind(wx.EVT_BUTTON, lambda evt: self.on_close())
[docs] def show_popup_menu(self, evt): """Opens the Popup menu when right click detected on a listctrl item. """ menu = wx.Menu() row = evt.GetIndex() self.Bind(wx.EVT_MENU, lambda evt: self.list_ctrl.show_editor_at_row(row), menu.Append(-1, _("Give a new value for all selected"))) self.list_ctrl.PopupMenu(menu, evt.GetPoint())
[docs] def get_updated_values_map(self): """Returns a map with updated values. """ updated_values_map = {} for row_index in range(self.list_ctrl.GetItemCount()): new_value = self.list_ctrl.GetItemText(row_index, 0) orig_value = self.list_ctrl.GetItemText(row_index, 1) if orig_value and new_value != orig_value: key = (self.title, orig_value) updated_values_map[key] = new_value return updated_values_map
[docs] def on_close(self, ret_val=wx.ID_CANCEL): """Asks to save unsaved changes when the window is about to close. """ if self.unsaved_changes and ret_val != wx.ID_OK: with wx.MessageDialog(self, _("Changes found. Keep changes?"), style=wx.YES_NO | wx.CANCEL) as dlg: dlg.SetYesNoLabels(_("Keep"), _("Discard")) return_val = dlg.ShowModal() if return_val == wx.ID_YES: self.EndModal(wx.ID_OK) elif return_val == wx.ID_CANCEL: return self.EndModal(ret_val)
[docs] def refresh_listctrl(self, row_index, new_value): """Remaps a value on the given row with a new value. :param int row_index: The index of the row of the item to edit. :param str new_value: The new value for the item. """ orig_value = self.list_ctrl.GetItemText(row_index, 1) if orig_value != new_value: self.unsaved_changes = True self.list_ctrl.SetItem(row_index, 0, new_value)
[docs] def remap_edited_values(self): """Maps edited values to the old values and changes the item text of the corresponding listctrl rows. """ new_value = self.list_ctrl.editor.GetValue() if self.list_ctrl.GetSelectedItemCount() > 1: item = self.list_ctrl.GetFirstSelected() while item >= 0: self.refresh_listctrl(item, new_value) item = self.list_ctrl.GetNextSelected(item) else: cur_row = self.list_ctrl.curRow self.refresh_listctrl(cur_row, new_value)
[docs]class EditableListCtrl(wx.ListCtrl, listctrl.TextEditMixin, listctrl.ListCtrlAutoWidthMixin): """A list control class that extends wx.ListCtrl, wx.lib.mixins.listctrl.TextEditMixin and wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin. The list control can be set to resize automatically and its text fields can be set editable. """ def __init__(self, parent, style, editable_cols, *args, **kwargs): wx.ListCtrl.__init__(self, parent, style=style, *args, **kwargs) listctrl.TextEditMixin.__init__(self) listctrl.ListCtrlAutoWidthMixin.__init__(self) self.editable_cols = editable_cols self.editable_rows = [] self.first_item_clicked = False self.curRow = None self.col_locs = None
[docs] def set_header_item_colours(self, index, header): """Sets text and background colours for header items in the listctrl. :param int index: The index of the listctrl row to edit. :param HeaderLine header: The HeaderLine object that determines the listctrl row colours. """ if header.builtin: self.SetItemTextColour(index, (80, 80, 80)) else: self.SetItemTextColour(index, (0, 0, 0)) if index % 2 == 0: self.SetItemBackgroundColour(index, wx.Colour(235, 240, 250)) if header.status == HeaderStatus.NOT_HEADER: self.SetItemBackgroundColour(index, wx.Colour(240, 240, 240))
[docs] def show_editor_at_row(self, row): """Activate editor on the first column of the given row. """ self.editor.SetSize(0, self.GetItemRect(row)[1], self.GetColumnWidth(0), -1) self.editor.SetValue(self.GetItem(row, 0).GetText()) self.editor.Show() self.editor.Raise() self.editor.SetSelection(-1, -1) self.editor.SetFocus()
# listctrl.TextEditMixin has a bug that doesn't allow the user to select the first item
[docs] def OnItemSelected(self, evt): """Overwrites OnItemSelected method of listctrl.TextEditMixin so that first item in the list can be selected. """ self.curRow = evt.GetIndex() if self.curRow == 0: self.first_item_clicked = True evt.Skip()
[docs] def open_editor(self, col, row): """Adds editable col and row check before opening editor. """ if col in self.editable_cols and row in self.editable_rows: self.OpenEditor(col, row) self.first_item_clicked = False
[docs] def OnChar(self, event): """Catch the TAB, Shift-TAB, cursor DOWN/UP events. Overrides listctrl.EditableListCtrl.OnChar to fix bug with opening the editor at the right col and taking editable rows and cols into account. """ keycode = event.GetKeyCode() if keycode == wx.WXK_TAB and event.ShiftDown(): self.CloseEditor() if self.curCol-1 >= 0: self.curCol -= 1 # Add to orig OnChar: decreased curCol self.open_editor(self.curCol, self.curRow) elif keycode == wx.WXK_TAB: self.CloseEditor() if self.curCol+1 < self.GetColumnCount(): self.curCol += 1 # Add to orig OnChar: increased curCol self.open_editor(self.curCol, self.curRow) elif keycode == wx.WXK_ESCAPE: self.CloseEditor() elif keycode == wx.WXK_DOWN: self.CloseEditor() if self.curRow+1 < self.GetItemCount(): self._SelectIndex(self.curRow+1) self.open_editor(self.curCol, self.curRow) elif keycode == wx.WXK_UP: self.CloseEditor() if self.curRow > 0: self._SelectIndex(self.curRow-1) self.open_editor(self.curCol, self.curRow) else: event.Skip()
[docs] def OnLeftDown(self, evt=None): """Overwrites OnLeftDown method of listctrl.TextEditMixin so that first item in the list can be selected. Disables editing any other column but the ones specified at init. """ if self.editor.IsShown(): self.CloseEditor() x_pos, y_pos = evt.GetPosition() row, _tmp = self.HitTest((x_pos, y_pos)) if row != self.curRow or (self.curRow == 0 and not self.first_item_clicked): evt.Skip() return if wx.GetMouseState().HasAnyModifiers(): self.Select(row, not self.IsSelected(row)) return self.col_locs = [0] loc = 0 for col_n in range(self.GetColumnCount()): loc = loc + self.GetColumnWidth(col_n) self.col_locs.append(loc) col = bisect(self.col_locs, x_pos+self.GetScrollPos(wx.HORIZONTAL)) - 1 self.open_editor(col, row)
[docs]class HeaderPanel(wx.Panel): """A panel with header editing controls. """ ID_SHOW_BUILTIN = wx.Window.NewControlId() ID_HIDE_BUILTIN = wx.Window.NewControlId() ID_SHOW_NONHEADER = wx.Window.NewControlId() ID_HIDE_NONHEADER = wx.Window.NewControlId() ID_MOVE_OFF = wx.Window.NewControlId() ID_MOVE_ON = wx.Window.NewControlId() def __init__(self, parent, main_win, model, style=wx.TAB_TRAVERSAL): super().__init__(parent, style=style) self.main_win = main_win self.padding = self.main_win.padding self.show_builtin_headers = False self.show_non_headers = False self.model = model self._init_panel() pub.subscribe(self._update_undo_redo, gui_model.Messages.UNDO_REDO_CHANGED) def _update_undo_redo(self, undo, redo): """ """ self.undo_redo_button_bar.EnableButton(wx.ID_UNDO, undo) self.undo_redo_button_bar.EnableButton(wx.ID_REDO, redo) def _init_panel(self): """Init main panel contents. """ sizer = wx.BoxSizer(wx.HORIZONTAL) self.list_ctrl = EditableListCtrl(self, editable_cols=[0], style=wx.LC_REPORT | wx.BORDER_SUNKEN) self._init_listctrl() ribbon_bar = RB.RibbonBar(self, style=RB.RIBBON_BAR_FLOW_VERTICAL | wx.BORDER_THEME) ribbon_page = RB.RibbonPage(ribbon_bar) self.undo_redo_button_bar = gui_utils.new_buttonbar(ribbon_page) self._init_side_ribbon_bar(ribbon_page) gui_utils.set_ribbon_colours(ribbon_bar, hide_panel_label=True) ribbon_bar.Realize() sizer.Add(ribbon_bar, 0, wx.ALL, self.padding) sizer.Add(self.list_ctrl, 1, wx.EXPAND | wx.ALL, self.padding) self.menu = wx.Menu() self._init_popup_menu() border = wx.BoxSizer(wx.VERTICAL) border.Add(sizer, 1, wx.ALL | wx.EXPAND, self.padding) self.SetSizerAndFit(border) self.SetBackgroundColour(gui_utils.COLOUR_XTRA_LIGHT) def _init_side_ribbon_bar(self, ribbon_page): """Inits the side ribbon bar with header edit controls. """ btn_id = self.ID_HIDE_NONHEADER + 1 new_buttonbar = gui_utils.new_buttonbar self.undo_redo_button_bar.AddButton(wx.ID_UNDO, "", wx.ArtProvider.GetBitmap(wx.ART_UNDO, wx.ART_TOOLBAR), _("Undo last header edit")) self.undo_redo_button_bar.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, lambda evt: self.model.undo(), id=wx.ID_UNDO) self.undo_redo_button_bar.AddButton(wx.ID_REDO, "", wx.ArtProvider.GetBitmap(wx.ART_REDO, wx.ART_TOOLBAR), _("Redo last header edit")) self.undo_redo_button_bar.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, lambda evt: self.model.redo(), id=wx.ID_REDO) gui_utils.add_separator_to_ribbon_page(ribbon_page, False, wx.LI_HORIZONTAL) ribbon_tb = new_buttonbar(ribbon_page) ribbon_tb.AddButton(btn_id, _("Background information"), gui_utils.create_bitmap("values"), _("Edit background information values associated with selected header")) ribbon_tb.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, lambda evt: self.open_edit_value_dlg(), id=btn_id) self.main_win.add_button_exists_hook(ribbon_tb, btn_id, gui_model.Messages.STUDY_EXISTS) btn_id += 1 ribbon_tb = new_buttonbar(ribbon_page) ribbon_tb.AddDropdownButton(btn_id, _("Select headers"), gui_utils.create_bitmap("tag"), _("Sets status for all selected headers (keep Shift or Ctrl pressed " "to select multiple lines).")) ribbon_tb.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self._show_status_menu, id=btn_id) ribbon_tb.Bind(RB.EVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED, self._show_status_menu, id=btn_id) self.main_win.add_button_exists_hook(ribbon_tb, btn_id, gui_model.Messages.STUDY_EXISTS) btn_id += 1 ribbon_tb = new_buttonbar(ribbon_page) ribbon_tb.AddDropdownButton(btn_id, _("Align"), gui_utils.create_bitmap("align"), _("Sets alignment for all selected headers (keep Shift or Ctrl " "pressed to select multiple lines).")) ribbon_tb.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self._show_align_menu, id=btn_id) ribbon_tb.Bind(RB.EVT_RIBBONBUTTONBAR_DROPDOWN_CLICKED, self._show_align_menu, id=btn_id) self.main_win.add_button_exists_hook(ribbon_tb, btn_id, gui_model.Messages.STUDY_EXISTS) btn_id += 1 gui_utils.add_separator_to_ribbon_page(ribbon_page, False, wx.LI_HORIZONTAL) builtin_tb = new_buttonbar(ribbon_page) self.add_show_builtin_button(builtin_tb) nonheader_tb = new_buttonbar(ribbon_page) self.add_show_nonheader_button(nonheader_tb) gui_utils.add_separator_to_ribbon_page(ribbon_page, False, wx.LI_HORIZONTAL) move_tb = gui_utils.new_buttonbar(ribbon_page) self.add_move_headers_button(move_tb) def _init_listctrl(self): """Inits the listctrl. """ self.list_ctrl.InsertColumn(0, _("Header"), width=350) self.list_ctrl.InsertColumn(1, _("Original")) self.list_ctrl.InsertColumn(2, _("Align")) self.list_ctrl.InsertColumn(3, _("Status")) self.list_ctrl.setResizeColumn(2) self.list_ctrl.Bind(wx.EVT_LIST_END_LABEL_EDIT, lambda evt: self._remap_edited_header()) self.list_ctrl.Bind(wx.EVT_LIST_KEY_DOWN, self._key_down) self.list_ctrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self._show_popup_menu) pub.subscribe(self.update_listctrl, gui_model.Messages.ALL_HEADERS_CHANGED) pub.subscribe(self._update_header, gui_model.Messages.HEADER_CHANGED) def _init_popup_menu(self): """Inits the pop up menu for the panel. """ menu = self.menu self.Bind(wx.EVT_MENU, lambda evt: self.open_edit_value_dlg(), menu.Append(-1, _("Edit values"))) menu.AppendSeparator() self.Bind(wx.EVT_MENU, lambda evt: self._change_header_property(lambda hdr: hdr.restore_original_header()), menu.Append(-1, _("Restore original header"))) menu.AppendSeparator() self.Bind(wx.EVT_MENU, lambda menu_evt: self.align_selected(ALIGN.LEFT), menu.Append(-1, _("Align left"))) self.Bind(wx.EVT_MENU, lambda menu_evt: self.align_selected(ALIGN.CENTER), menu.Append(-1, _("Align center"))) self.Bind(wx.EVT_MENU, lambda menu_evt: self.align_selected(ALIGN.RIGHT), menu.Append(-1, _("Align right"))) menu.AppendSeparator() self.Bind(wx.EVT_MENU, lambda menu_evt: self.set_header_status(HeaderStatus.SHOW), menu.Append(-1, str(HeaderStatus.SHOW))) self.Bind(wx.EVT_MENU, lambda menu_evt: self.set_header_status(HeaderStatus.HIDE), menu.Append(-1, str(HeaderStatus.HIDE))) self.Bind(wx.EVT_MENU, lambda menu_evt: self.set_header_status(HeaderStatus.NOT_HEADER), menu.Append(-1, str(HeaderStatus.NOT_HEADER))) self.Bind(wx.EVT_MENU, lambda menu_evt: self.set_header_status(HeaderStatus.DAF_HEADER), menu.Append(-1, str(HeaderStatus.DAF_HEADER))) def _show_status_menu(self, evt): """Displays pop up menu for status selection. """ menu = wx.Menu() self.Bind(wx.EVT_MENU, lambda menu_evt: self.set_header_status(HeaderStatus.SHOW), menu.Append(-1, str(HeaderStatus.SHOW))) self.Bind(wx.EVT_MENU, lambda menu_evt: self.set_header_status(HeaderStatus.HIDE), menu.Append(-1, str(HeaderStatus.HIDE))) self.Bind(wx.EVT_MENU, lambda menu_evt: self.set_header_status(HeaderStatus.NOT_HEADER), menu.Append(-1, str(HeaderStatus.NOT_HEADER))) self.Bind(wx.EVT_MENU, lambda menu_evt: self.set_header_status(HeaderStatus.DAF_HEADER), menu.Append(-1, str(HeaderStatus.DAF_HEADER))) evt.PopupMenu(menu) def _show_align_menu(self, evt): """Displays a pop up menu for alignment selection. """ menu = wx.Menu() self.Bind(wx.EVT_MENU, lambda menu_evt: self.align_selected(ALIGN.LEFT), menu.Append(-1, _("Align left"))) self.Bind(wx.EVT_MENU, lambda menu_evt: self.align_selected(ALIGN.CENTER), menu.Append(-1, _("Align center"))) self.Bind(wx.EVT_MENU, lambda menu_evt: self.align_selected(ALIGN.RIGHT), menu.Append(-1, _("Align right"))) evt.PopupMenu(menu) def _show_popup_menu(self, evt): """Opens the Popup menu when right click is detected on a listctrl item. """ # Disable value edit control if more than one header is selected self.menu.FindItemByPosition(0).Enable(self.list_ctrl.GetSelectedItemCount() < 2) self.list_ctrl.PopupMenu(self.menu, evt.GetPoint())
[docs] def add_move_headers_button(self, parent_tb): """Removes and readds the move headers button with an icon and label according to if moving rows is activated. """ active = self.model.move_rows_active btn_id = self.ID_MOVE_ON if active else self.ID_MOVE_OFF title = _("Header ordering") parent_tb.ClearButtons() icon_id = "move_off" if btn_id == self.ID_MOVE_OFF else "move_on" parent_tb.AddButton( btn_id, title, gui_utils.create_bitmap(icon_id), _("Enables or disables moving rows with up/down keys.")) parent_tb.Bind( RB.EVT_RIBBONBUTTONBAR_CLICKED, self.toggle_move_rows, id=btn_id) self.main_win.add_button_exists_hook(parent_tb, btn_id, gui_model.Messages.STUDY_EXISTS)
[docs] def toggle_move_rows(self, evt): """Enables or disables moving listctrl rows. """ ribbon_btn_bar = evt.GetBar() self.model.toggle_move_rows() self.add_move_headers_button(ribbon_btn_bar)
[docs] def add_show_builtin_button(self, parent_tb): """Clears the button bar with the show builtin button and adds a new button with an icon and label according to the value of show_builtin_headers.""" btn_id = self.ID_HIDE_BUILTIN if self.show_builtin_headers else self.ID_SHOW_BUILTIN parent_tb.ClearButtons() btn_title = _("Hide builtin headers") if btn_id == self.ID_HIDE_BUILTIN else _("Show builtin headers") icon_id = "hide" if btn_id == self.ID_HIDE_BUILTIN else "show" tooltip = (_("Hide builtin headers in the list") if btn_id == self.ID_HIDE_BUILTIN else _("Show builtin headers in the list")) parent_tb.AddButton(btn_id, btn_title, gui_utils.create_bitmap(icon_id), tooltip) parent_tb.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.toggle_show_builtin_headers, id=btn_id) self.main_win.add_button_exists_hook(parent_tb, btn_id, gui_model.Messages.STUDY_EXISTS)
[docs] def add_show_nonheader_button(self, parent_tb): """Clears the button bar with the show non headers button and adds a new button with an icon and label according to the value of show_non_headers.""" btn_id = self.ID_HIDE_NONHEADER if self.show_non_headers else self.ID_SHOW_NONHEADER parent_tb.ClearButtons() btn_title = _("Hide non-headers") if btn_id == self.ID_HIDE_NONHEADER else _("Show non-headers") icon_id = "hide" if btn_id == self.ID_HIDE_NONHEADER else "show" tooltip = (_("Hide lines marked as not headers") if btn_id == self.ID_HIDE_NONHEADER else _("Show lines marked as not headers")) parent_tb.AddButton(btn_id, btn_title, gui_utils.create_bitmap(icon_id), tooltip) parent_tb.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.toggle_show_non_headers, id=btn_id) self.main_win.add_button_exists_hook(parent_tb, btn_id, gui_model.Messages.STUDY_EXISTS)
[docs] def toggle_show_builtin_headers(self, evt): """Toggles builtin headers visibility. """ ribbon_btn_bar = evt.GetBar() self.show_builtin_headers = not self.show_builtin_headers self.add_show_builtin_button(ribbon_btn_bar) self.model.set_type_shown(gui_model.HeaderType.BUILTIN, self.show_builtin_headers)
[docs] def toggle_show_non_headers(self, evt): """Toggles non-header line visibility. """ ribbon_btn_bar = evt.GetBar() self.show_non_headers = not self.show_non_headers self.add_show_nonheader_button(ribbon_btn_bar) self.model.set_type_shown(gui_model.HeaderType.NOT_HEADER, self.show_non_headers)
[docs] def open_edit_value_dlg(self): """Opens a dialog window for editing header values. """ if self.list_ctrl.GetSelectedItemCount() != 1: wx.MessageBox(_("Select one header to edit values!"), "Info", wx.OK) return index = self.list_ctrl.GetFirstSelected() header_line = deepcopy(self.model.get_headerline(index)) with ValueEditDlg(self, header_line.get_values(), header_line.original_header) as edit_vals_dlg: return_val = edit_vals_dlg.ShowModal() if return_val == wx.ID_OK: new_header_values = edit_vals_dlg.get_updated_values_map() self._change_header_property( lambda header: header.update_changed_values(new_header_values))
def _key_down(self, event): """Called when user presses a key. Selected header swaps places with header below if pressed key is down key, and with header above if pressed key is up key. """ if event.KeyCode == wx.WXK_UP: if event.Index > 0: self.model.move_up(event.Index) elif event.KeyCode == wx.WXK_DOWN: if event.Index + 1 < self.list_ctrl.GetItemCount(): self.model.move_down(event.Index) def _remap_edited_header(self): """Updates the edited header value. """ self.model.set_row_name(self.list_ctrl.curRow, self.list_ctrl.editor.GetValue()) def _update_header(self, header, index): """Updates header in given index. """ if index in self.list_ctrl.editable_rows: self.list_ctrl.editable_rows.remove(index) self.list_ctrl.SetItem(index, 0, header.edited_header) if header.edited_header != header.original_header: self.list_ctrl.SetItem(index, 1, header.original_header) else: self.list_ctrl.SetItem(index, 1, '') self.list_ctrl.SetItem(index, 2, ALIGN_STR[header.alignment]) self.list_ctrl.SetItem(index, 3, str(header.status)) self.list_ctrl.set_header_item_colours(index, header) if (header.status == HeaderStatus.SHOW or header.status == HeaderStatus.DAF_HEADER) and not header.builtin: self.list_ctrl.editable_rows.append(index)
[docs] def update_listctrl(self, headers): """Updates all header changes to the listctrl. """ self.list_ctrl.DeleteAllItems() self.list_ctrl.editable_rows.clear() for index, header in enumerate(headers): self.list_ctrl.InsertItem(index, header.edited_header) self._update_header(header, index)
def _change_header_property(self, change_func, no_action_condition=None): """Changes a property of the headers selected on the listctrl. :param callable change_func: A function that changes a header property. :param callable no_action_condition: When called with a HeaderLine object, returns True if the property should not be changed. """ indexes = [] item = self.list_ctrl.GetFirstSelected() while item >= 0: indexes.append(item) item = self.list_ctrl.GetNextSelected(item) self.model.change_header_property(indexes, change_func, no_action_condition)
[docs] def align_selected(self, alignment): """Sets the alignment of selected headers. :param str alignment: The new alignment. """ self._change_header_property(lambda header: header.set_align(alignment), lambda header: header.alignment == alignment)
[docs] def set_header_status(self, status): """Changes the status of selected headers. :param int status: The new status. """ self._change_header_property(lambda header: header.set_status(status), lambda header: (header.builtin and status == HeaderStatus.NOT_HEADER) or header.status == status)