예제 #1
0
class SettingChangedEvent(QEvent):
    """
    A settings has changed.

    This event is sent by Settings instance to itself when a setting for
    a key has changed.

    """
    SettingChanged = QEvent.registerEventType()
    """Setting was changed"""

    SettingAdded = QEvent.registerEventType()
    """A setting was added"""

    SettingRemoved = QEvent.registerEventType()
    """A setting was removed"""
    def __init__(self, etype, key, value=None, oldValue=None):
        """
        Initialize the event instance
        """
        QEvent.__init__(self, etype)
        self.__key = key
        self.__value = value
        self.__oldValue = oldValue

    def key(self):
        return self.__key

    def value(self):
        return self.__value

    def oldValue(self):
        return self.__oldValue
예제 #2
0
파일: outputview.py 프로젝트: wibrt/orange3
class QueuedCallEvent(QEvent):
    QueuedCall = QEvent.registerEventType()

    def __init__(self, function, args, kwargs):
        QEvent.__init__(self, QueuedCallEvent.QueuedCall)
        self.function = function
        self.args = args
        self.kwargs = kwargs
        self._result = None
        self._exc_info = None
        self._state = 0

    def call(self):
        try:
            self._result = self.function(*self.args, **self.kwargs)
            self._state = 1
        except Exception as ex:
            self._exc_info = (type(ex), ex.args, None)
            raise

    def result(self):
        if self._state == 1:
            return self._result
        elif self._exc_info:
            raise self._exc_info[0](self._exc_info[1])
        else:
            # Should this block, add timeout?
            raise RuntimeError("Result not yet ready")

    def isready(self):
        return self._state == 1 or self._exc_info
예제 #3
0
class ThunkEvent( QEvent ):
    """
    A QEvent subclass that holds a callable which can be executed by its listeners.
    Sort of like a "queued connection" signal.
    """
    EventType = QEvent.Type(QEvent.registerEventType())

    def __init__(self, func, *args):
        QEvent.__init__(self, self.EventType)
        if len(args) > 0:
            self.thunk = partial(func, *args)
        else:
            self.thunk = func
    
    def __call__(self):
        self.thunk()

    @classmethod
    def post(cls, handlerObject, func, *args):
        e = ThunkEvent( func, *args )
        QApplication.postEvent(handlerObject, e)

    @classmethod
    def send(cls, handlerObject, func, *args):
        e = ThunkEvent( func, *args )
        QApplication.sendEvent(handlerObject, e)
예제 #4
0
class EventFlusher(QObject):
    SetEvent = QEvent.Type(QEvent.registerEventType())

    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self._state = threading.Event()

    def event(self, e):
        if e.type() == EventFlusher.SetEvent:
            assert threading.current_thread().name == "MainThread"
            self.set()
            return True
        return False

    def eventFilter(self, watched, event):
        return self.event(event)

    def set(self):
        QApplication.sendPostedEvents()
        QApplication.processEvents()
        QApplication.flush()
        assert not self._state.is_set()
        self._state.set()

    def clear(self):
        self._state.clear()

    def wait(self):
        assert threading.current_thread().name != "MainThread"
        self._state.wait()
예제 #5
0
class CallbackEvent(QEvent):

    _evtype = QEvent.Type(QEvent.registerEventType())

    def __init__(self, callback, *args):
        super(CallbackEvent, self).__init__(self._evtype)
        self.callback = callback
        self.args = args
예제 #6
0
    class WidgetInitEvent(QEvent):
        DelayedInit = QEvent.registerEventType()

        def __init__(self, initstate):
            super().__init__(WidgetManager.WidgetInitEvent.DelayedInit)
            self._initstate = initstate

        def initstate(self):
            return self._initstate
예제 #7
0
class ExecuteCallEvent(QEvent):
    """
    Represents an function call from the event loop (used by :class:`Task`
    to schedule the :func:`Task.run` method to be invoked)

    """
    ExecuteCall = QEvent.registerEventType()

    def __init__(self):
        QEvent.__init__(self, ExecuteCallEvent.ExecuteCall)
예제 #8
0
파일: update.py 프로젝트: pksmall/EliteOCR
    class Updater():

        QUIT_EVENT_TYPE = QEvent.Type(QEvent.registerEventType())

        def __init__(self, window):
            pass

        def checkForUpdates(self):
            pass

        def close(self):
            pass
예제 #9
0
class StateChangedEvent(QEvent):
    """
    Represents a change in the internal state of a :class:`Future`.
    """
    StateChanged = QEvent.registerEventType()

    def __init__(self, state):
        QEvent.__init__(self, StateChangedEvent.StateChanged)
        self._state = state

    def state(self):
        """
        Return the new state (Future.Pending, Future.Cancelled, ...).
        """
        return self._state
class QueuedCallEvent(QEvent):
    QueuedCall = QEvent.registerEventType()

    def __init__(self, function, args, kwargs):
        QEvent.__init__(self, QueuedCallEvent.QueuedCall)
        self.function = function
        self.args = args
        self.kwargs = kwargs
        self._result = None
        self._exc_info = None
        self._state = 0

    def call(self):
        try:
            self._result = self.function(*self.args, **self.kwargs)
            self._state = 1
        except Exception, ex:
            self._exc_info = (type(ex), ex.args, None)
            raise
