def __init__(self, session, parent=None): super(SpecvizViewer, self).__init__(session, parent=parent) # We set up the specviz viewer and controller as done for the standalone # specviz application self.viewer = Viewer() self.controller = Controller(self.viewer) self.setCentralWidget(self.viewer) # We now set up the options widget. This controls for example which # attribute should be used to indicate the filenames of the spectra. self._options_widget = OptionsWidget(data_viewer=self) # The layer widget is used to select which data or subset to show. # We don't use the default layer list, because in this case we want to # make sure that only one dataset or subset can be selected at any one # time. self._layer_widget = LayerWidget() # We keep a cache of the specviz data objects that correspond to a given # filename - although this could take up a lot of memory if there are # many spectra, so maybe this isn't needed self._specviz_data_cache = {} # Make sure we update the viewer if either the selected layer or the # column specifying the filename is changed. self._layer_widget.ui.combo_active_layer.currentIndexChanged.connect(nonpartial(self._update_options)) self._layer_widget.ui.combo_active_layer.currentIndexChanged.connect(nonpartial(self._refresh_specviz_data)) self._options_widget.ui.combo_file_attribute.currentIndexChanged.connect(nonpartial(self._refresh_specviz_data))
def __init__(self, layer, axes, viewer_state): # Keep a reference to the layer (data or subset) and axes self.layer = layer self.axes = axes self.viewer_state = viewer_state # Watch for changes in the viewer state which would require the # layers to be redrawn # TODO: don't connect to ALL signals here self.viewer_state.connect_all(nonpartial(self.update)) # Set up a state object for the layer artists self.layer_state = ScatterLayerState() self.layer_state.layer = layer self.layer_state.connect_all(self.update) # TODO: need to connect to visual properties of layer in one go add_callback(self.layer.style, 'color', nonpartial(self.update)) add_callback(self.layer.style, 'markersize', nonpartial(self.update)) # Set up the layer style editor self.style_editor = ScatterLayerStyleEditor(self.layer_state) # Set up an initially empty artist self.artist1 = self.axes.plot([], [], 'o')[0] self.artist2 = self.axes.plot([], [], '-')[0] self.zorder = 0 self.update()
def __init__(self, parent=None): super(GlueLogger, self).__init__(parent) self._text = QtWidgets.QTextEdit() self._text.setTextInteractionFlags(Qt.TextSelectableByMouse) clear = QtWidgets.QPushButton("Clear") clear.clicked.connect(nonpartial(self._clear)) report = QtWidgets.QPushButton("Send Bug Report") report.clicked.connect(nonpartial(self._send_report)) self.stderr = sys.stderr sys.stderr = self self._status = ClickableLabel() self._status.setToolTip("View Errors and Warnings") self._status.clicked.connect(self._show) self._status.setPixmap(status_pixmap()) self._status.setContentsMargins(0, 0, 0, 0) l = QtWidgets.QVBoxLayout() h = QtWidgets.QHBoxLayout() l.setContentsMargins(2, 2, 2, 2) l.setSpacing(2) h.setContentsMargins(0, 0, 0, 0) l.addWidget(self._text) h.insertStretch(0) h.addWidget(report) h.addWidget(clear) l.addLayout(h) self.setLayout(l)
def __init__(self, vispy_viewer, layer=None, layer_state=None): super(ScatterLayerArtist, self).__init__(layer) self._clip_limits = None self._marker_keep = Ellipsis # Set data caches self._marker_data = None self._color_data = None self._size_data = None self.layer = layer or layer_state.layer self.vispy_viewer = vispy_viewer self.vispy_widget = vispy_viewer._vispy_widget # TODO: need to remove layers when layer artist is removed self._viewer_state = vispy_viewer.state self.state = layer_state or ScatterLayerState(layer=self.layer) if self.state not in self._viewer_state.layers: self._viewer_state.layers.append(self.state) # We create a unique ID for this layer artist, that will be used to # refer to the layer artist in the MultiColorScatter. We have to do this # rather than use self.id because we can't guarantee the latter is # unique. self.id = str(uuid.uuid4()) # We need to use MultiColorScatter instance to store scatter plots, but # we should only have one per canvas. Therefore, we store the # MultiColorScatter instance in the vispy viewer instance. if not hasattr(self.vispy_widget, '_multiscat'): multiscat = MultiColorScatter() multiscat.set_gl_state(depth_test=False, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self.vispy_widget.add_data_visual(multiscat) self.vispy_widget._multiscat = multiscat self._multiscat = self.vispy_widget._multiscat self._multiscat.allocate(self.id) self._multiscat.set_zorder(self.id, self.get_zorder) try: self.state.add_callback('*', self._update_from_state, as_kwargs=True) except TypeError: # glue-core >= 0.11 self.state.add_global_callback(self._update_from_state) self._update_from_state(**self.state.as_dict()) self._viewer_state.add_callback('x_att', nonpartial(self._update_data)) self._viewer_state.add_callback('y_att', nonpartial(self._update_data)) self._viewer_state.add_callback('z_att', nonpartial(self._update_data)) self._update_data() self.visible = True
def register_to_hub(self, hub): super(ScatterWidget, self).register_to_hub(hub) self.client.register_to_hub(hub) hub.subscribe(self, core.message.DataUpdateMessage, nonpartial(self._sync_labels)) hub.subscribe(self, core.message.ComponentsChangedMessage, nonpartial(self._update_combos)) hub.subscribe(self, core.message.ComponentReplacedMessage, self._on_component_replace)
def __init__(self, vispy_widget=None, parent=None): super(VispyDataViewerToolbar, self).__init__(parent=parent) # Keep a reference to the Vispy Widget and the Vispy Data Viewer self._vispy_widget = vispy_widget self._vispy_data_viewer = parent self._data_collection = parent._data # Set visual preferences self.setIconSize(QtCore.QSize(25, 25)) # Initialize drawing visual self.line_pos = [] self.line = scene.visuals.Line(color='white', method='gl', parent=self._vispy_widget.canvas.scene) # Selection defaults self._scatter = None self.mode = None self.selection_origin = (0, 0) self.selected = [] # Set up selection actions a = QtGui.QAction(get_icon('glue_lasso'), 'Lasso Selection', parent) a.triggered.connect(nonpartial(self.toggle_lasso)) a.setCheckable(True) parent.addAction(a) self.addAction(a) self.lasso_action = a a = QtGui.QAction(get_icon('glue_square'), 'Rectangle Selection', parent) a.triggered.connect(nonpartial(self.toggle_rectangle)) a.setCheckable(True) parent.addAction(a) self.addAction(a) self.rectangle_action = a a = QtGui.QAction(get_icon('glue_circle'), 'Ellipse Selection', parent) a.triggered.connect(nonpartial(self.toggle_ellipse)) a.setCheckable(True) parent.addAction(a) self.addAction(a) self.ellipse_action = a # TODO: change path to icon once it's in a released version of glue a = QtGui.QAction(QtGui.QIcon(POINT_ICON), 'Point Selection', parent) a.triggered.connect(nonpartial(self.toggle_point)) a.setCheckable(True) parent.addAction(a) self.addAction(a) self.point_action = a # Connect callback functions to VisPy Canvas self._vispy_widget.canvas.events.mouse_press.connect(self.on_mouse_press) self._vispy_widget.canvas.events.mouse_release.connect(self.on_mouse_release) self._vispy_widget.canvas.events.mouse_move.connect(self.on_mouse_move)
def initialize_toolbar(self): super(ImageWidget, self).initialize_toolbar() # connect viewport update buttons to client commands to # allow resampling cl = self.client self.toolbar.actions['mpl:home'].triggered.connect(nonpartial(cl.check_update)) self.toolbar.actions['mpl:forward'].triggered.connect(nonpartial(cl.check_update)) self.toolbar.actions['mpl:back'].triggered.connect(nonpartial(cl.check_update))
def register_to_hub(self, hub): super(DataCollectionComboHelper, self).register_to_hub(hub) hub.subscribe(self, DataUpdateMessage, handler=nonpartial(self.refresh), filter=lambda msg: msg.sender in self._datasets) hub.subscribe(self,DataCollectionAddMessage, handler=nonpartial(self.refresh), filter=lambda msg: msg.sender is self._datasets) hub.subscribe(self, DataCollectionDeleteMessage, handler=nonpartial(self.refresh), filter=lambda msg: msg.sender is self._datasets)
def __init__(self, label="", pix2world=None, lo=0, hi=10, parent=None, aggregation=None): super(SliceWidget, self).__init__(parent) if aggregation is not None: raise NotImplemented("Aggregation option not implemented") if pix2world is not None: raise NotImplemented("Pix2world option not implemented") layout = QtGui.QVBoxLayout() layout.setContentsMargins(3, 1, 3, 1) layout.setSpacing(0) top = QtGui.QHBoxLayout() top.setContentsMargins(3, 3, 3, 3) label = QtGui.QLabel(label) top.addWidget(label) mode = QtGui.QComboBox() mode.addItem("x", "x") mode.addItem("y", "y") mode.addItem("slice", "slice") mode.currentIndexChanged.connect(lambda x: self.mode_changed.emit(self.mode)) mode.currentIndexChanged.connect(self._update_mode) top.addWidget(mode) layout.addLayout(top) slider = load_ui("data_slice_widget.ui", None, directory=os.path.dirname(__file__)) slider.slider slider.slider.setMinimum(lo) slider.slider.setMaximum(hi) slider.slider.setValue((lo + hi) / 2) slider.slider.valueChanged.connect(lambda x: self.slice_changed.emit(self.mode)) slider.slider.valueChanged.connect(lambda x: slider.label.setText(str(x))) slider.label.setMinimumWidth(50) slider.label.setText(str(slider.slider.value())) slider.label.textChanged.connect(lambda x: slider.slider.setValue(int(x))) slider.first.clicked.connect(nonpartial(self._browse_slice, "first")) slider.prev.clicked.connect(nonpartial(self._browse_slice, "prev")) slider.next.clicked.connect(nonpartial(self._browse_slice, "next")) slider.last.clicked.connect(nonpartial(self._browse_slice, "last")) layout.addWidget(slider) self.setLayout(layout) self._ui_label = label self._ui_slider = slider self._ui_mode = mode self._update_mode() self._frozen = False
def __init__(self, parent=None): super(VispyWidget, self).__init__(parent=parent) # Prepare Vispy canvas. We set the depth_size to 24 to avoid issues # with isosurfaces on MacOS X self.canvas = scene.SceneCanvas(keys='interactive', show=False, config={'depth_size': 24}, bgcolor=rgb(settings.BACKGROUND_COLOR)) # Set up a viewbox self.view = self.canvas.central_widget.add_view() self.view.parent = self.canvas.scene # Set whether we are emulating a 3D texture. This needs to be enabled # as a workaround on Windows otherwise VisPy crashes. self.emulate_texture = (sys.platform == 'win32' and sys.version_info[0] < 3) self.scene_transform = scene.STTransform() self.limit_transforms = {} # Add a 3D cube to show us the unit cube. The 1.001 factor is to make # sure that the grid lines are not 'hidden' by volume renderings on the # front side due to numerical precision. vertices, filled_indices, outline_indices = create_cube() self.axis = scene.visuals.Mesh(vertices['position'], outline_indices, color=rgb(settings.FOREGROUND_COLOR), mode='lines') self.axis.transform = self.scene_transform self.view.add(self.axis) # Create a turntable camera. For now, this is the only camerate type # we support, but if we support more in future, we should implement # that here # Orthographic perspective view as default self.view.camera = scene.cameras.TurntableCamera(parent=self.view.scene, fov=0., distance=4.0) # Add the native canvas widget to this widget layout = QtGui.QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.canvas.native) self.setLayout(layout) # We need to call render here otherwise we'll later encounter an OpenGL # program validation error. self.canvas.render() # Set up callbacks add_callback(self, 'visible_axes', nonpartial(self._toggle_axes)) add_callback(self, 'perspective_view', nonpartial(self._toggle_perspective))
def __init__(self, parent=None): super(DataCollectionView, self).__init__(parent) self.doubleClicked.connect(self._edit) # this keeps the full-row of the selection bar in-sync self.pressed.connect(nonpartial(self._update_viewport)) # only edit label on model.new_item self.setItemDelegate(LabeledDelegate()) self.setEditTriggers(self.NoEditTriggers) self._timer = QtCore.QTimer(self) self._timer.timeout.connect(nonpartial(self._update_viewport)) self._timer.start(1000)
def menu_actions(self): result = [] a = QtWidgets.QAction("minmax", None) a.triggered.connect(nonpartial(self.set_clip_percentile, 0, 100)) result.append(a) a = QtWidgets.QAction("99%", None) a.triggered.connect(nonpartial(self.set_clip_percentile, 1, 99)) result.append(a) a = QtWidgets.QAction("95%", None) a.triggered.connect(nonpartial(self.set_clip_percentile, 5, 95)) result.append(a) a = QtWidgets.QAction("90%", None) a.triggered.connect(nonpartial(self.set_clip_percentile, 10, 90)) result.append(a) rng = QtWidgets.QAction("Set range...", None) rng.triggered.connect(nonpartial(self.choose_vmin_vmax)) result.append(rng) a = QtWidgets.QAction("", None) a.setSeparator(True) result.append(a) a = QtWidgets.QAction("linear", None) a.triggered.connect(nonpartial(setattr, self, 'stretch', 'linear')) result.append(a) a = QtWidgets.QAction("log", None) a.triggered.connect(nonpartial(setattr, self, 'stretch', 'log')) result.append(a) a = QtWidgets.QAction("sqrt", None) a.triggered.connect(nonpartial(setattr, self, 'stretch', 'sqrt')) result.append(a) a = QtWidgets.QAction("asinh", None) a.triggered.connect(nonpartial(setattr, self, 'stretch', 'arcsinh')) result.append(a) for r in result: if r is rng: continue if self._move_callback is not None: r.triggered.connect(nonpartial(self._move_callback, self)) return result
def __init__(self, plotly_args=[], plotly_kwargs={}, parent=None): super(QtPlotlyExporter, self).__init__(parent=parent) self.plotly_args = plotly_args self.plotly_kwargs = plotly_kwargs self.ui = load_ui('exporter.ui', self, directory=os.path.dirname(__file__)) self.button_cancel.clicked.connect(self.reject) self.button_export.clicked.connect(self.accept) # Set up radio button groups self._radio_account = QtWidgets.QButtonGroup() self._radio_account.addButton(self.ui.radio_account_glue) self._radio_account.addButton(self.ui.radio_account_config) self._radio_account.addButton(self.ui.radio_account_manual) self._radio_sharing = QtWidgets.QButtonGroup() self._radio_sharing.addButton(self.ui.radio_sharing_public) self._radio_sharing.addButton(self.ui.radio_sharing_secret) self._radio_sharing.addButton(self.ui.radio_sharing_private) # Find out stored credentials (note that this will create the # credentials file if it doesn't already exist) from plotly import plotly credentials = plotly.get_credentials() config_available = credentials['username'] != "" and credentials['api_key'] != "" if config_available: self.ui.radio_account_config.setChecked(True) label = self.ui.radio_account_config.text() self.ui.radio_account_config.setText(label + " (username: {0})".format(credentials['username'])) else: self.ui.radio_account_glue.setChecked(True) self.ui.radio_account_config.setEnabled(False) self.ui.radio_sharing_secret.setChecked(True) self.ui.text_username.textChanged.connect(nonpartial(self._set_manual_mode)) self.ui.text_api_key.textChanged.connect(nonpartial(self._set_manual_mode)) self.ui.radio_account_glue.toggled.connect(nonpartial(self._set_allowed_sharing_modes)) self.set_status('', color='black') self._set_allowed_sharing_modes()
def _create_actions(self): tree = self.ui.layerTree sep = QtWidgets.QAction("", tree) sep.setSeparator(True) tree.addAction(sep) # Actions relating to I/O self._actions['save_data'] = ExportDataAction(self) self._actions['save_subset'] = ExportSubsetAction(self) self._actions['import_subset_mask'] = ImportSubsetMaskAction(self) self._actions['export_subset_mask'] = ExportSubsetMaskAction(self) self._actions['copy'] = CopyAction(self) self._actions['paste'] = PasteAction(self) self._actions['paste_special'] = PasteSpecialAction(self) self._actions['invert'] = Inverter(self) self._actions['new'] = NewAction(self) self._actions['clear'] = ClearAction(self) self._actions['delete'] = DeleteAction(self) self._actions['facet'] = FacetAction(self) self._actions['metadata'] = MetadataAction(self) self._actions['merge'] = MergeAction(self) self._actions['maskify'] = MaskifySubsetAction(self) self._actions['link'] = LinkAction(self) sep = QtWidgets.QAction("", tree) sep.setSeparator(True) tree.addAction(sep) a = action("Add/edit arithmetic attributes", self, tip="Add/edit attributes derived from existing ones") tree.addAction(a) a.triggered.connect(nonpartial(self._create_component)) self._actions['new_component'] = a a = action("Reorder/rename data attributes", self, tip="Reorder/rename data attributes") tree.addAction(a) a.triggered.connect(nonpartial(self._manage_components)) self._actions['manage_components'] = a # Add user-defined layer actions. Note that _asdict is actually a public # method, but just has an underscore to prevent conflict with # namedtuple attributes. for item in layer_action: self._actions[item.label] = UserAction(self, **item._asdict()) # right click pulls up menu tree.setContextMenuPolicy(Qt.ActionsContextMenu)
def __init__(self, state, attribute, cache=None, **kwargs): self._state = state self._attribute = attribute self._values = dict((key, kwargs[key]) for key in self.values_names if key in kwargs) self._modifiers = dict((key, kwargs[key]) for key in self.modifiers_names if key in kwargs) self._attribute_lookup = {'attribute': self._attribute} self._attribute_lookup.update(self._values) self._attribute_lookup.update(self._modifiers) self._attribute_lookup_inv = {v: k for k, v in self._attribute_lookup.items()} self._state.add_callback(self._attribute, nonpartial(self._update_attribute)) for prop in self._modifiers.values(): self._state.add_callback(prop, self._update_values, as_kwargs=True) for prop in self._values.values(): self._state.add_callback(prop, self._update_values, as_kwargs=True) # NOTE: don't use self._cache = cache or {} here since if the initial # cache is empty it will evaluate as False! if cache is None: self._cache = {} else: self._cache = cache
def __init__(self, instance, option): super(BoolFormItem, self).__init__(instance, option) value = option.__get__(instance) self.widget = QtWidgets.QCheckBox() self.widget.setChecked(value) self.widget.clicked.connect(nonpartial(self.changed.emit))
def __init__(self, layer, vispy_viewer): super(IsosurfaceLayerArtist, self).__init__(layer) self.layer = layer self.vispy_viewer = vispy_viewer self._iso_visual = scene.Isosurface(np.ones((3, 3, 3)), level=0.5, shading='smooth') self.vispy_viewer.add_data_visual(self._iso_visual) # Set up connections so that when any of the properties are # modified, we update the appropriate part of the visualization add_callback(self, 'attribute', nonpartial(self._update_data)) add_callback(self, 'level', nonpartial(self._update_level)) add_callback(self, 'color', nonpartial(self._update_color)) add_callback(self, 'alpha', nonpartial(self._update_color))
def __init__(self, application, parent=None): super(PreferencesDialog, self).__init__(parent=parent) self.app = application self.ui = load_ui('preferences.ui', self, directory=os.path.dirname(__file__)) self.ui.cancel.clicked.connect(self.reject) self.ui.ok.clicked.connect(self.accept) self.ui.combo_theme.currentIndexChanged.connect(nonpartial(self._update_colors_from_theme)) from glue.config import settings self.background = settings.BACKGROUND_COLOR self.foreground = settings.FOREGROUND_COLOR self.data_color = settings.DATA_COLOR self.data_alpha = settings.DATA_ALPHA self.show_large_data_warning = settings.SHOW_LARGE_DATA_WARNING self._update_theme_from_colors() self.panes = [] from glue.config import preference_panes for label, widget_cls in sorted(preference_panes): pane = widget_cls() self.ui.tab_widget.addTab(pane, label) self.panes.append(pane)
def register_to_hub(self, hub): hub.subscribe(self, ComponentsChangedMessage, handler=nonpartial(self.refresh), filter=lambda msg: msg.data in self._data) hub.subscribe(self, DataCollectionDeleteMessage, handler=lambda msg: self.remove_data(msg.data), filter=lambda msg: msg.sender is self._data_collection)
def __init__(self, session, parent=None): super(PyspeckitViewer, self).__init__(session, parent=parent) class Client(object): pass self.central_widget = MplWidget() self.client = Client() self.client.axes = self.central_widget.canvas.fig.add_subplot(1, 1, 1) self.central_widget = self.central_widget self._control_panel = ControlPanel() self._control_panel.modeChanged.connect(lambda mode: self.set_mode(init=True)) self._control_panel.fitEvent.connect(nonpartial(self.run_fitter)) # TODO: implement subtraction # self._control_panel.subtractEvent.connect(nonpartial(self.subtract)) self._options_widget = OptionsWidget(data_viewer=self) self._splitter = QtWidgets.QSplitter() self._splitter.setOrientation(Qt.Horizontal) self._splitter.addWidget(self.central_widget) self._splitter.addWidget(self._control_panel) self.setCentralWidget(self._splitter) self.spectra = {} self.spectrum = None
def __init__(self, **kwargs): super(Vispy3DViewerState, self).__init__(**kwargs) if self.limits_cache is None: self.limits_cache = {} self.x_lim_helper = StateAttributeLimitsHelper(self, attribute='x_att', lower='x_min', upper='x_max', cache=self.limits_cache) self.y_lim_helper = StateAttributeLimitsHelper(self, attribute='y_att', lower='y_min', upper='y_max', cache=self.limits_cache) self.z_lim_helper = StateAttributeLimitsHelper(self, attribute='z_att', lower='z_min', upper='z_max', cache=self.limits_cache) # TODO: if limits_cache is re-assigned to a different object, we need to # update the attribute helpers. However if in future we make limits_cache # into a smart dictionary that can call callbacks when elements are # changed then we shouldn't always call this. It'd also be nice to # avoid this altogether and make it more clean. self.add_callback('limits_cache', nonpartial(self._update_limits_cache))
def _add_argument_widget(self, argument): """ Create and add a single argument widget to the input canvas :param arguement: The argument name (string) """ widget = ArgumentWidget(argument) widget.editor.textChanged.connect(nonpartial(self._update_add_enabled)) self._ui.input_canvas.layout().addWidget(widget) self._argument_widgets.append(widget)
def menu_actions(self): acts = [] for label in ginga_cmap.get_names(): cmap = ginga_cmap.get_cmap(label) a = ColormapAction(label, cmap, self.viewer) a.triggered.connect(nonpartial(self.viewer.client.set_cmap, cmap)) acts.append(a) return acts
def _connect(self): # attach Qt signals ds = self._ui.data_selector ds.currentIndexChanged.connect(nonpartial(self._set_components)) self._ui.component_selector.currentItemChanged.connect( lambda *args: self.component_changed.emit()) self._ui.data_selector.currentIndexChanged.connect( lambda *args: self.data_changed.emit())
def make_toolbar(self): result = GlueToolbar(self.central_widget.canvas, self, name='Image') for mode in self._mouse_modes(): result.add_mode(mode) cmap = _colormap_mode(self, self.client.set_cmap) result.addWidget(cmap) # connect viewport update buttons to client commands to # allow resampling cl = self.client result.buttons['HOME'].triggered.connect(nonpartial(cl.check_update)) result.buttons['FORWARD'].triggered.connect(nonpartial( cl.check_update)) result.buttons['BACK'].triggered.connect(nonpartial(cl.check_update)) return result
def menu_actions(self): from glue import config acts = [] for label, cmap in config.colormaps: a = ColormapAction(label, cmap, self.viewer) a.triggered.connect(nonpartial(self.viewer.set_cmap, cmap)) acts.append(a) return acts
def __init__(self, parent=None): # Prepare Vispy canvas. We set the depth_size to 24 to avoid issues # with isosurfaces on MacOS X self.canvas = scene.SceneCanvas(keys=None, show=False, config={'depth_size': 24}, bgcolor=rgb(settings.BACKGROUND_COLOR)) # Set up a viewbox self.view = self.canvas.central_widget.add_view() self.view.parent = self.canvas.scene # Set whether we are emulating a 3D texture. This needs to be enabled # as a workaround on Windows otherwise VisPy crashes. self.emulate_texture = (sys.platform == 'win32' and sys.version_info[0] < 3) self.scene_transform = scene.STTransform() self.limit_transforms = {} fc = rgb(settings.FOREGROUND_COLOR) self.axis = AxesVisual3D(axis_color=fc, tick_color=fc, text_color=fc, tick_width=1, minor_tick_length=2, major_tick_length=4, axis_width=0, tick_label_margin=10, axis_label_margin=25, tick_font_size=6, axis_font_size=8, view=self.view, transform=self.scene_transform) # Create a turntable camera. For now, this is the only camerate type # we support, but if we support more in future, we should implement # that here # Orthographic perspective view as default self.view.camera = scene.cameras.TurntableCamera(parent=self.view.scene, fov=0., distance=4.0) # We need to call render here otherwise we'll later encounter an OpenGL # program validation error. # self.canvas.render() # Set up callbacks add_callback(self, 'visible_axes', nonpartial(self._toggle_axes)) add_callback(self, 'perspective_view', nonpartial(self._toggle_perspective))
def _make_mode(self, name, tip, icon, mode): a = action(name, self, tip, icon) a.setCheckable(True) a.triggered.connect(nonpartial(set_mode, mode)) self._group.addAction(a) self.addAction(a) self._modes[mode] = a label = name.split()[0].lower().replace('&', '') self._modes[label] = mode
def connect_color(client, prop, widget): def update_widget(text): widget.setColor(text) def update_prop(): setattr(client, prop, widget.color()) add_callback(client, prop, update_widget) widget.colorChanged.connect(nonpartial(update_prop))
def __init__(self, layer, vispy_viewer): super(IsosurfaceLayerArtist, self).__init__(layer) self.layer = layer self.vispy_viewer = vispy_viewer self._iso_visual = Isosurface(np.ones((3, 3, 3)), level=0.5, shading='smooth') self.vispy_viewer.add_data_visual(self._iso_visual) # Set up connections so that when any of the properties are # modified, we update the appropriate part of the visualization add_callback(self, 'attribute', nonpartial(self._update_data)) add_callback(self, 'level', nonpartial(self._update_level)) add_callback(self, 'color', nonpartial(self._update_color)) add_callback(self, 'alpha', nonpartial(self._update_color))
def add_mode(self, mode): parent = QtWidgets.QToolBar.parent(self) def toggle(): self._custom_mode(mode) def enable(): # turn on if not if self._active != mode.mode_id: self._custom_mode(mode) action = QtWidgets.QAction(mode.icon, mode.action_text, parent) action.triggered.connect(nonpartial(toggle)) parent.addAction(action) self.__signals.extend([toggle, enable]) if mode.shortcut is not None: action.setShortcut(mode.shortcut) action.setShortcutContext(Qt.WidgetShortcut) action.setToolTip(mode.tool_tip) action.setCheckable(True) self.buttons[mode.mode_id] = action menu_actions = mode.menu_actions() if len(menu_actions) > 0: menu = QtWidgets.QMenu(self) for ma in mode.menu_actions(): ma.setParent(self) menu.addAction(ma) action.setMenu(menu) menu.triggered.connect(nonpartial(enable)) self.addAction(action) # bind action status to mode.enabled def toggle(state): action.setVisible(state) action.setEnabled(state) add_callback(mode, 'enabled', toggle) return action
def __init__(self, instance, option): super(NumberFormItem, self).__init__(instance, option) value = option.__get__(instance) w = self.widget_cls() w.setRange(option.min, option.max) w.setValue(value) w.valueChanged.connect(nonpartial(self.changed.emit)) self.widget = w
def _build_ui(self): w = QtGui.QComboBox() for p in sorted(self.params): w.addItem(p) if isinstance(self.params, list): self.params = dict((p, p) for p in self.params) w.currentIndexChanged.connect(nonpartial(self.changed)) return w
def _create_actions(self): act = QtGui.QAction("Edit style", self) act.triggered.connect(nonpartial(self._edit_style)) self.addAction(act) act = QtGui.QAction("Remove", self) act.setShortcut(QtGui.QKeySequence(Qt.Key_Backspace)) act.setShortcutContext(Qt.WidgetShortcut) act.triggered.connect(lambda *args: self.model().removeRow(self.current_row())) self.addAction(act)
def menu_actions(self): from ginga.ColorDist import get_dist_names result = [] for algname in get_dist_names(): a = QtWidgets.QAction(algname, None) a.triggered.connect(nonpartial(self.set_dist, algname)) result.append(a) return result
def register_to_hub(self, hub): super(ManualDataComboHelper, self).register_to_hub(hub) hub.subscribe(self, DataUpdateMessage, handler=nonpartial(self.refresh), filter=lambda msg: msg.sender in self._datasets) hub.subscribe(self, DataCollectionDeleteMessage, handler=lambda msg: self.remove(msg.data), filter=lambda msg: msg.sender is self._data_collection)
def _create_actions(self): act = QtGui.QAction('Edit style', self) act.triggered.connect(nonpartial(self._edit_style)) self.addAction(act) act = QtGui.QAction('Remove', self) act.setShortcut(QtGui.QKeySequence(Qt.Key_Backspace)) act.setShortcutContext(Qt.WidgetShortcut) act.triggered.connect( lambda *args: self.model().removeRow(self.current_row())) self.addAction(act)
def menu_actions(self): result = [] a = QtWidgets.QAction('Rectangle', None) a.triggered.connect(nonpartial(self.set_roi_tool, 'Rectangle')) result.append(a) a = QtWidgets.QAction('Circle', None) a.triggered.connect(nonpartial(self.set_roi_tool, 'Circle')) result.append(a) a = QtWidgets.QAction('Polygon', None) a.triggered.connect(nonpartial(self.set_roi_tool, 'Polygon')) result.append(a) for r in result: if self._move_callback is not None: r.triggered.connect(nonpartial(self._move_callback, self)) return result
def __init__(self, parent=None): super(VispyWidget, self).__init__(parent=parent) # Prepare Vispy canvas. We set the depth_size to 24 to avoid issues # with isosurfaces on MacOS X self.canvas = scene.SceneCanvas(keys='interactive', show=False, config={'depth_size': 24}) # Set up a viewbox self.view = self.canvas.central_widget.add_view() self.view.parent = self.canvas.scene # Set whether we are emulating a 3D texture. This needs to be enabled # as a workaround on Windows otherwise VisPy crashes. self.emulate_texture = (sys.platform == 'win32' and sys.version_info[0] < 3) self.scene_transform = scene.STTransform() self.limit_transforms = {} # Add a 3D cube to show us the unit cube. The 1.001 factor is to make # sure that the grid lines are not 'hidden' by volume renderings on the # front side due to numerical precision. vertices, filled_indices, outline_indices = create_cube() self.axis = scene.visuals.Mesh(vertices['position'], outline_indices, color=(1, 1, 1), mode='lines') self.axis.transform = self.scene_transform self.view.add(self.axis) # Create a turntable camera. For now, this is the only camerate type # we support, but if we support more in future, we should implement # that here # Remove the fov=60 here to solve the mismatch of selection problem # self.view.camera = scene.cameras.TurntableCamera(parent=self.view.scene, distance=2) self.view.camera = scene.cameras.TurntableCamera( parent=self.view.scene, distance=2.0) # Add the native canvas widget to this widget layout = QtGui.QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.canvas.native) self.setLayout(layout) # We need to call render here otherwise we'll later encounter an OpenGL # program validation error. self.canvas.render() # Set up callbacks add_callback(self, 'visible_axes', nonpartial(self._toggle_axes))
def __init__(self, viewer_state, session, parent=None): super(HistogramOptionsWidget, self).__init__(parent=parent) self.ui = load_ui('options_widget.ui', self, directory=os.path.dirname(__file__)) autoconnect_callbacks_to_qt(viewer_state, self.ui) self.viewer_state = viewer_state viewer_state.add_callback('x_att', nonpartial(self._update_attribute))
def _connect(self): ui = self.ui ui.monochrome.toggled.connect(self._update_rgb_console) ui.rgb_options.colors_changed.connect(self.update_window_title) # sync client and widget slices ui.slice.slice_changed.connect(lambda: setattr(self, 'slice', self.ui.slice.slice)) update_ui_slice = lambda val: setattr(ui.slice, 'slice', val) add_callback(self.client, 'slice', update_ui_slice) add_callback(self.client, 'display_data', self.ui.slice.set_data) # sync window title to data/attribute add_callback(self.client, 'display_data', nonpartial(self._display_data_changed)) add_callback(self.client, 'display_attribute', nonpartial(self._display_attribute_changed)) add_callback(self.client, 'display_aspect', nonpartial(self.client._update_aspect)) # sync data/attribute combos with client properties connect_current_combo(self.client, 'display_data', self.ui.displayDataCombo) connect_current_combo(self.client, 'display_attribute', self.ui.attributeComboBox) connect_current_combo(self.client, 'display_aspect', self.ui.aspectCombo)
def menu_actions(self): result = [] a = QtWidgets.QAction('Rectangle', None) a.triggered.connect(nonpartial(self.set_roi_tool, 'rectangle')) result.append(a) a = QtWidgets.QAction('Circle', None) a.triggered.connect(nonpartial(self.set_roi_tool, 'circle')) result.append(a) a = QtWidgets.QAction('Lasso', None) a.triggered.connect(nonpartial(self.set_roi_tool, 'freepolygon')) result.append(a) a = QtWidgets.QAction('Polygon', None) a.triggered.connect(nonpartial(self.set_roi_tool, 'polygon')) result.append(a) return result
def __init__(self, button_console, parent=None): super(GlueLogger, self).__init__(parent) self.button_console = button_console self.button_stylesheet = button_console.styleSheet() self.button_console.clicked.connect(self._show) self._text = QtWidgets.QTextEdit() self._text.setTextInteractionFlags(Qt.TextSelectableByMouse) clear = QtWidgets.QPushButton("Clear") clear.clicked.connect(nonpartial(self._clear)) report = QtWidgets.QPushButton("Send Bug Report") report.clicked.connect(nonpartial(self._send_report)) if isinstance(sys.stderr, GlueLogger): if isinstance(sys.stderr._stderr_original, GlueLogger): raise Exception('Too many nested GlueLoggers') self._stderr_original = sys.stderr._stderr_original else: self._stderr_original = sys.stderr sys.stderr = self l = QtWidgets.QVBoxLayout() h = QtWidgets.QHBoxLayout() l.setContentsMargins(2, 2, 2, 2) l.setSpacing(2) h.setContentsMargins(0, 0, 0, 0) l.addWidget(self._text) h.insertStretch(0) h.addWidget(report) h.addWidget(clear) l.addLayout(h) self.setLayout(l)
def _connect(self): """ Connect widget signals to methods """ self._actions['link'] = LinkAction(self) self.layerAddButton.clicked.connect(nonpartial(self._load_data)) self.layerRemoveButton.clicked.connect(self._actions['delete'].trigger) self.linkButton.set_action(self._actions['link']) self.newSubsetButton.set_action(self._actions['new'], text=False) rbut = self.layerRemoveButton def update_enabled(): return rbut.setEnabled(self._actions['delete'].isEnabled()) self.layerTree.selection_changed.connect(update_enabled)
def register_to_hub(self, hub): super(TableWidget, self).register_to_hub(hub) def dfilter(x): return x.sender.data is self.data hub.subscribe(self, msg.SubsetCreateMessage, handler=nonpartial(self._refresh), filter=dfilter) hub.subscribe(self, msg.SubsetUpdateMessage, handler=nonpartial(self._refresh), filter=dfilter) hub.subscribe(self, msg.SubsetDeleteMessage, handler=nonpartial(self._refresh), filter=dfilter) hub.subscribe(self, msg.DataUpdateMessage, handler=nonpartial(self._refresh), filter=dfilter)
def menu(self): m = QtGui.QMenu() a = QtGui.QAction("Or", m) a.setIcon(get_icon('glue_or')) a.triggered.connect(nonpartial(self._paste, OrMode)) m.addAction(a) a = QtGui.QAction("And", m) a.setIcon(get_icon('glue_and')) a.triggered.connect(nonpartial(self._paste, AndMode)) m.addAction(a) a = QtGui.QAction("XOR", m) a.setIcon(get_icon('glue_xor')) a.triggered.connect(nonpartial(self._paste, XorMode)) m.addAction(a) a = QtGui.QAction("Not", m) a.setIcon(get_icon('glue_andnot')) a.triggered.connect(nonpartial(self._paste, AndNotMode)) m.addAction(a) return m
def __init__(self, session, parent=None): super(BaseVizViewer, self).__init__(session, parent=parent) # Connect the dataview to the specviz messaging system DispatchHandle.setup(self) # We now set up the options widget. This controls for example which # attribute should be used to indicate the filenames of the spectra. self._options_widget = OptionsWidget(data_viewer=self) # The layer widget is used to select which data or subset to show. # We don't use the default layer list, because in this case we want to # make sure that only one dataset or subset can be selected at any one # time. self._layer_widget = LayerWidget() # Make sure we update the viewer if either the selected layer or the # column specifying the filename is changed. self._layer_widget.ui.combo_active_layer.currentIndexChanged.connect( nonpartial(self._update_options)) self._layer_widget.ui.combo_active_layer.currentIndexChanged.connect( nonpartial(self._refresh_data)) self._options_widget.ui.combo_file_attribute.currentIndexChanged.connect( nonpartial(self._refresh_data))
def __init__(self, session, viewer_state=None, parent=None): super(BaseVispyViewer, self).__init__(session, parent=parent) self.state = viewer_state or self._state_cls() if BROKEN_CONDA_PYQT5: QtWidgets.QMessageBox.critical(self, "Error", BROKEN_CONDA_PYQT5_MESSAGE) raise Exception(BROKEN_CONDA_PYQT5_MESSAGE) self._vispy_widget = VispyWidgetHelper(viewer_state=self.state) self.setCentralWidget(self._vispy_widget.canvas.native) self._options_widget = VispyOptionsWidget(parent=self, viewer_state=self.state) self.state.add_callback('clip_data', nonpartial(self._toggle_clip)) self.status_label = None self.client = None # When layer artists are removed from the layer artist container, we need # to make sure we remove matching layer states in the viewer state # layers attribute. self._layer_artist_container.on_changed(nonpartial(self._sync_state_layers))
def __init__(self, *args, **kwargs): super(QVersionsDialog, self).__init__(*args, **kwargs) self.ui = load_ui('versions.ui', self, directory=os.path.dirname(__file__)) self.resize(400, 500) self.setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint) self.center() self._update_deps() self._clipboard = QtWidgets.QApplication.clipboard() self.ui.button_copy.clicked.connect(nonpartial(self._copy))
def _create_actions(self): tree = self.ui.layerTree sep = QtWidgets.QAction("", tree) sep.setSeparator(True) tree.addAction(sep) # Actions relating to I/O self._actions['save_data'] = ExportDataAction(self) self._actions['save_subset'] = ExportSubsetAction(self) self._actions['import_subset_mask'] = ImportSubsetMaskAction(self) self._actions['export_subset_mask'] = ExportSubsetMaskAction(self) self._actions['copy'] = CopyAction(self) self._actions['paste'] = PasteAction(self) self._actions['paste_special'] = PasteSpecialAction(self) self._actions['invert'] = Inverter(self) self._actions['new'] = NewAction(self) self._actions['clear'] = ClearAction(self) self._actions['delete'] = DeleteAction(self) self._actions['facet'] = FacetAction(self) self._actions['merge'] = MergeAction(self) self._actions['maskify'] = MaskifySubsetAction(self) self._actions['link'] = LinkAction(self) # new component definer separator = QtWidgets.QAction("sep", tree) separator.setSeparator(True) tree.addAction(separator) a = action("Define new component", self, tip="Define a new component using python expressions") tree.addAction(a) a.triggered.connect(nonpartial(self._create_component)) self._actions['new_component'] = a # user-defined layer actions for name, callback, tooltip, icon in single_subset_action: self._actions[name] = SingleSubsetUserAction(self, callback, name=name, tooltip=tooltip, icon=icon) # right click pulls up menu tree.setContextMenuPolicy(Qt.ActionsContextMenu)
def _init_toolbar(self): self.basedir = os.path.join(matplotlib.rcParams['datapath'], 'images') parent = QtWidgets.QToolBar.parent(self) a = QtWidgets.QAction(get_icon('glue_home'), 'Home', parent) a.triggered.connect(nonpartial(self.home)) a.setToolTip('Reset original zoom') a.setShortcut('H') a.setShortcutContext(Qt.WidgetShortcut) parent.addAction(a) self.buttons['HOME'] = a self.addAction(a) a = QtWidgets.QAction(get_icon('glue_filesave'), 'Save', parent) a.triggered.connect(nonpartial(self.save_figure)) a.setToolTip('Save the figure') a.setShortcut('Ctrl+Shift+S') parent.addAction(a) self.buttons['SAVE'] = a self.addAction(a) a = QtWidgets.QAction(get_icon('glue_back'), 'Back', parent) a.triggered.connect(nonpartial(self.back)) parent.addAction(a) self.addAction(a) self.buttons['BACK'] = a a.setToolTip('Back to previous view') a = QtWidgets.QAction(get_icon('glue_forward'), 'Forward', parent) a.triggered.connect(nonpartial(self.forward)) a.setToolTip('Forward to next view') parent.addAction(a) self.buttons['FORWARD'] = a self.addAction(a) a = QtWidgets.QAction(get_icon('glue_move'), 'Pan', parent) a.triggered.connect(nonpartial(self.pan)) a.setToolTip('Pan axes with left mouse, zoom with right') a.setCheckable(True) a.setShortcut('M') a.setShortcutContext(Qt.WidgetShortcut) parent.addAction(a) self.addAction(a) self.buttons['PAN'] = a a = QtWidgets.QAction(get_icon('glue_zoom_to_rect'), 'Zoom', parent) a.triggered.connect(nonpartial(self.zoom)) a.setToolTip('Zoom to rectangle') a.setShortcut('Z') a.setShortcutContext(Qt.WidgetShortcut) a.setCheckable(True) parent.addAction(a) self.addAction(a) self.buttons['ZOOM'] = a
def _create_actions(self): tree = self.ui.layerTree sep = QtWidgets.QAction("", tree) sep.setSeparator(True) tree.addAction(sep) # Actions relating to I/O self._actions['save_data'] = ExportDataAction(self) self._actions['save_subset'] = ExportSubsetAction(self) self._actions['import_subset_mask'] = ImportSubsetMaskAction(self) self._actions['export_subset_mask'] = ExportSubsetMaskAction(self) self._actions['copy'] = CopyAction(self) self._actions['paste'] = PasteAction(self) self._actions['paste_special'] = PasteSpecialAction(self) self._actions['invert'] = Inverter(self) self._actions['new'] = NewAction(self) self._actions['clear'] = ClearAction(self) self._actions['delete'] = DeleteAction(self) self._actions['facet'] = FacetAction(self) self._actions['merge'] = MergeAction(self) self._actions['maskify'] = MaskifySubsetAction(self) self._actions['link'] = LinkAction(self) sep = QtWidgets.QAction("", tree) sep.setSeparator(True) tree.addAction(sep) a = action("Add/edit data components", self, tip="Change existing data components and add new " "components derived from existing ones") tree.addAction(a) a.triggered.connect(nonpartial(self._create_component)) self._actions['new_component'] = a # Add user-defined layer actions. Note that _asdict is actually a public # method, but just has an underscore to prevent conflict with # namedtuple attributes. for item in layer_action: self._actions[item.label] = UserAction(self, **item._asdict()) # right click pulls up menu tree.setContextMenuPolicy(Qt.ActionsContextMenu)
def __init__(self, parent=None): super(LayerArtistView, self).__init__(parent) self.setDragEnabled(True) self.setAcceptDrops(True) self.setDragDropMode(QtGui.QAbstractItemView.InternalMove) self.setIconSize(QtCore.QSize(15, 15)) self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.setContextMenuPolicy(Qt.ActionsContextMenu) self.setEditTriggers(self.NoEditTriggers) self._set_palette() self._actions = {} self._create_actions() self._timer = QtCore.QTimer(self) self._timer.timeout.connect(nonpartial(self._update_viewport)) self._timer.start(1000)
def __init__(self, viewer_state, session, parent=None): super(HistogramOptionsWidget, self).__init__(parent=parent) self.ui = load_ui('options_widget.ui', self, directory=os.path.dirname(__file__)) autoconnect_callbacks_to_qt(viewer_state, self.ui) viewer_state.add_callback('layers', self._update_combo_data) self.x_att_helper = ComponentIDComboHelper(self.ui.combodata_x_att, session.data_collection) self.viewer_state = viewer_state viewer_state.add_callback('x_att', nonpartial(self._update_attribute))
def __init__(self, layer, vispy_viewer): super(VolumeLayerArtist, self).__init__(layer) self.layer = layer self.vispy_viewer = vispy_viewer # We create a unique ID for this layer artist, that will be used to # refer to the layer artist in the MultiVolume. We have to do this # rather than use self.id because we can't guarantee the latter is # unique. self.id = str(uuid.uuid4()) # We need to use MultiVolume instance to store volumes, but we should # only have one per canvas. Therefore, we store the MultiVolume # instance in the vispy viewer instance. if not hasattr(vispy_viewer, '_multivol'): # Set whether we are emulating a 3D texture. This needs to be # enabled as a workaround on Windows otherwise VisPy crashes. emulate_texture = (sys.platform == 'win32' and sys.version_info[0] < 3) try: multivol = MultiVolume(threshold=0.1, emulate_texture=emulate_texture) except: multivol = MultiVolumeLegacy(threshold=0.1, emulate_texture=emulate_texture) self.vispy_viewer.add_data_visual(multivol) vispy_viewer._multivol = multivol self._multivol = vispy_viewer._multivol self._multivol.allocate(self.id) # Set up connections so that when any of the properties are # modified, we update the appropriate part of the visualization add_callback(self, 'attribute', nonpartial(self._update_data)) add_callback(self, 'vmin', nonpartial(self._update_limits)) add_callback(self, 'vmax', nonpartial(self._update_limits)) add_callback(self, 'color', nonpartial(self._update_cmap_from_color)) add_callback(self, 'cmap', nonpartial(self._update_cmap)) add_callback(self, 'alpha', nonpartial(self._update_alpha)) if isinstance(self.layer, Subset): add_callback(self, 'subset_mode', nonpartial(self._update_data))
def _connect(self): ui = self.ui cl = self.client connect_bool_button(cl, 'xlog', ui.xLogCheckBox) connect_bool_button(cl, 'ylog', ui.yLogCheckBox) connect_bool_button(cl, 'xflip', ui.xFlipCheckBox) connect_bool_button(cl, 'yflip', ui.yFlipCheckBox) ui.xAxisComboBox.currentIndexChanged.connect(self.update_xatt) ui.yAxisComboBox.currentIndexChanged.connect(self.update_yatt) ui.hidden_attributes.toggled.connect(lambda x: self._update_combos()) ui.swapAxes.clicked.connect(nonpartial(self.swap_axes)) ui.snapLimits.clicked.connect(cl.snap) connect_float_edit(cl, 'xmin', ui.xmin) connect_float_edit(cl, 'xmax', ui.xmax) connect_float_edit(cl, 'ymin', ui.ymin) connect_float_edit(cl, 'ymax', ui.ymax)
def __init__(self, session, parent=None, wcs=None, state=None): super(MatplotlibDataViewer, self).__init__(session, parent, state=state) # Use MplWidget to set up a Matplotlib canvas inside the Qt window self.mpl_widget = MplWidget() self.setCentralWidget(self.mpl_widget) # TODO: shouldn't have to do this self.central_widget = self.mpl_widget self.figure, self._axes = init_mpl(self.mpl_widget.canvas.fig, wcs=wcs) self.state.add_callback('aspect', self.update_aspect) self.update_aspect() self.state.add_callback('x_min', nonpartial(self.limits_to_mpl)) self.state.add_callback('x_max', nonpartial(self.limits_to_mpl)) self.state.add_callback('y_min', nonpartial(self.limits_to_mpl)) self.state.add_callback('y_max', nonpartial(self.limits_to_mpl)) self.limits_to_mpl() self.state.add_callback('x_log', nonpartial(self.update_x_log)) self.state.add_callback('y_log', nonpartial(self.update_y_log)) self.update_x_log() self.axes.callbacks.connect('xlim_changed', nonpartial(self.limits_from_mpl)) self.axes.callbacks.connect('ylim_changed', nonpartial(self.limits_from_mpl)) self.axes.set_autoscale_on(False) self.central_widget.resize(600, 400) self.resize(self.central_widget.size())