From f69c91af486f76e90dc5c5d7ef43e62753ee8401 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 19 Jan 2017 22:19:33 -0500 Subject: [PATCH 1/2] MNT: clean up qt imports - don't force so much in init, let environment take care of more - account for some of coming Qt5 changes --- xray_vision/__init__.py | 20 +------ xray_vision/messenger/__init__.py | 8 +-- xray_vision/messenger/mpl/cross_section_2d.py | 54 +++++++++---------- 3 files changed, 32 insertions(+), 50 deletions(-) diff --git a/xray_vision/__init__.py b/xray_vision/__init__.py index e9c0e41..fa1f01a 100644 --- a/xray_vision/__init__.py +++ b/xray_vision/__init__.py @@ -34,31 +34,13 @@ ######################################################################## # imports to smooth over differences between PyQt4, PyQt5, PyQt4.1 and PySides -import sip +from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets import matplotlib import logging from logging import NullHandler from ._version import get_versions -sip.setapi('QString', 2) -old = matplotlib.rcParams["backend"] -matplotlib.rcParams["backend"] = "Qt4Agg" - -# use the PySide rcParams if that's your preference -usePyQt4 = True -if usePyQt4: - matplotlib.rcParams["backend.qt4"] = "PyQt4" - # from PyQt4.QtCore import QDateTime - # QDateTime.toPython = QDateTime.toPyDateTime -else: - matplotlib.rcParams["backend.qt4"] = "PySide" - -try: - from matplotlib.backends.qt_compat import QtCore, QtGui -except ImportError: - from matplotlib.backends.qt4_compat import QtCore, QtGui -matplotlib.rcParams["backend"] = old logger = logging.getLogger(__name__) diff --git a/xray_vision/messenger/__init__.py b/xray_vision/messenger/__init__.py index accd7e6..6ee5c04 100644 --- a/xray_vision/messenger/__init__.py +++ b/xray_vision/messenger/__init__.py @@ -36,7 +36,7 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) -from .. import QtCore, QtGui +from .. import QtCore, QtGui, QtWidgets # other relevant imports @@ -179,7 +179,7 @@ def sl_append_data(self, lbl_list, xy_list, axis_list, append_to_end_list): axis_list=axis_list, append_to_end_list=append_to_end_list) self.sl_update_plot() - + @QtCore.Slot(list, list, list, list) def sl_add_datum(self, lbl_list, x_list, y_list, val_list): """ @@ -206,7 +206,7 @@ def sl_add_datum(self, lbl_list, x_list, y_list, val_list): self.sl_update_plot() -class AbstractDisplayWidget(QtGui.QWidget): +class AbstractDisplayWidget(QtWidgets.QWidget): """ AbstractDisplayWidget class docstring. The purpose of this class and its daughter classes is simply to render the @@ -216,4 +216,4 @@ def __init__(self, parent=None, *args, **kwargs): # init the QWidget super(AbstractDisplayWidget, self).__init__(parent=parent, *args, **kwargs) - # do nothing else \ No newline at end of file + # do nothing else diff --git a/xray_vision/messenger/mpl/cross_section_2d.py b/xray_vision/messenger/mpl/cross_section_2d.py index ae23bee..c3762e7 100644 --- a/xray_vision/messenger/mpl/cross_section_2d.py +++ b/xray_vision/messenger/mpl/cross_section_2d.py @@ -36,7 +36,7 @@ unicode_literals) import six -from .. import QtCore, QtGui +from .. import QtCore, QtGui, QtWidgets from matplotlib import colors from matplotlib.pyplot import colormaps @@ -121,7 +121,7 @@ def sl_update_limit_func(self, limit_func): self.sl_update_view() -class CrossSection2DControlWidget(QtGui.QDockWidget): +class CrossSection2DControlWidget(QtWidgets.QDockWidget): """ This object contains the CrossSectionViewer (2D Image Display) and finish the doc string... @@ -138,16 +138,16 @@ class CrossSection2DControlWidget(QtGui.QDockWidget): def __init__(self, name, init_img, num_images): self.default_cmap = AbstractMPLDataView._default_cmap - QtGui.QDockWidget.__init__(self, name) + QtWidgets.QDockWidget.__init__(self, name) # make the control widget float self.setFloating(True) # add a widget that lives in the floating control widget - self._widget = QtGui.QWidget(self) + self._widget = QtWidgets.QWidget(self) # give the widget to the dock widget self.setWidget(self._widget) # create a layout - ctrl_layout = QtGui.QVBoxLayout() + ctrl_layout = QtWidgets.QVBoxLayout() # set the layout to the widget self._widget.setLayout(ctrl_layout) @@ -156,33 +156,33 @@ def __init__(self, name, init_img, num_images): self._hi = np.max(init_img) # set up axis swap buttons - self._cb_ax1 = QtGui.QComboBox(parent=self) - self._cb_ax2 = QtGui.QComboBox(parent=self) - self._btn_swap = QtGui.QPushButton('Swap Axes', parent=self) + self._cb_ax1 = QtWidgets.QComboBox(parent=self) + self._cb_ax2 = QtWidgets.QComboBox(parent=self) + self._btn_swap = QtWidgets.QPushButton('Swap Axes', parent=self) self.init_swap_btns(self._cb_ax1, self._cb_ax2, self._btn_swap) # set up slider and spinbox - self._slider_img = QtGui.QSlider(parent=self) - self._spin_img = QtGui.QSpinBox(parent=self) + self._slider_img = QtWidgets.QSlider(parent=self) + self._spin_img = QtWidgets.QSpinBox(parent=self) # init the slider and spinbox self.init_img_changer(self._slider_img, self._spin_img, num_images) - widget_box1 = QtGui.QVBoxLayout() - slider_label = QtGui.QLabel("&Frame") + widget_box1 = QtWidgets.QVBoxLayout() + slider_label = QtWidgets.QLabel("&Frame") slider_label.setBuddy(self._slider_img) - widget_box1_hbox = QtGui.QHBoxLayout() + widget_box1_hbox = QtWidgets.QHBoxLayout() widget_box1_hbox.addWidget(self._slider_img) widget_box1_hbox.addWidget(self._spin_img) widget_box1.addWidget(slider_label) widget_box1.addLayout(widget_box1_hbox) # set up color map combo box - self._cm_cb = QtGui.QComboBox(parent=self) + self._cm_cb = QtWidgets.QComboBox(parent=self) self.init_cmap_box(self._cm_cb) # set up the interpolation combo box - self._cmb_interp = QtGui.QComboBox(parent=self) + self._cmb_interp = QtWidgets.QComboBox(parent=self) self._cmb_interp.addItems(CrossSection2DView.interpolation) # set up intensity manipulation combo box @@ -201,33 +201,33 @@ def __init__(self, name, init_img, num_images): # TODO should not have to hard-code this, but it is getting # called before it is fully updated, figure out why self._limit_factory = View.fullrange_limit_factory - self._cmbbox_intensity_behavior = QtGui.QComboBox(parent=self) + self._cmbbox_intensity_behavior = QtWidgets.QComboBox(parent=self) self._cmbbox_intensity_behavior.addItems(intensity_behavior_types) # can add PowerNorm, BoundaryNorm, but those require extra inputs norm_names = ['linear', 'log'] norm_funcs = [colors.Normalize, colors.LogNorm] self._norm_dict = {k: v for k, v in zip(norm_names, norm_funcs)} - self._cmbbox_norm = QtGui.QComboBox(parent=self) + self._cmbbox_norm = QtWidgets.QComboBox(parent=self) self._cmbbox_norm.addItems(norm_names) # set up intensity manipulation spin boxes # create the intensity manipulation spin boxes - self._spin_min = QtGui.QDoubleSpinBox(parent=self) - self._spin_max = QtGui.QDoubleSpinBox(parent=self) - self._spin_step = QtGui.QDoubleSpinBox(parent=self) + self._spin_min = QtWidgets.QDoubleSpinBox(parent=self) + self._spin_max = QtWidgets.QDoubleSpinBox(parent=self) + self._spin_step = QtWidgets.QDoubleSpinBox(parent=self) self.init_spinners(self._spin_min, self._spin_max, self._spin_step, min_intensity=np.min(init_img), max_intensity=np.max(init_img)) - ctrl_form = QtGui.QFormLayout() + ctrl_form = QtWidgets.QFormLayout() ctrl_form.addRow("Color &map", self._cm_cb) ctrl_form.addRow("&Interpolation", self._cmb_interp) ctrl_form.addRow("&Normalization", self._cmbbox_norm) ctrl_form.addRow("limit &strategy", self._cmbbox_intensity_behavior) ctrl_layout.addLayout(ctrl_form) - clim_spinners = QtGui.QGroupBox("clim parameters") - ispiner_form = QtGui.QFormLayout() + clim_spinners = QtWidgets.QGroupBox("clim parameters") + ispiner_form = QtWidgets.QFormLayout() ispiner_form.addRow("mi&n", self._spin_min) ispiner_form.addRow("ma&x", self._spin_max) ispiner_form.addRow("s&tep", self._spin_step) @@ -235,13 +235,13 @@ def __init__(self, name, init_img, num_images): ctrl_layout.addWidget(clim_spinners) # construct widget box 1 - widget_box1_sub1 = QtGui.QVBoxLayout() - axes_swap_form = QtGui.QFormLayout() + widget_box1_sub1 = QtWidgets.QVBoxLayout() + axes_swap_form = QtWidgets.QFormLayout() axes_swap_form.addRow("axes A", self._cb_ax1) axes_swap_form.addRow("axes B", self._cb_ax2) widget_box1_sub1.addLayout(axes_swap_form) widget_box1_sub1.addWidget(self._btn_swap) - swap_axes_box = QtGui.QGroupBox("Swap!") + swap_axes_box = QtWidgets.QGroupBox("Swap!") swap_axes_box.setLayout(widget_box1_sub1) swap_axes_box.setEnabled(False) ctrl_layout.addWidget(swap_axes_box) @@ -564,4 +564,4 @@ def set_image_intensity_behavior(self, im_behavior): 'absolute': Display the image with absolute intensity values. """ self._set_combobox_index_by_item_name(self._cmbbox_intensity_behavior, - im_behavior) \ No newline at end of file + im_behavior) From 4e4ddd4c13290ecf5fc4b0087a0f34b21b929270 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 19 Jan 2017 22:20:32 -0500 Subject: [PATCH 2/2] MNT: change draw to draw_idle This is more efficient and works around a bug where mpl segfaults if an invisible Qt canvas is drawn. --- xray_vision/backend/mpl/__init__.py | 2 +- xray_vision/backend/mpl/cross_section_2d.py | 4 ++-- xray_vision/backend/mpl/stack_1d.py | 2 +- xray_vision/messenger/mpl/__init__.py | 4 ++-- xray_vision/plotter.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/xray_vision/backend/mpl/__init__.py b/xray_vision/backend/mpl/__init__.py index b98fdd2..f699374 100644 --- a/xray_vision/backend/mpl/__init__.py +++ b/xray_vision/backend/mpl/__init__.py @@ -100,4 +100,4 @@ def update_norm(self, norm): self._norm = norm def draw(self): - self._fig.canvas.draw() \ No newline at end of file + self._fig.canvas.draw_idle() diff --git a/xray_vision/backend/mpl/cross_section_2d.py b/xray_vision/backend/mpl/cross_section_2d.py index 5c2feb8..f6bcf74 100644 --- a/xray_vision/backend/mpl/cross_section_2d.py +++ b/xray_vision/backend/mpl/cross_section_2d.py @@ -472,7 +472,7 @@ def _connect_callbacks(self): self._clear_cid = self._fig.canvas.mpl_connect('draw_event', self._clear) self._fig.tight_layout() - self._fig.canvas.draw() + self._fig.canvas.draw_idle() def _disconnect_callbacks(self): """ @@ -667,7 +667,7 @@ def _update_artists(self): self._cb_dirty = False def _draw(self): - self._fig.canvas.draw() + self._fig.canvas.draw_idle() @auto_redraw def autoscale_horizontal(self, enable): diff --git a/xray_vision/backend/mpl/stack_1d.py b/xray_vision/backend/mpl/stack_1d.py index abb6582..bfd41ef 100644 --- a/xray_vision/backend/mpl/stack_1d.py +++ b/xray_vision/backend/mpl/stack_1d.py @@ -232,4 +232,4 @@ def clear_data(self): # call the replot function self.replot() # redraw the canvas - self._fig.canvas.draw() + self._fig.canvas.draw_idle() diff --git a/xray_vision/messenger/mpl/__init__.py b/xray_vision/messenger/mpl/__init__.py index 14368af..b7e1780 100644 --- a/xray_vision/messenger/mpl/__init__.py +++ b/xray_vision/messenger/mpl/__init__.py @@ -87,7 +87,7 @@ def sl_update_cmap(self, cmap): @QtCore.Slot() def sl_update_view(self): self._view.replot() - self._view._fig.canvas.draw() + self._view._fig.canvas.draw_idle() class MPLDisplayWidget(AbstractDisplayWidget): @@ -122,4 +122,4 @@ def __init__(self, parent=None, *args, **kwargs): self.setLayout(layout) def draw(self): - self._fig.canvas.draw() + self._fig.canvas.draw_idle() diff --git a/xray_vision/plotter.py b/xray_vision/plotter.py index 9b896a0..d1772a2 100644 --- a/xray_vision/plotter.py +++ b/xray_vision/plotter.py @@ -147,4 +147,4 @@ def plot(self, sample_obj, obj_error, diff_error, sup_error): self.line3, = self.ax3.plot(sup_error) finally: for fig in self.figures: - fig.canvas.draw() + fig.canvas.draw_idle()