예제 #11
0
파일: update.py 프로젝트: pksmall/EliteOCR
    class Updater():

        QUIT_EVENT_TYPE = QEvent.Type(QEvent.registerEventType())

        # https://github.com/vslavik/winsparkle/wiki/Basic-Setup

        def __init__(self, window):
            try:
                sys.frozen  # don't want to try updating python.exe
                self.updater = ctypes.cdll.WinSparkle
                self.updater.win_sparkle_set_appcast_url(
                    'http://eliteocr.sourceforge.net/appcast.xml'
                )  # py2exe won't let us embed this in resources
                self.updater.win_sparkle_set_automatic_check_for_updates(1)
                self.updater.win_sparkle_set_update_check_interval(47 * 60 *
                                                                   60)

                # set up shutdown callback
                global mainwindow
                mainwindow = window
                self.callback_t = ctypes.CFUNCTYPE(None)  # keep reference
                self.callback_fn = self.callback_t(shutdown_request)
                self.updater.win_sparkle_set_shutdown_request_callback(
                    self.callback_fn)

                self.updater.win_sparkle_init()

            except:
                from traceback import print_exc
                print_exc()
                self.updater = None

        def checkForUpdates(self):
            if self.updater:
                self.updater.win_sparkle_check_update_with_ui()

        def close(self):
            if self.updater:
                self.updater.win_sparkle_cleanup()
            self.updater = None
예제 #12
0
파일: update.py 프로젝트: pksmall/EliteOCR
    class Updater():

        QUIT_EVENT_TYPE = QEvent.Type(QEvent.registerEventType())

        # http://sparkle-project.org/documentation/customization/

        def __init__(self, window):
            try:
                objc.loadBundle(
                    'Sparkle', globals(),
                    join(dirname(sys.executable), os.pardir, 'Frameworks',
                         'Sparkle.framework'))
                self.updater = SUUpdater.sharedUpdater()
            except:
                # can't load framework - not frozen or not included in app bundle?
                self.updater = None

        def checkForUpdates(self):
            if self.updater:
                self.updater.checkForUpdates_(None)

        def close(self):
            self.updater = None
예제 #13
0
class QueuedCallEvent(QEvent):
    QueuedCall = QEvent.registerEventType()

    def __init__(self, function, args, kwargs, semaphore=None):
        QEvent.__init__(self, QueuedCallEvent.QueuedCall)
        self.function = function
        self.args = args
        self.kwargs = kwargs
        self.semaphore = semaphore
        self._result = None
        self._exc_info = None
        self._state = 0

    def call(self):
        try:
            self._result = self.function(*self.args, **self.kwargs)
            self._state = 1
            if self.semaphore is not None:
                self.semaphore.release()
        except BaseException, ex:
            self._exc_info = (type(ex), ex.args, None)
            if self.semaphore is not None:
                self.semaphore.release()
            raise
예제 #14
0
class UpdateNbImageEvent(QEvent):
    def __init__(self, nb):
        QEvent.__init__(self, self.event_type)
        self.nb = nb

    event_type = QEvent.Type(QEvent.registerEventType())
예제 #15
0
class FinishLoadingEvent(QEvent):
    def __init__(self):
        QEvent.__init__(self, self.event_type)

    event_type = QEvent.Type(QEvent.registerEventType())
예제 #16
0
파일: gui.py 프로젝트: junzis/bluesky
    def __init__(self, navdb):
        super(Gui, self).__init__([])
        self.acdata          = ACDataEvent()
        self.navdb           = navdb
        self.radarwidget     = []
        self.command_history = []
        self.cmdargs         = []
        self.history_pos     = 0
        self.command_mem     = ''
        self.command_line    = ''
        self.prev_cmdline    = ''
        self.simevent_target = 0
        self.mousepos        = (0, 0)
        self.prevmousepos    = (0, 0)
        self.panzoomchanged  = False

        # Register our custom pan/zoom event
        for etype in [PanZoomEventType, ACDataEventType, SimInfoEventType,
                      StackTextEventType, ShowDialogEventType,
                      DisplayFlagEventType, RouteDataEventType,
                      DisplayShapeEventType]:
            reg_etype = QEvent.registerEventType(etype)
            if reg_etype != etype:
                print('Warning: Registered event type differs from requested type id (%d != %d)' % (reg_etype, etype))

        self.splash = Splash()
        self.splash.show()

        self.splash.showMessage('Constructing main window')
        self.processEvents()

        # Install error message handler
        handler = QErrorMessage.qtHandler()
        handler.setWindowFlags(Qt.WindowStaysOnTopHint)

        # Check and set OpenGL capabilities
        if not QGLFormat.hasOpenGL():
            raise RuntimeError('No OpenGL support detected for this system!')
        else:
            f = QGLFormat()
            f.setVersion(3, 3)
            f.setProfile(QGLFormat.CoreProfile)
            f.setDoubleBuffer(True)
            QGLFormat.setDefaultFormat(f)
            print('QGLWidget initialized for OpenGL version %d.%d' % (f.majorVersion(), f.minorVersion()))

        # Create the main window and related widgets
        self.radarwidget = RadarWidget(navdb)
        self.win = MainWindow(self, self.radarwidget)
        self.nd  = ND(shareWidget=self.radarwidget)

        # Enable HiDPI support (Qt5 only)
        if QT_VERSION == 5:
            self.setAttribute(Qt.AA_UseHighDpiPixmaps)

        timer = QTimer(self)
        timer.timeout.connect(self.radarwidget.updateGL)
        timer.timeout.connect(self.nd.updateGL)
        timer.start(50)

        # Load geo data
        if False:
            pb = QProgressDialog('Binary buffer file not found, or file out of date: Constructing vertex buffers from geo data.', 'Cancel', 0, 100)
            pb.setWindowFlags(Qt.WindowStaysOnTopHint)
            pb.show()
            for i in range(101):
                pb.setValue(i)
                self.processEvents()
                QThread.msleep(100)
            pb.close()
