Esempio n. 1
0
class TabularEditor(Editor):
    """ A traits UI editor for editing tabular data (arrays, list of tuples,
        lists of objects, etc).
    """

    #-- Trait Definitions ------------------------------------------------------

    # The event fired when a table update is needed:
    update = Event

    # The event fired when a simple repaint is needed:
    refresh = Event

    # The current set of selected items (which one is used depends upon the
    # initial state of the editor factory 'multi_select' trait):
    selected = Any
    multi_selected = List

    # The current set of selected item indices (which one is used depends upon
    # the initial state of the editor factory 'multi_select' trait):
    selected_row = Int(-1)
    multi_selected_rows = List(Int)

    # The most recently actived item and its index:
    activated = Any(comparison_mode=NO_COMPARE)
    activated_row = Int(comparison_mode=NO_COMPARE)

    # The most recent left click data:
    clicked = Instance('TabularEditorEvent')

    # The most recent left double click data:
    dclicked = Instance('TabularEditorEvent')

    # The most recent right click data:
    right_clicked = Instance('TabularEditorEvent')

    # The most recent right double click data:
    right_dclicked = Instance('TabularEditorEvent')

    # The most recent column click data:
    column_clicked = Instance('TabularEditorEvent')

    # The most recent column click data:
    column_right_clicked = Instance('TabularEditorEvent')

    # The event triggering scrolling.
    scroll_to_row = Event(Int)

    # Is the tabular editor scrollable? This value overrides the default.
    scrollable = True

    # Row index of item to select after rebuilding editor list:
    row = Any

    # Should the selected item be edited after rebuilding the editor list:
    edit = Bool(False)

    # The adapter from trait values to editor values:
    adapter = Instance(TabularAdapter)

    # The table model associated with the editor:
    model = Instance(TabularModel)

    # Dictionary mapping image names to QIcons
    images = Any({})

    # Dictionary mapping ImageResource objects to QIcons
    image_resources = Any({})

    # An image being converted:
    image = Image

    header_event_filter = Any()

    widget_factory = Callable(lambda *args, **kwds: _TableView(*args, **kwds))

    #---------------------------------------------------------------------------
    #  Editor interface:
    #---------------------------------------------------------------------------

    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        factory = self.factory
        adapter = self.adapter = factory.adapter
        self.model = TabularModel(editor=self)

        # Create the control
        control = self.control = self.widget_factory(self)

        # Set up the selection listener
        if factory.multi_select:
            self.sync_value(factory.selected,
                            'multi_selected',
                            'both',
                            is_list=True)
            self.sync_value(factory.selected_row,
                            'multi_selected_rows',
                            'both',
                            is_list=True)
        else:
            self.sync_value(factory.selected, 'selected', 'both')
            self.sync_value(factory.selected_row, 'selected_row', 'both')

        # Connect to the mode specific selection handler
        if factory.multi_select:
            slot = self._on_rows_selection
        else:
            slot = self._on_row_selection
        signal = 'selectionChanged(QItemSelection,QItemSelection)'
        QtCore.QObject.connect(self.control.selectionModel(),
                               QtCore.SIGNAL(signal), slot)

        # Synchronize other interesting traits as necessary:
        self.sync_value(factory.update, 'update', 'from')
        self.sync_value(factory.refresh, 'refresh', 'from')
        self.sync_value(factory.activated, 'activated', 'to')
        self.sync_value(factory.activated_row, 'activated_row', 'to')
        self.sync_value(factory.clicked, 'clicked', 'to')
        self.sync_value(factory.dclicked, 'dclicked', 'to')
        self.sync_value(factory.right_clicked, 'right_clicked', 'to')
        self.sync_value(factory.right_dclicked, 'right_dclicked', 'to')
        self.sync_value(factory.column_clicked, 'column_clicked', 'to')
        self.sync_value(factory.column_right_clicked, 'column_right_clicked',
                        'to')
        self.sync_value(factory.scroll_to_row, 'scroll_to_row', 'from')

        # Connect other signals as necessary
        signal = QtCore.SIGNAL('activated(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_activate)
        signal = QtCore.SIGNAL('clicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_click)
        signal = QtCore.SIGNAL('clicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_right_click)
        signal = QtCore.SIGNAL('doubleClicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_dclick)
        signal = QtCore.SIGNAL('sectionClicked(int)')
        QtCore.QObject.connect(control.horizontalHeader(), signal,
                               self._on_column_click)

        control.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        signal = QtCore.SIGNAL('customContextMenuRequested(QPoint)')
        QtCore.QObject.connect(control, signal, self._on_context_menu)

        self.header_event_filter = HeaderEventFilter(self)
        control.horizontalHeader().installEventFilter(self.header_event_filter)

        # Make sure we listen for 'items' changes as well as complete list
        # replacements:
        try:
            self.context_object.on_trait_change(self.update_editor,
                                                self.extended_name + '_items',
                                                dispatch='ui')
        except:
            pass

        # If the user has requested automatic update, attempt to set up the
        # appropriate listeners:
        if factory.auto_update:
            self.context_object.on_trait_change(self.refresh_editor,
                                                self.extended_name + '.-',
                                                dispatch='ui')

        # Create the mapping from user supplied images to QImages:
        for image_resource in factory.images:
            self._add_image(image_resource)

        # Refresh the editor whenever the adapter changes:
        self.on_trait_change(self.refresh_editor,
                             'adapter.+update',
                             dispatch='ui')

        # Rebuild the editor columns and headers whenever the adapter's
        # 'columns' changes:
        self.on_trait_change(self.update_editor,
                             'adapter.columns',
                             dispatch='ui')

    def dispose(self):
        """ Disposes of the contents of an editor.
        """
        self.context_object.on_trait_change(self.update_editor,
                                            self.extended_name + '_items',
                                            remove=True)

        if self.factory.auto_update:
            self.context_object.on_trait_change(self.refresh_editor,
                                                self.extended_name + '.-',
                                                remove=True)

        self.on_trait_change(self.refresh_editor,
                             'adapter.+update',
                             remove=True)
        self.on_trait_change(self.update_editor,
                             'adapter.columns',
                             remove=True)

        self.adapter.cleanup()

        super(TabularEditor, self).dispose()

    def update_editor(self):
        """ Updates the editor when the object trait changes externally to the
            editor.
        """
        if not self._no_update:
            self.model.reset()
            if self.factory.multi_select:
                self._multi_selected_changed(self.multi_selected)
            else:
                self._selected_changed(self.selected)

    #---------------------------------------------------------------------------
    #  TabularEditor interface:
    #---------------------------------------------------------------------------

    def refresh_editor(self):
        """ Requests the table view to redraw itself.
        """
        self.control.viewport().update()

    def callx(self, func, *args, **kw):
        """ Call a function without allowing the editor to update.
        """
        old = self._no_update
        self._no_update = True
        try:
            func(*args, **kw)
        finally:
            self._no_update = old

    def setx(self, **keywords):
        """ Set one or more attributes without allowing the editor to update.
        """
        old = self._no_notify
        self._no_notify = True
        try:
            for name, value in keywords.items():
                setattr(self, name, value)
        finally:
            self._no_notify = old

    #---------------------------------------------------------------------------
    #  UI preference save/restore interface:
    #---------------------------------------------------------------------------

    def restore_prefs(self, prefs):
        """ Restores any saved user preference information associated with the
            editor.
        """
        cws = prefs.get('cached_widths')
        num_columns = len(self.adapter.columns)
        if cws is not None and num_columns == len(cws):
            for column in xrange(num_columns):
                self.control.setColumnWidth(column, cws[column])

    def save_prefs(self):
        """ Returns any user preference information associated with the editor.
        """
        widths = [
            self.control.columnWidth(column)
            for column in xrange(len(self.adapter.columns))
        ]
        return {'cached_widths': widths}

    #---------------------------------------------------------------------------
    #  Private methods:
    #---------------------------------------------------------------------------

    def _add_image(self, image_resource):
        """ Adds a new image to the image map.
        """
        image = image_resource.create_icon()

        self.image_resources[image_resource] = image
        self.images[image_resource.name] = image

        return image

    def _get_image(self, image):
        """ Converts a user specified image to a QIcon.
        """
        if isinstance(image, basestring):
            self.image = image
            image = self.image

        if isinstance(image, ImageResource):
            result = self.image_resources.get(image)
            if result is not None:
                return result
            return self._add_image(image)

        return self.images.get(image)

    def _mouse_click(self, index, trait):
        """ Generate a TabularEditorEvent event for a specified model index and
            editor trait name.
        """
        event = TabularEditorEvent(editor=self,
                                   row=index.row(),
                                   column=index.column())
        setattr(self, trait, event)

    #-- Trait Event Handlers ---------------------------------------------------

    def _update_changed(self):
        self.update_editor()

    def _refresh_changed(self):
        self.refresh_editor()

    def _selected_changed(self, new):
        if not self._no_update:
            try:
                selected_row = self.value.index(new)
            except:
                pass
            else:
                self._selected_row_changed(selected_row)

    def _selected_row_changed(self, selected_row):
        if not self._no_update:
            smodel = self.control.selectionModel()
            if selected_row == -1:
                smodel.clearSelection()
            else:
                smodel.select(
                    self.model.index(selected_row, 0),
                    QtGui.QItemSelectionModel.ClearAndSelect
                    | QtGui.QItemSelectionModel.Rows)

    def _multi_selected_changed(self, new):
        if not self._no_update:
            values = self.value
            try:
                rows = [values.index(i) for i in new]
            except:
                pass
            else:
                self._multi_selected_rows_changed(rows)

    def _multi_selected_items_changed(self, event):
        values = self.value
        try:
            added = [values.index(item) for item in event.added]
            removed = [values.index(item) for item in event.removed]
        except:
            pass
        else:
            list_event = TraitListEvent(0, added, removed)
            self._multi_selected_rows_items_changed(list_event)

    def _multi_selected_rows_changed(self, selected_rows):
        if not self._no_update:
            smodel = self.control.selectionModel()
            selection = QtGui.QItemSelection()
            for row in selected_rows:
                selection.select(self.model.index(row, 0),
                                 self.model.index(row, 0))
            smodel.clearSelection()
            smodel.select(
                selection, QtGui.QItemSelectionModel.Select
                | QtGui.QItemSelectionModel.Rows)

    def _multi_selected_rows_items_changed(self, event):
        smodel = self.control.selectionModel()
        for row in event.removed:
            smodel.select(
                self.model.index(row, 0), QtGui.QItemSelectionModel.Deselect
                | QtGui.QItemSelectionModel.Rows)
        for row in event.added:
            smodel.select(
                self.model.index(row, 0), QtGui.QItemSelectionModel.Select
                | QtGui.QItemSelectionModel.Rows)

    scroll_to_row_hint_map = {
        'center': QtGui.QTableView.PositionAtCenter,
        'top': QtGui.QTableView.PositionAtTop,
        'bottom': QtGui.QTableView.PositionAtBottom,
        'visible': QtGui.QTableView.EnsureVisible,
    }

    def _scroll_to_row_changed(self, row):
        """ Scroll to the given row.
        """
        scroll_hint = self.scroll_to_row_hint_map.get(
            self.factory.scroll_to_row_hint, self.control.PositionAtCenter)
        self.control.scrollTo(self.model.index(row, 0), scroll_hint)

    #-- Table Control Event Handlers -------------------------------------------

    def _on_activate(self, index):
        """ Handle a cell being activated.
        """
        self.activated_row = row = index.row()
        self.activated = self.adapter.get_item(self.object, self.name, row)

    def _on_click(self, index):
        """ Handle a cell being clicked.
        """
        self._mouse_click(index, 'clicked')

    def _on_dclick(self, index):
        """ Handle a cell being double clicked.
        """
        self._mouse_click(index, 'dclicked')

    def _on_column_click(self, column):
        event = TabularEditorEvent(editor=self, row=0, column=column)
        setattr(self, 'column_clicked', event)

    def _on_right_click(self, column):
        event = TabularEditorEvent(editor=self, row=0, column=column)
        setattr(self, 'right_clicked', event)

    def _on_column_right_click(self, column):
        event = TabularEditorEvent(editor=self, row=0, column=column)
        setattr(self, 'column_right_clicked', event)

    def _on_row_selection(self, added, removed):
        """ Handle the row selection being changed.
        """
        self._no_update = True
        try:
            indexes = self.control.selectionModel().selectedRows()
            if len(indexes):
                self.selected_row = indexes[0].row()
                self.selected = self.adapter.get_item(self.object, self.name,
                                                      self.selected_row)
            else:
                self.selected_row = -1
                self.selected = None
        finally:
            self._no_update = False

    def _on_rows_selection(self, added, removed):
        """ Handle the rows selection being changed.
        """
        self._no_update = True
        try:
            indexes = self.control.selectionModel().selectedRows()
            selected_rows = []
            selected = []
            for index in indexes:
                row = index.row()
                selected_rows.append(row)
                selected.append(
                    self.adapter.get_item(self.object, self.name, row))
            self.multi_selected_rows = selected_rows
            self.multi_selected = selected
        finally:
            self._no_update = False

    def _on_context_menu(self, pos):
        column, row = self.control.columnAt(pos.x()), self.control.rowAt(
            pos.y())
        menu = self.adapter.get_menu(self.object, self.name, row, column)
        if menu:
            self._menu_context = {
                'selection': self.object,
                'object': self.object,
                'editor': self,
                'column': column,
                'row': row,
                'item': self.adapter.get_item(self.object, self.name, row),
                'info': self.ui.info,
                'handler': self.ui.handler
            }
            qmenu = menu.create_menu(self.control, self)
            qmenu.exec_(self.control.mapToGlobal(pos))
            self._menu_context = None

    def _on_column_context_menu(self, pos):
        column = self.control.columnAt(pos.x())
        menu = self.adapter.get_column_menu(self.object, self.name, -1, column)
        if menu:
            self._menu_context = {
                'selection': self.object,
                'object': self.object,
                'editor': self,
                'column': column,
                'info': self.ui.info,
                'handler': self.ui.handler
            }
            qmenu = menu.create_menu(self.control, self)
            qmenu.exec_(self.control.mapToGlobal(pos))
            self._menu_context = None
        else:
            #If no menu is defined on the adapter, just trigger a click event.
            self._on_column_right_click(column)
Esempio n. 2
0
    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        factory = self.factory
        adapter = self.adapter = factory.adapter
        self.model = TabularModel(editor=self)

        # Create the control
        control = self.control = self.widget_factory(self)

        # Set up the selection listener
        if factory.multi_select:
            self.sync_value(factory.selected,
                            'multi_selected',
                            'both',
                            is_list=True)
            self.sync_value(factory.selected_row,
                            'multi_selected_rows',
                            'both',
                            is_list=True)
        else:
            self.sync_value(factory.selected, 'selected', 'both')
            self.sync_value(factory.selected_row, 'selected_row', 'both')

        # Connect to the mode specific selection handler
        if factory.multi_select:
            slot = self._on_rows_selection
        else:
            slot = self._on_row_selection
        signal = 'selectionChanged(QItemSelection,QItemSelection)'
        QtCore.QObject.connect(self.control.selectionModel(),
                               QtCore.SIGNAL(signal), slot)

        # Synchronize other interesting traits as necessary:
        self.sync_value(factory.update, 'update', 'from')
        self.sync_value(factory.refresh, 'refresh', 'from')
        self.sync_value(factory.activated, 'activated', 'to')
        self.sync_value(factory.activated_row, 'activated_row', 'to')
        self.sync_value(factory.clicked, 'clicked', 'to')
        self.sync_value(factory.dclicked, 'dclicked', 'to')
        self.sync_value(factory.right_clicked, 'right_clicked', 'to')
        self.sync_value(factory.right_dclicked, 'right_dclicked', 'to')
        self.sync_value(factory.column_clicked, 'column_clicked', 'to')
        self.sync_value(factory.column_right_clicked, 'column_right_clicked',
                        'to')
        self.sync_value(factory.scroll_to_row, 'scroll_to_row', 'from')

        # Connect other signals as necessary
        signal = QtCore.SIGNAL('activated(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_activate)
        signal = QtCore.SIGNAL('clicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_click)
        signal = QtCore.SIGNAL('clicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_right_click)
        signal = QtCore.SIGNAL('doubleClicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_dclick)
        signal = QtCore.SIGNAL('sectionClicked(int)')
        QtCore.QObject.connect(control.horizontalHeader(), signal,
                               self._on_column_click)

        control.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        signal = QtCore.SIGNAL('customContextMenuRequested(QPoint)')
        QtCore.QObject.connect(control, signal, self._on_context_menu)

        self.header_event_filter = HeaderEventFilter(self)
        control.horizontalHeader().installEventFilter(self.header_event_filter)

        # Make sure we listen for 'items' changes as well as complete list
        # replacements:
        try:
            self.context_object.on_trait_change(self.update_editor,
                                                self.extended_name + '_items',
                                                dispatch='ui')
        except:
            pass

        # If the user has requested automatic update, attempt to set up the
        # appropriate listeners:
        if factory.auto_update:
            self.context_object.on_trait_change(self.refresh_editor,
                                                self.extended_name + '.-',
                                                dispatch='ui')

        # Create the mapping from user supplied images to QImages:
        for image_resource in factory.images:
            self._add_image(image_resource)

        # Refresh the editor whenever the adapter changes:
        self.on_trait_change(self.refresh_editor,
                             'adapter.+update',
                             dispatch='ui')

        # Rebuild the editor columns and headers whenever the adapter's
        # 'columns' changes:
        self.on_trait_change(self.update_editor,
                             'adapter.columns',
                             dispatch='ui')
Esempio n. 3
0
    def init (self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        factory = self.factory
        adapter = self.adapter = factory.adapter
        self.model = TabularModel(editor=self)

        # Create the control
        control = self.control = self.widget_factory(self)

        # Set up the selection listener
        if factory.multi_select:
            self.sync_value(factory.selected, 'multi_selected', 'both',
                            is_list=True)
            self.sync_value(factory.selected_row, 'multi_selected_rows','both',
                            is_list=True)
        else:
            self.sync_value(factory.selected, 'selected', 'both')
            self.sync_value(factory.selected_row, 'selected_row', 'both')

        # Connect to the mode specific selection handler
        if factory.multi_select:
            slot = self._on_rows_selection
        else:
            slot = self._on_row_selection
        signal = 'selectionChanged(QItemSelection,QItemSelection)'
        QtCore.QObject.connect(self.control.selectionModel(),
                               QtCore.SIGNAL(signal), slot)

        # Synchronize other interesting traits as necessary:
        self.sync_value(factory.update, 'update', 'from')
        self.sync_value(factory.refresh, 'refresh', 'from')
        self.sync_value(factory.activated,     'activated',     'to')
        self.sync_value(factory.activated_row, 'activated_row', 'to')
        self.sync_value(factory.clicked,  'clicked',  'to')
        self.sync_value(factory.dclicked, 'dclicked', 'to')
        self.sync_value(factory.right_clicked,  'right_clicked',  'to')
        self.sync_value(factory.right_dclicked, 'right_dclicked', 'to')
        self.sync_value(factory.column_clicked, 'column_clicked', 'to')
        self.sync_value(factory.column_right_clicked, 'column_right_clicked', 'to')
        self.sync_value(factory.scroll_to_row, 'scroll_to_row', 'from')

        # Connect other signals as necessary
        signal = QtCore.SIGNAL('activated(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_activate)
        signal = QtCore.SIGNAL('clicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_click)
        signal = QtCore.SIGNAL('clicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_right_click)
        signal = QtCore.SIGNAL('doubleClicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_dclick)
        signal = QtCore.SIGNAL('sectionClicked(int)')
        QtCore.QObject.connect(control.horizontalHeader(), signal, self._on_column_click)

        control.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        signal = QtCore.SIGNAL('customContextMenuRequested(QPoint)')
        QtCore.QObject.connect(control, signal, self._on_context_menu)

        self.header_event_filter = HeaderEventFilter(self)
        control.horizontalHeader().installEventFilter(self.header_event_filter)

        # Make sure we listen for 'items' changes as well as complete list
        # replacements:
        try:
            self.context_object.on_trait_change(
                self.update_editor, self.extended_name+'_items', dispatch='ui')
        except:
            pass

        # If the user has requested automatic update, attempt to set up the
        # appropriate listeners:
        if factory.auto_update:
            self.context_object.on_trait_change(
                self.refresh_editor, self.extended_name + '.-', dispatch='ui')

        # Create the mapping from user supplied images to QImages:
        for image_resource in factory.images:
            self._add_image(image_resource)

        # Refresh the editor whenever the adapter changes:
        self.on_trait_change(self.refresh_editor, 'adapter.+update',
                             dispatch='ui')

        # Rebuild the editor columns and headers whenever the adapter's
        # 'columns' changes:
        self.on_trait_change(self.update_editor, 'adapter.columns',
                             dispatch='ui')
Esempio n. 4
0
class TabularEditor(Editor):
    """ A traits UI editor for editing tabular data (arrays, list of tuples,
        lists of objects, etc).
    """

    #-- Trait Definitions ------------------------------------------------------

    # The event fired when a table update is needed:
    update = Event

    # The event fired when a simple repaint is needed:
    refresh = Event

    # The current set of selected items (which one is used depends upon the
    # initial state of the editor factory 'multi_select' trait):
    selected       = Any
    multi_selected = List

    # The current set of selected item indices (which one is used depends upon
    # the initial state of the editor factory 'multi_select' trait):
    selected_row        = Int(-1)
    multi_selected_rows = List(Int)

    # The most recently actived item and its index:
    activated     = Any(comparison_mode=NO_COMPARE)
    activated_row = Int(comparison_mode=NO_COMPARE)

    # The most recent left click data:
    clicked = Instance('TabularEditorEvent')

    # The most recent left double click data:
    dclicked = Instance('TabularEditorEvent')

    # The most recent right click data:
    right_clicked = Instance('TabularEditorEvent')

    # The most recent right double click data:
    right_dclicked = Instance('TabularEditorEvent')

    # The most recent column click data:
    column_clicked = Instance('TabularEditorEvent')

    # The most recent column click data:
    column_right_clicked = Instance('TabularEditorEvent')

    # The event triggering scrolling.
    scroll_to_row = Event(Int)

    # Is the tabular editor scrollable? This value overrides the default.
    scrollable = True

    # Row index of item to select after rebuilding editor list:
    row = Any

    # Should the selected item be edited after rebuilding the editor list:
    edit = Bool(False)

    # The adapter from trait values to editor values:
    adapter = Instance(TabularAdapter)

    # The table model associated with the editor:
    model = Instance(TabularModel)

    # Dictionary mapping image names to QIcons
    images = Any({})

    # Dictionary mapping ImageResource objects to QIcons
    image_resources = Any({})

    # An image being converted:
    image = Image

    header_event_filter = Any()

    widget_factory = Callable(lambda *args, **kwds: _TableView(*args, **kwds))

    #---------------------------------------------------------------------------
    #  Editor interface:
    #---------------------------------------------------------------------------

    def init (self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        factory = self.factory
        adapter = self.adapter = factory.adapter
        self.model = TabularModel(editor=self)

        # Create the control
        control = self.control = self.widget_factory(self)

        # Set up the selection listener
        if factory.multi_select:
            self.sync_value(factory.selected, 'multi_selected', 'both',
                            is_list=True)
            self.sync_value(factory.selected_row, 'multi_selected_rows','both',
                            is_list=True)
        else:
            self.sync_value(factory.selected, 'selected', 'both')
            self.sync_value(factory.selected_row, 'selected_row', 'both')

        # Connect to the mode specific selection handler
        if factory.multi_select:
            slot = self._on_rows_selection
        else:
            slot = self._on_row_selection
        signal = 'selectionChanged(QItemSelection,QItemSelection)'
        QtCore.QObject.connect(self.control.selectionModel(),
                               QtCore.SIGNAL(signal), slot)

        # Synchronize other interesting traits as necessary:
        self.sync_value(factory.update, 'update', 'from')
        self.sync_value(factory.refresh, 'refresh', 'from')
        self.sync_value(factory.activated,     'activated',     'to')
        self.sync_value(factory.activated_row, 'activated_row', 'to')
        self.sync_value(factory.clicked,  'clicked',  'to')
        self.sync_value(factory.dclicked, 'dclicked', 'to')
        self.sync_value(factory.right_clicked,  'right_clicked',  'to')
        self.sync_value(factory.right_dclicked, 'right_dclicked', 'to')
        self.sync_value(factory.column_clicked, 'column_clicked', 'to')
        self.sync_value(factory.column_right_clicked, 'column_right_clicked', 'to')
        self.sync_value(factory.scroll_to_row, 'scroll_to_row', 'from')

        # Connect other signals as necessary
        signal = QtCore.SIGNAL('activated(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_activate)
        signal = QtCore.SIGNAL('clicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_click)
        signal = QtCore.SIGNAL('clicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_right_click)
        signal = QtCore.SIGNAL('doubleClicked(QModelIndex)')
        QtCore.QObject.connect(control, signal, self._on_dclick)
        signal = QtCore.SIGNAL('sectionClicked(int)')
        QtCore.QObject.connect(control.horizontalHeader(), signal, self._on_column_click)

        control.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        signal = QtCore.SIGNAL('customContextMenuRequested(QPoint)')
        QtCore.QObject.connect(control, signal, self._on_context_menu)

        self.header_event_filter = HeaderEventFilter(self)
        control.horizontalHeader().installEventFilter(self.header_event_filter)

        # Make sure we listen for 'items' changes as well as complete list
        # replacements:
        try:
            self.context_object.on_trait_change(
                self.update_editor, self.extended_name+'_items', dispatch='ui')
        except:
            pass

        # If the user has requested automatic update, attempt to set up the
        # appropriate listeners:
        if factory.auto_update:
            self.context_object.on_trait_change(
                self.refresh_editor, self.extended_name + '.-', dispatch='ui')

        # Create the mapping from user supplied images to QImages:
        for image_resource in factory.images:
            self._add_image(image_resource)

        # Refresh the editor whenever the adapter changes:
        self.on_trait_change(self.refresh_editor, 'adapter.+update',
                             dispatch='ui')

        # Rebuild the editor columns and headers whenever the adapter's
        # 'columns' changes:
        self.on_trait_change(self.update_editor, 'adapter.columns',
                             dispatch='ui')

    def dispose (self):
        """ Disposes of the contents of an editor.
        """
        self.context_object.on_trait_change(
            self.update_editor, self.extended_name + '_items', remove=True)

        if self.factory.auto_update:
            self.context_object.on_trait_change(
                self.refresh_editor, self.extended_name + '.-', remove=True)

        self.on_trait_change(self.refresh_editor, 'adapter.+update',
                             remove=True)
        self.on_trait_change(self.update_editor, 'adapter.columns',
                             remove=True)

        self.adapter.cleanup()

        super(TabularEditor, self).dispose()

    def update_editor(self):
        """ Updates the editor when the object trait changes externally to the
            editor.
        """
        if not self._no_update:
            self.model.reset()
            if self.factory.multi_select:
                self._multi_selected_changed(self.multi_selected)
            else :
                self._selected_changed(self.selected)

    #---------------------------------------------------------------------------
    #  TabularEditor interface:
    #---------------------------------------------------------------------------

    def refresh_editor(self):
        """ Requests the table view to redraw itself.
        """
        self.control.viewport().update()

    def callx(self, func, *args, **kw):
        """ Call a function without allowing the editor to update.
        """
        old = self._no_update
        self._no_update = True
        try:
            func(*args, **kw)
        finally:
            self._no_update = old

    def setx(self, **keywords):
        """ Set one or more attributes without allowing the editor to update.
        """
        old = self._no_notify
        self._no_notify = True
        try:
            for name, value in keywords.items():
                setattr(self, name, value)
        finally:
            self._no_notify = old

    #---------------------------------------------------------------------------
    #  UI preference save/restore interface:
    #---------------------------------------------------------------------------

    def restore_prefs(self, prefs):
        """ Restores any saved user preference information associated with the
            editor.
        """
        cws = prefs.get('cached_widths')
        num_columns = len(self.adapter.columns)
        if cws is not None and num_columns == len(cws):
            for column in xrange(num_columns):
                self.control.setColumnWidth(column, cws[column])

    def save_prefs(self):
        """ Returns any user preference information associated with the editor.
        """
        widths = [ self.control.columnWidth(column)
                   for column in xrange(len(self.adapter.columns)) ]
        return { 'cached_widths': widths }

    #---------------------------------------------------------------------------
    #  Private methods:
    #---------------------------------------------------------------------------

    def _add_image(self, image_resource):
        """ Adds a new image to the image map.
        """
        image = image_resource.create_icon()

        self.image_resources[image_resource] = image
        self.images[image_resource.name] = image

        return image

    def _get_image(self, image):
        """ Converts a user specified image to a QIcon.
        """
        if isinstance(image, basestring):
            self.image = image
            image = self.image

        if isinstance(image, ImageResource):
            result = self.image_resources.get(image)
            if result is not None:
                return result
            return self._add_image(image)

        return self.images.get(image)

    def _mouse_click(self, index, trait):
        """ Generate a TabularEditorEvent event for a specified model index and
            editor trait name.
        """
        event = TabularEditorEvent(editor=self, row=index.row(),
                                   column=index.column())
        setattr(self, trait, event)

    #-- Trait Event Handlers ---------------------------------------------------

    def _update_changed(self):
        self.update_editor()

    def _refresh_changed(self):
        self.refresh_editor()

    def _selected_changed(self, new):
        if not self._no_update:
            try:
                selected_row = self.value.index(new)
            except:
                pass
            else:
                self._selected_row_changed(selected_row)

    def _selected_row_changed(self, selected_row):
        if not self._no_update:
            smodel = self.control.selectionModel()
            if selected_row == -1:
                smodel.clearSelection()
            else:
                smodel.select(self.model.index(selected_row, 0),
                              QtGui.QItemSelectionModel.ClearAndSelect |
                              QtGui.QItemSelectionModel.Rows)

    def _multi_selected_changed(self, new):
        if not self._no_update:
            values = self.value
            try:
                rows = [ values.index(i) for i in new]
            except:
                pass
            else:
                self._multi_selected_rows_changed(rows)

    def _multi_selected_items_changed(self, event):
        values = self.value
        try:
            added = [ values.index(item) for item in event.added ]
            removed = [ values.index(item) for item in event.removed ]
        except:
            pass
        else:
            list_event = TraitListEvent(0, added, removed)
            self._multi_selected_rows_items_changed(list_event)

    def _multi_selected_rows_changed(self, selected_rows):
        if not self._no_update:
            smodel = self.control.selectionModel()
            selection = QtGui.QItemSelection()
            for row in selected_rows:
                selection.select(self.model.index(row, 0), self.model.index(row, 0))
            smodel.clearSelection()
            smodel.select(selection,
                QtGui.QItemSelectionModel.Select |
                QtGui.QItemSelectionModel.Rows)

    def _multi_selected_rows_items_changed(self, event):
        smodel = self.control.selectionModel()
        for row in event.removed:
            smodel.select(self.model.index(row, 0),
                          QtGui.QItemSelectionModel.Deselect |
                          QtGui.QItemSelectionModel.Rows)
        for row in event.added:
            smodel.select(self.model.index(row, 0),
                          QtGui.QItemSelectionModel.Select |
                          QtGui.QItemSelectionModel.Rows)

    scroll_to_row_hint_map = {
        'center'  : QtGui.QTableView.PositionAtCenter,
        'top'     : QtGui.QTableView.PositionAtTop,
        'bottom'  : QtGui.QTableView.PositionAtBottom,
        'visible' : QtGui.QTableView.EnsureVisible,
    }

    def _scroll_to_row_changed(self, row):
        """ Scroll to the given row.
        """
        scroll_hint = self.scroll_to_row_hint_map.get(self.factory.scroll_to_row_hint, self.control.PositionAtCenter)
        self.control.scrollTo(self.model.index(row, 0), scroll_hint)

    #-- Table Control Event Handlers -------------------------------------------

    def _on_activate(self, index):
        """ Handle a cell being activated.
        """
        self.activated_row = row = index.row()
        self.activated = self.adapter.get_item(self.object, self.name, row)

    def _on_click(self, index):
        """ Handle a cell being clicked.
        """
        self._mouse_click(index, 'clicked')

    def _on_dclick(self, index):
        """ Handle a cell being double clicked.
        """
        self._mouse_click(index, 'dclicked')

    def _on_column_click(self, column):
        event = TabularEditorEvent(editor=self, row=0, column=column)
        setattr(self, 'column_clicked', event)

    def _on_right_click(self, column):
        event = TabularEditorEvent(editor=self, row=0, column=column)
        setattr(self, 'right_clicked', event)

    def _on_column_right_click(self, column):
        event = TabularEditorEvent(editor=self, row=0, column=column)
        setattr(self, 'column_right_clicked', event)

    def _on_row_selection(self, added, removed):
        """ Handle the row selection being changed.
        """
        self._no_update = True
        try:
            indexes = self.control.selectionModel().selectedRows()
            if len(indexes):
                self.selected_row = indexes[0].row()
                self.selected = self.adapter.get_item(self.object, self.name,
                                                      self.selected_row)
            else:
                self.selected_row = -1
                self.selected = None
        finally:
            self._no_update = False

    def _on_rows_selection(self, added, removed):
        """ Handle the rows selection being changed.
        """
        self._no_update = True
        try:
            indexes = self.control.selectionModel().selectedRows()
            selected_rows = []
            selected = []
            for index in indexes:
                row = index.row()
                selected_rows.append(row)
                selected.append(self.adapter.get_item(self.object, self.name,
                                                      row))
            self.multi_selected_rows = selected_rows
            self.multi_selected = selected
        finally:
            self._no_update = False

    def _on_context_menu(self, pos) :
        column, row = self.control.columnAt(pos.x()), self.control.rowAt(pos.y())
        menu = self.adapter.get_menu(self.object, self.name, row, column)
        if menu :
            self._menu_context = {'selection' : self.object,
                             'object':  self.object,
                             'editor':  self,
                             'column':  column,
                             'row':     row,
                             'item':    self.adapter.get_item(self.object, self.name, row),
                             'info':    self.ui.info,
                             'handler': self.ui.handler }
            qmenu = menu.create_menu( self.control, self )
            qmenu.exec_(self.control.mapToGlobal(pos))
            self._menu_context = None

    def _on_column_context_menu(self, pos) :
        column = self.control.columnAt(pos.x())
        menu = self.adapter.get_column_menu(self.object, self.name, -1, column)
        if menu :
            self._menu_context = {'selection' : self.object, 'object':  self.object,
                             'editor':  self,
                             'column':  column,
                             'info':    self.ui.info,
                             'handler': self.ui.handler }
            qmenu = menu.create_menu( self.control, self )
            qmenu.exec_(self.control.mapToGlobal(pos))
            self._menu_context = None
        else:
            #If no menu is defined on the adapter, just trigger a click event.
            self._on_column_right_click(column)
Esempio n. 5
0
# separate data into training and test data
batch_size = 60000
test_size = int(batch_size * 0.2)

cat_train = cats[:batch_size - test_size]
cat_test = cats[batch_size - test_size:batch_size]
con_train = conts[:batch_size - test_size]
con_test = conts[batch_size - test_size:batch_size]
y_train = y[:batch_size - test_size]
y_test = y[batch_size - test_size:batch_size]

# Neural Networky stuff
selfembeds = nn.ModuleList([nn.Embedding(ni, nf) for ni, nf in emb_szs])

torch.manual_seed(33)
model = TabularModel(emb_szs, conts.shape[1], 1, [200, 100], p=0.4)

criterion = nn.MSELoss()  # we'll convert this to RMSE later
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

start_time = time.time()

epochs = 300
losses = []

for i in range(epochs):
    i += 1
    y_pred = model(cat_train, con_train)
    loss = torch.sqrt(criterion(y_pred, y_train))  # RMSE
    losses.append(loss)