예제 #17
0
class OWCorrespondenceAnalysis(widget.OWWidget):
    name = "Correspondence Analysis"
    description = "Correspondence analysis"
    icon = "icons/CorrespondenceAnalysis.svg"

    inputs = [("Data", Orange.data.Table, "set_data")]

    Invalidate = QEvent.registerEventType()

    settingsHandler = settings.DomainContextHandler()

    selected_var_indices = settings.ContextSetting([])

    def __init__(self, parent=None):
        super().__init__(parent)

        self.data = None
        self.component_x = 0
        self.component_y = 1

        box = gui.widgetBox(self.controlArea, "Variables")
        self.varlist = itemmodels.VariableListModel()
        self.varview = view = QListView(
            selectionMode=QListView.MultiSelection
        )
        view.setModel(self.varlist)
        view.selectionModel().selectionChanged.connect(self._var_changed)

        box.layout().addWidget(view)

        axes_box = gui.widgetBox(self.controlArea, "Axes")
        box = gui.widgetBox(axes_box, "Axis X", margin=0)
        box.setFlat(True)
        self.axis_x_cb = gui.comboBox(
            box, self, "component_x", callback=self._component_changed)

        box = gui.widgetBox(axes_box, "Axis Y", margin=0)
        box.setFlat(True)
        self.axis_y_cb = gui.comboBox(
            box, self, "component_y", callback=self._component_changed)

        self.infotext = gui.widgetLabel(
            gui.widgetBox(self.controlArea, "Contribution to Inertia"), "\n"
        )

        gui.rubber(self.controlArea)

        self.plot = pg.PlotWidget(background="w")
        self.mainArea.layout().addWidget(self.plot)

    def set_data(self, data):
        self.closeContext()
        self.clear()
        self.warning(0)
        self.data = data

        if data is not None:
            self.varlist[:] = data.domain.variables
            self.selected_var_indices = [0, 1][:len(self.varlist)]
            self.component_x, self.component_y = 0, 1
            self.openContext(data)
            self._restore_selection()
#             self._invalidate()
        self._update_CA()

    def clear(self):
        self.data = None
        self.ca = None
        self.plot.clear()
        self.varlist[:] = []

    def selected_vars(self):
        rows = sorted(
            ind.row() for ind in self.varview.selectionModel().selectedRows())
        return [self.varlist[i] for i in rows]

    def _restore_selection(self):
        def restore(view, indices):
            with itemmodels.signal_blocking(view.selectionModel()):
                select_rows(view, indices)
        restore(self.varview, self.selected_var_indices)

    def _p_axes(self):
#         return (0, 1)
        return (self.component_x, self.component_y)

    def _var_changed(self):
        self.selected_var_indices = sorted(
            ind.row() for ind in self.varview.selectionModel().selectedRows())
        self._invalidate()

    def _component_changed(self):
        if self.ca is not None:
            self._setup_plot()
            self._update_info()

    def _invalidate(self):
        self.__invalidated = True
        QApplication.postEvent(self, QEvent(self.Invalidate))

    def customEvent(self, event):
        if event.type() == self.Invalidate:
            self.ca = None
            self.plot.clear()
            self._update_CA()
            return
        return super().customEvent(event)

    def _update_CA(self):
        ca_vars = self.selected_vars()
        if len(ca_vars) == 0:
            return

        multi = len(ca_vars) != 2
        if multi:
            _, ctable = burt_table(self.data, ca_vars)
        else:
            ctable = contingency.get_contingency(self.data, *ca_vars[::-1])

        self.ca = correspondence(ctable, )
        axes = ["{}".format(i + 1)
                for i in range(self.ca.row_factors.shape[1])]
        self.axis_x_cb.clear()
        self.axis_x_cb.addItems(axes)
        self.axis_y_cb.clear()
        self.axis_y_cb.addItems(axes)
        self.component_x, self.component_y = self.component_x, self.component_y

        self._setup_plot()
        self._update_info()

    def _setup_plot(self):
        self.plot.clear()

        points = self.ca
        variables = self.selected_vars()
        colors = colorpalette.ColorPaletteGenerator(len(variables))

        p_axes = self._p_axes()

        if len(variables) == 2:
            row_points = self.ca.row_factors[:, p_axes]
            col_points = self.ca.col_factors[:, p_axes]
            points = [row_points, col_points]
        else:
            points = self.ca.row_factors[:, p_axes]
            counts = [len(var.values) for var in variables]
            range_indices = numpy.cumsum([0] + counts)
            ranges = zip(range_indices, range_indices[1:])
            points = [points[s:e] for s, e in ranges]

        for i, (v, points) in enumerate(zip(variables, points)):
            color_outline = colors[i]
            color_outline.setAlpha(200)
            color = QColor(color_outline)
            color.setAlpha(120)
            item = ScatterPlotItem(
                x=points[:, 0], y=points[:, 1], brush=QBrush(color),
                pen=pg.mkPen(color_outline.darker(120), width=1.5),
                size=numpy.full((points.shape[0],), 10.1),
            )
            self.plot.addItem(item)

            for name, point in zip(v.values, points):
                item = pg.TextItem(name, anchor=(0.5, 0))
                self.plot.addItem(item)
                item.setPos(point[0], point[1])

        inertia = self.ca.inertia_of_axis()
        inertia = 100 * inertia / numpy.sum(inertia)

        ax = self.plot.getAxis("bottom")
        ax.setLabel("Component {} ({:.1f}%)"
                    .format(p_axes[0] + 1, inertia[p_axes[0]]))
        ax = self.plot.getAxis("left")
        ax.setLabel("Component {} ({:.1f}%)"
                    .format(p_axes[1] + 1, inertia[p_axes[1]]))

    def _update_info(self):
        if self.ca is None:
            self.infotext.setText("\n\n")
        else:
            fmt = ("Axis 1: {:.2f}\n"
                   "Axis 2: {:.2f}")
            inertia = self.ca.inertia_of_axis()
            inertia = 100 * inertia / numpy.sum(inertia)

            ax1, ax2 = self._p_axes()
            self.infotext.setText(fmt.format(inertia[ax1], inertia[ax2]))
예제 #18
0
class AbortPlottingEvent(QEvent):
    def __init__(self, reason):
        QEvent.__init__(self, self.event_type)
        self.reason = reason

    event_type = QEvent.Type(QEvent.registerEventType())
예제 #19
0
 def __init__(cls, name, bases, dct):
     super(EventMeta, cls).__init__(name, bases, dct)
     cls.id = QEvent.registerEventType() if name != 'EventBase' else None
예제 #20
0
class ActivateParentEvent(QEvent):
    ActivateParent = QEvent.registerEventType()

    def __init__(self):
        QEvent.__init__(self, self.ActivateParent)
class RuntimeEvent(QEvent):
    Init = QEvent.registerEventType()
예제 #22
0
class ImageReadyPlottingEvent(QEvent):
    def __init__(self):
        QEvent.__init__(self, self.event_type)

    event_type = QEvent.Type(QEvent.registerEventType())
예제 #23
0
파일: thumbnail.py 프로젝트: kzwkt/dff
class ScaleEvent(QEvent):
    Type = QEvent.Type(QEvent.registerEventType())

    def __init__(self, config):
        QEvent.__init__(self, 11003)
        self.config = config
예제 #24
0
class NextImageEvent(QEvent):
    def __init__(self):
        QEvent.__init__(self, self.event_type)

    event_type = QEvent.Type(QEvent.registerEventType())
예제 #25
0
파일: event.py 프로젝트: scudella/blink-qt
 def __init__(cls, name, bases, dct):
     super(EventMeta, cls).__init__(name, bases, dct)
     cls.id = QEvent.registerEventType() if name != 'EventBase' else None
예제 #26
0
class FinishImageGrowthEvent(QEvent):
    def __init__(self):
        QEvent.__init__(self, FinishImageGrowthEvent.event_type)
    event_type = QEvent.Type(QEvent.registerEventType())
예제 #27
0
class AsyncUpdateLoop(QObject):
    """
    Run/drive an coroutine from the event loop.

    This is a utility class which can be used for implementing
    asynchronous update loops. I.e. coroutines which periodically yield
    control back to the Qt event loop.

    """
    Next = QEvent.registerEventType()

    #: State flags
    Idle, Running, Cancelled, Finished = 0, 1, 2, 3
    #: The coroutine has yielded control to the caller (with `object`)
    yielded = Signal(object)
    #: The coroutine has finished/exited (either with an exception
    #: or with a return statement)
    finished = Signal()

    #: The coroutine has returned (normal return statement / StopIteration)
    returned = Signal(object)
    #: The coroutine has exited with with an exception.
    raised = Signal(object)
    #: The coroutine was cancelled/closed.
    cancelled = Signal()

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.__coroutine = None
        self.__next_pending = False  # Flag for compressing scheduled events
        self.__in_next = False
        self.__state = AsyncUpdateLoop.Idle

    @Slot(object)
    def setCoroutine(self, loop):
        """
        Set the coroutine.

        The coroutine will be resumed (repeatedly) from the event queue.
        If there is an existing coroutine set it is first closed/cancelled.

        Raises an RuntimeError if the current coroutine is running.
        """
        if self.__coroutine is not None:
            self.__coroutine.close()
            self.__coroutine = None
            self.__state = AsyncUpdateLoop.Cancelled

            self.cancelled.emit()
            self.finished.emit()

        if loop is not None:
            self.__coroutine = loop
            self.__state = AsyncUpdateLoop.Running
            self.__schedule_next()

    @Slot()
    def cancel(self):
        """
        Cancel/close the current coroutine.

        Raises an RuntimeError if the current coroutine is running.
        """
        self.setCoroutine(None)

    def state(self):
        """
        Return the current state.
        """
        return self.__state

    def isRunning(self):
        return self.__state == AsyncUpdateLoop.Running

    def __schedule_next(self):
        if not self.__next_pending:
            self.__next_pending = True
            QtCore.QTimer.singleShot(10, self.__on_timeout)

    def __next(self):
        if self.__coroutine is not None:
            try:
                rval = next(self.__coroutine)
            except StopIteration as stop:
                self.__state = AsyncUpdateLoop.Finished
                self.returned.emit(stop.value)
                self.finished.emit()
                self.__coroutine = None
            except BaseException as er:
                self.__state = AsyncUpdateLoop.Finished
                self.raised.emit(er)
                self.finished.emit()
                self.__coroutine = None
            else:
                self.yielded.emit(rval)
                self.__schedule_next()

    @Slot()
    def __on_timeout(self):
        assert self.__next_pending
        self.__next_pending = False
        if not self.__in_next:
            self.__in_next = True
            try:
                self.__next()
            finally:
                self.__in_next = False
        else:
            # warn
            self.__schedule_next()

    def customEvent(self, event):
        if event.type() == AsyncUpdateLoop.Next:
            self.__on_timeout()
        else:
            super().customEvent(event)
예제 #28
0
class OWLinearProjection(widget.OWWidget):
    name = "Linear Projection"
    description = "A multi-axes projection of data to a two-dimension plane."
    icon = "icons/LinearProjection.svg"
    priority = 2000

    inputs = [("Data", Orange.data.Table, "set_data", widget.Default),
              ("Data Subset", Orange.data.Table, "set_subset_data")]
#              #TODO: Allow for axes to be supplied from an external source.
#               ("Projection", numpy.ndarray, "set_axes"),]
    outputs = [("Selected Data", Orange.data.Table)]

    settingsHandler = settings.DomainContextHandler()

    selected_variables = settings.ContextSetting(
        [], required=settings.ContextSetting.REQUIRED
    )
    variable_state = settings.ContextSetting({})

    color_index = settings.ContextSetting(0)
    shape_index = settings.ContextSetting(0)
    size_index = settings.ContextSetting(0)
    label_index = settings.ContextSetting(0)

    point_size = settings.Setting(10)
    alpha_value = settings.Setting(255)
    jitter_value = settings.Setting(0)

    auto_commit = settings.Setting(True)

    MinPointSize = 6

    ReplotRequest = QEvent.registerEventType()

    def __init__(self, parent=None):
        super().__init__(parent)

        self.data = None
        self.subset_data = None
        self._subset_mask = None
        self._selection_mask = None
        self._item = None
        self.__selection_item = None
        self.__replot_requested = False

        box = gui.widgetBox(self.controlArea, "Axes")

        box1 = gui.widgetBox(box, "Displayed", margin=0)
        box1.setFlat(True)
        self.active_view = view = QListView(
            sizePolicy=QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Ignored),
            selectionMode=QListView.ExtendedSelection,
            dragEnabled=True,
            defaultDropAction=Qt.MoveAction,
            dragDropOverwriteMode=False,
            dragDropMode=QListView.DragDrop,
            showDropIndicator=True,
            minimumHeight=50,
        )

        view.viewport().setAcceptDrops(True)
        movedown = QAction(
            "Move down", view,
            shortcut=QKeySequence(Qt.AltModifier | Qt.Key_Down),
            triggered=self.__deactivate_selection
        )
        view.addAction(movedown)

        self.varmodel_selected = model = DnDVariableListModel(
            parent=self)

        model.rowsInserted.connect(self._invalidate_plot)
        model.rowsRemoved.connect(self._invalidate_plot)
        model.rowsMoved.connect(self._invalidate_plot)

        view.setModel(model)

        box1.layout().addWidget(view)

        box1 = gui.widgetBox(box, "Other", margin=0)
        box1.setFlat(True)
        self.other_view = view = QListView(
            sizePolicy=QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Ignored),
            selectionMode=QListView.ExtendedSelection,
            dragEnabled=True,
            defaultDropAction=Qt.MoveAction,
            dragDropOverwriteMode=False,
            dragDropMode=QListView.DragDrop,
            showDropIndicator=True,
            minimumHeight=50
        )
        view.viewport().setAcceptDrops(True)
        moveup = QtGui.QAction(
            "Move up", view,
            shortcut=QKeySequence(Qt.AltModifier | Qt.Key_Up),
            triggered=self.__activate_selection
        )
        view.addAction(moveup)

        self.varmodel_other = model = DnDVariableListModel(parent=self)
        view.setModel(model)

        box1.layout().addWidget(view)

        box = gui.widgetBox(self.controlArea, "Jittering")
        gui.comboBox(box, self, "jitter_value",
                     items=["None", "0.01%", "0.1%", "0.5%", "1%", "2%"],
                     callback=self._invalidate_plot)
        box.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        box = gui.widgetBox(self.controlArea, "Points")
        box.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)

        self.colorvar_model = itemmodels.VariableListModel(parent=self)
        self.shapevar_model = itemmodels.VariableListModel(parent=self)
        self.sizevar_model = itemmodels.VariableListModel(parent=self)
        self.labelvar_model = itemmodels.VariableListModel(parent=self)

        form = QtGui.QFormLayout(
            formAlignment=Qt.AlignLeft,
            labelAlignment=Qt.AlignLeft,
            fieldGrowthPolicy=QtGui.QFormLayout.AllNonFixedFieldsGrow,
            spacing=8
        )
        box.layout().addLayout(form)

        cb = gui.comboBox(box, self, "color_index",
                          callback=self._on_color_change)
        cb.setModel(self.colorvar_model)

        form.addRow("Colors", cb)
        alpha_slider = QSlider(
            Qt.Horizontal, minimum=10, maximum=255, pageStep=25,
            tickPosition=QSlider.TicksBelow, value=self.alpha_value)
        alpha_slider.valueChanged.connect(self._set_alpha)

        form.addRow("Opacity", alpha_slider)

        cb = gui.comboBox(box, self, "shape_index",
                          callback=self._on_shape_change)
        cb.setModel(self.shapevar_model)

        form.addRow("Shape", cb)

        cb = gui.comboBox(box, self, "size_index",
                          callback=self._on_size_change)
        cb.setModel(self.sizevar_model)

        form.addRow("Size", cb)
        size_slider = QSlider(
            Qt.Horizontal,  minimum=3, maximum=30, value=self.point_size,
            pageStep=3,
            tickPosition=QSlider.TicksBelow)

        size_slider.valueChanged.connect(self._set_size)
        form.addRow("", size_slider)

        toolbox = gui.widgetBox(self.controlArea, "Zoom/Select")
        toollayout = QtGui.QHBoxLayout()
        toolbox.layout().addLayout(toollayout)

        gui.auto_commit(self.controlArea, self, "auto_commit", "Commit")

        # Main area plot
        self.view = pg.GraphicsView(background="w")
        self.view.setRenderHint(QtGui.QPainter.Antialiasing, True)
        self.view.setFrameStyle(QtGui.QFrame.StyledPanel)
        self.viewbox = pg.ViewBox(enableMouse=True, enableMenu=False)
        self.viewbox.grabGesture(Qt.PinchGesture)
        self.view.setCentralItem(self.viewbox)

        self.mainArea.layout().addWidget(self.view)

        self.selection = PlotSelectionTool(
            self, selectionMode=PlotSelectionTool.Lasso)
        self.selection.setViewBox(self.viewbox)
        self.selection.selectionFinished.connect(self._selection_finish)

        self.zoomtool = PlotZoomTool(self)
        self.pantool = PlotPanTool(self)
        self.pinchtool = PlotPinchZoomTool(self)
        self.pinchtool.setViewBox(self.viewbox)

        self.continuous_palette = colorpalette.ContinuousPaletteGenerator(
            QtGui.QColor(220, 220, 220),
            QtGui.QColor(0, 0, 0),
            False
        )
        self.discrete_palette = colorpalette.ColorPaletteGenerator(13)

        def icon(name):
            path = "icons/Dlg_{}.png".format(name)
            path = pkg_resources.resource_filename(widget.__name__, path)
            return QtGui.QIcon(path)

        actions = namespace(
            zoomtofit=QAction(
                "Zoom to fit", self, icon=icon("zoom_reset"),
                shortcut=QKeySequence(Qt.ControlModifier | Qt.Key_0),
                triggered=lambda:
                    self.viewbox.setRange(QRectF(-1.05, -1.05, 2.1, 2.1))),
            zoomin=QAction(
                "Zoom in", self,
                shortcut=QKeySequence(QKeySequence.ZoomIn),
                triggered=lambda: self.viewbox.scaleBy((1 / 1.25, 1 / 1.25))),
            zoomout=QAction(
                "Zoom out", self,
                shortcut=QKeySequence(QKeySequence.ZoomOut),
                triggered=lambda: self.viewbox.scaleBy((1.25, 1.25))),
            select=QAction(
                "Select", self, checkable=True, icon=icon("arrow"),
                shortcut=QKeySequence(Qt.ControlModifier + Qt.Key_1)),
            zoom=QAction(
                "Zoom", self, checkable=True, icon=icon("zoom"),
                shortcut=QKeySequence(Qt.ControlModifier + Qt.Key_2)),
            pan=QAction(
                "Pan", self, checkable=True, icon=icon("pan_hand"),
                shortcut=QKeySequence(Qt.ControlModifier + Qt.Key_3)),
        )
        self.addActions([actions.zoomtofit, actions.zoomin, actions.zoomout])

        group = QtGui.QActionGroup(self, exclusive=True)
        group.addAction(actions.select)
        group.addAction(actions.zoom)
        group.addAction(actions.pan)

        actions.select.setChecked(True)

        currenttool = self.selection

        def activated(action):
            nonlocal currenttool
            if action is actions.select:
                tool, cursor = self.selection, Qt.ArrowCursor
            elif action is actions.zoom:
                tool, cursor = self.zoomtool, Qt.ArrowCursor
            elif action is actions.pan:
                tool, cursor = self.pantool, Qt.OpenHandCursor
            else:
                assert False
            currenttool.setViewBox(None)
            tool.setViewBox(self.viewbox)
            self.viewbox.setCursor(QtGui.QCursor(cursor))
            currenttool = tool

        group.triggered[QAction].connect(activated)

        def button(action):
            b = QtGui.QToolButton()
            b.setDefaultAction(action)
            return b

        toollayout.addWidget(button(actions.select))
        toollayout.addWidget(button(actions.zoom))
        toollayout.addWidget(button(actions.pan))

        toollayout.addSpacing(4)
        toollayout.addWidget(button(actions.zoomtofit))
        toollayout.addStretch()
        toolbox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)

    def sizeHint(self):
        return QSize(800, 500)

    def clear(self):
        self.data = None
        self._subset_mask = None
        self._selection_mask = None

        self.varmodel_selected[:] = []
        self.varmodel_other[:] = []

        self.colorvar_model[:] = []
        self.sizevar_model[:] = []
        self.shapevar_model[:] = []
        self.labelvar_model[:] = []

        self.clear_plot()

    def clear_plot(self):
        if self._item is not None:
            self._item.setParentItem(None)
            self.viewbox.removeItem(self._item)
            self._item = None

        self.viewbox.clear()

    def _invalidate_plot(self):
        """
        Schedule a delayed replot.
        """
        if not self.__replot_requested:
            self.__replot_requested = True
            QApplication.postEvent(self, QEvent(self.ReplotRequest),
                                   Qt.LowEventPriority - 10)

    def set_data(self, data):
        """
        Set the input dataset.
        """
        self.closeContext()
        self.clear()
        self.data = data
        if data is not None:
            self._initialize(data)
            # get the default encoded state, replacing the position with Inf
            state = self._encode_var_state(
                [list(self.varmodel_selected), list(self.varmodel_other)]
            )
            state = {key: (source_ind, numpy.inf) for key, (source_ind, _)
                     in state.items()}

            self.openContext(data.domain)
            selected_keys = [key for key, (sind, _) in self.variable_state.items()
                             if sind == 0]

            if set(selected_keys).issubset(set(state.keys())):
                pass

            # update the defaults state (the encoded state must contain
            # all variables in the input domain)
            state.update(self.variable_state)
            # ... and restore it with saved positions taking precedence over
            # the defaults
            selected, other = self._decode_var_state(
                state, [list(self.varmodel_selected),
                        list(self.varmodel_other)]
            )
            self.varmodel_selected[:] = selected
            self.varmodel_other[:] = other

            self._invalidate_plot()

    def set_subset_data(self, subset):
        """
        Set the supplementary input subset dataset.
        """
        self.subset_data = subset
        self._subset_mask = None

    def handleNewSignals(self):
        if self.subset_data is not None and self._subset_mask is None:
            # Update the plot's highlight items
            if self.data is not None:
                dataids = self.data.ids.ravel()
                subsetids = numpy.unique(self.subset_data.ids)
                self._subset_mask = numpy.in1d(
                    dataids, subsetids, assume_unique=True)
                self._invalidate_plot()

        self.commit()

    def customEvent(self, event):
        if event.type() == OWLinearProjection.ReplotRequest:
            self.__replot_requested = False
            self._setup_plot()
        else:
            super().customEvent(event)

    def closeContext(self):
        self.variable_state = self._encode_var_state(
            [list(self.varmodel_selected), list(self.varmodel_other)]
        )
        super().closeContext()

    def _encode_var_state(self, lists):
        return {(type(var), var.name): (source_ind, pos)
                for source_ind, var_list in enumerate(lists)
                for pos, var in enumerate(var_list)
                if isinstance(var, Orange.data.Variable)}

    def _decode_var_state(self, state, lists):
        all_vars = reduce(list.__iadd__, lists, [])

        newlists = [[] for _ in lists]
        for var in all_vars:
            source, pos = state[(type(var), var.name)]
            newlists[source].append((pos, var))
        return [[var for _, var in sorted(newlist, key=itemgetter(0))]
                for newlist in newlists]

    def color_var(self):
        """
        Current selected color variable or None (if not selected).
        """
        if 1 <= self.color_index < len(self.colorvar_model):
            return self.colorvar_model[self.color_index]
        else:
            return None

    def size_var(self):
        """
        Current selected size variable or None (if not selected).
        """
        if 1 <= self.size_index < len(self.sizevar_model):
            return self.sizevar_model[self.size_index]
        else:
            return None

    def shape_var(self):
        """
        Current selected shape variable or None (if not selected).
        """
        if 1 <= self.shape_index < len(self.shapevar_model):
            return self.shapevar_model[self.shape_index]
        else:
            return None

    def _initialize(self, data):
        # Initialize the GUI controls from data's domain.
        all_vars = list(data.domain.variables)
        cont_vars = [var for var in data.domain.variables
                     if var.is_continuous]
        disc_vars = [var for var in data.domain.variables
                     if var.is_discrete]
        string_vars = [var for var in data.domain.variables
                       if var.is_string]

        self.all_vars = data.domain.variables
        self.varmodel_selected[:] = cont_vars[:3]
        self.varmodel_other[:] = cont_vars[3:]

        self.colorvar_model[:] = ["Same color"] + all_vars
        self.sizevar_model[:] = ["Same size"] + cont_vars
        self.shapevar_model[:] = ["Same shape"] + disc_vars
        self.labelvar_model[:] = ["No label"] + string_vars

        if data.domain.has_discrete_class:
            self.color_index = all_vars.index(data.domain.class_var) + 1

    def __activate_selection(self):
        view = self.other_view
        model = self.varmodel_other
        indices = view.selectionModel().selectedRows()

        variables = [model.data(ind, Qt.EditRole) for ind in indices]

        for i in sorted((ind.row() for ind in indices), reverse=True):
            del model[i]

        self.varmodel_selected.extend(variables)

    def __deactivate_selection(self):
        view = self.active_view
        model = self.varmodel_selected
        indices = view.selectionModel().selectedRows()

        variables = [model.data(ind, Qt.EditRole) for ind in indices]

        for i in sorted((ind.row() for ind in indices), reverse=True):
            del model[i]

        self.varmodel_other.extend(variables)

    def _get_data(self, var):
        """Return the column data for variable `var`."""
        X, _ = self.data.get_column_view(var)
        return X.ravel()

    def _setup_plot(self):
        self.__replot_requested = False
        self.clear_plot()

        variables = list(self.varmodel_selected)
        if not variables:
            return

        coords = [self._get_data(var) for var in variables]
        coords = numpy.vstack(coords)
        p, N = coords.shape
        assert N == len(self.data), p == len(variables)

        axes = linproj.defaultaxes(len(variables))

        assert axes.shape == (2, p)

        mask = ~numpy.logical_or.reduce(numpy.isnan(coords), axis=0)
        coords = coords[:, mask]

        X, Y = numpy.dot(axes, coords)
        X = plotutils.normalized(X)
        Y = plotutils.normalized(Y)

        pen_data, brush_data = self._color_data(mask)
        size_data = self._size_data(mask)
        shape_data = self._shape_data(mask)

        if self.jitter_value > 0:
            value = [0, 0.01, 0.1, 0.5, 1, 2][self.jitter_value]

            rstate = numpy.random.RandomState(0)
            jitter_x = (rstate.random_sample(X.shape) * 2 - 1) * value / 100
            rstate = numpy.random.RandomState(1)
            jitter_y = (rstate.random_sample(Y.shape) * 2 - 1) * value / 100
            X += jitter_x
            Y += jitter_y

        self._item = ScatterPlotItem(
            X, Y,
            pen=pen_data,
            brush=brush_data,
            size=size_data,
            shape=shape_data,
            antialias=True,
            data=numpy.arange(len(self.data))[mask]
        )
        self._item._mask = mask

        self.viewbox.addItem(self._item)

        for i, axis in enumerate(axes.T):
            axis_item = AxisItem(line=QLineF(0, 0, axis[0], axis[1]),
                                 label=variables[i].name)
            self.viewbox.addItem(axis_item)

        self.viewbox.setRange(QtCore.QRectF(-1.05, -1.05, 2.1, 2.1))

    def _color_data(self, mask=None):
        color_var = self.color_var()
        if color_var is not None:
            color_data = self._get_data(color_var)
            if color_var.is_continuous:
                color_data = plotutils.continuous_colors(color_data)
            else:
                color_data = plotutils.discrete_colors(
                    color_data, len(color_var.values)
                )
            if mask is not None:
                color_data = color_data[mask]

            pen_data = numpy.array(
                [pg.mkPen((r, g, b, self.alpha_value / 2))
                 for r, g, b in color_data],
                dtype=object)

            brush_data = numpy.array(
                [pg.mkBrush((r, g, b, self.alpha_value))
                 for r, g, b in color_data],
                dtype=object)
        else:
            color = QtGui.QColor(Qt.lightGray)
            color.setAlpha(self.alpha_value)
            pen_data = QtGui.QPen(color)
            pen_data.setCosmetic(True)
            color = QtGui.QColor(Qt.darkGray)
            color.setAlpha(self.alpha_value)
            brush_data = QtGui.QBrush(color)

        if self._subset_mask is not None:
            assert self._subset_mask.shape == (len(self.data),)
            if mask is not None:
                subset_mask = self._subset_mask[mask]
            else:
                subset_mask = self._subset_mask

            if isinstance(brush_data, QtGui.QBrush):
                brush_data = numpy.array([brush_data] * subset_mask.size,
                                         dtype=object)

            brush_data[~subset_mask] = QtGui.QBrush(Qt.NoBrush)

        if self._selection_mask is not None:
            assert self._selection_mask.shape == (len(self.data),)
            if mask is not None:
                selection_mask = self._selection_mask[mask]
            else:
                selection_mask = self._selection_mask

            if isinstance(pen_data, QtGui.QPen):
                pen_data = numpy.array([pen_data] * selection_mask.size,
                                       dtype=object)

            pen_data[selection_mask] = pg.mkPen((200, 200, 0, 150), width=4)
        return pen_data, brush_data

    def _on_color_change(self):
        if self.data is None or self._item is None:
            return

        pen, brush = self._color_data()

        if isinstance(pen, QtGui.QPen):
            # Reset the brush for all points
            self._item.data["pen"] = None
            self._item.setPen(pen)
        else:
            self._item.setPen(pen[self._item._mask])

        if isinstance(brush, QtGui.QBrush):
            # Reset the brush for all points
            self._item.data["brush"] = None
            self._item.setBrush(brush)
        else:
            self._item.setBrush(brush[self._item._mask])

    def _shape_data(self, mask):
        shape_var = self.shape_var()
        if shape_var is None:
            shape_data = numpy.array(["o"] * len(self.data))
        else:
            assert shape_var.is_discrete
            max_symbol = len(ScatterPlotItem.Symbols) - 1
            shape = self._get_data(shape_var)
            shape_mask = numpy.isnan(shape)
            shape = shape % (max_symbol - 1)
            shape[shape_mask] = max_symbol

            symbols = numpy.array(list(ScatterPlotItem.Symbols))
            shape_data = symbols[numpy.asarray(shape, dtype=int)]
        if mask is None:
            return shape_data
        else:
            return shape_data[mask]

    def _on_shape_change(self):
        if self.data is None:
            return

        self.set_shape(self._shape_data(mask=None))

    def _size_data(self, mask=None):
        size_var = self.size_var()
        if size_var is None:
            size_data = numpy.full((len(self.data),), self.point_size)
        else:
            size_data = plotutils.normalized(self._get_data(size_var))
            size_data -= numpy.nanmin(size_data)
            size_mask = numpy.isnan(size_data)
            size_data = \
                size_data * self.point_size + OWLinearProjection.MinPointSize
            size_data[size_mask] = OWLinearProjection.MinPointSize - 2
        if mask is None:
            return size_data
        else:
            return size_data[mask]

    def _on_size_change(self):
        if self.data is None:
            return
        self.set_size(self._size_data(mask=None))

    def set_shape(self, shape):
        """
        Set (update) the current point shape map.
        """
        if self._item is not None:
            self._item.setSymbol(shape[self._item._mask])

    def set_size(self, size):
        """
        Set (update) the current point size.
        """
        if self._item is not None:
            self._item.setSize(size[self._item._mask])

    def _set_alpha(self, value):
        self.alpha_value = value
        self._on_color_change()

    def _set_size(self, value):
        self.point_size = value
        self._on_size_change()

    def _selection_finish(self, path):
        self.select(path)

    def select(self, selectionshape):
        item = self._item
        if item is None:
            return

        indices = [spot.data()
                   for spot in item.points()
                   if selectionshape.contains(spot.pos())]

        if QApplication.keyboardModifiers() & Qt.ControlModifier:
            self.select_indices(indices)
        else:
            self._selection_mask = None
            self.select_indices(indices)

    def select_indices(self, indices):
        if self.data is None:
            return

        if self._selection_mask is None:
            self._selection_mask = numpy.zeros(len(self.data), dtype=bool)

        self._selection_mask[indices] = True

        self._on_color_change()
        self.commit()

    def commit(self):
        subset = None
        if self.data is not None and self._selection_mask is not None:
            indices = numpy.flatnonzero(self._selection_mask)
            if len(indices) > 0:
                subset = self.data[indices]

        self.send("Selected Data", subset)