diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1956a13 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires = ["setuptools", "wheel", "numpy", "Cython"] diff --git a/setup.py b/setup.py index 55d27a6..3334459 100755 --- a/setup.py +++ b/setup.py @@ -6,35 +6,17 @@ import subprocess -from setuptools import Distribution, setup, find_packages +from setuptools import setup, find_packages from setuptools.extension import Extension -# Fetch numpy and Cython as build dependencies. -Distribution().fetch_build_eggs(['numpy', 'Cython']) - # Safe to import here after line above. import numpy from Cython.Build import cythonize -def find_version(): - try: - short_hash = subprocess.check_output( - ['git', 'rev-parse', '--short', 'HEAD'], - stderr=subprocess.STDOUT - ) - except (FileNotFoundError, subprocess.CalledProcessError): - return None - else: - if short_hash.startswith(b'fatal'): - return None - else: - return short_hash.decode('ascii').strip() - - setup( name='metro-sci', - version=find_version() or 'dev', + version='1.0.0', author='Philipp Schmidt', author_email='philipp.schmidt@xfel.eu', description='Framework for experimental control, data acquisition and ' @@ -103,7 +85,7 @@ def find_version(): include_dirs=[numpy.get_include()]), ], language_level=3, build_dir='build'), - python_requires='>=3.6', + python_requires='>=3.8', install_requires=['typing', 'PyQt5', 'numpy', 'scipy', 'h5py', 'xarray'], classifiers=[ @@ -114,7 +96,7 @@ def find_version(): 'Environment :: X11 Applications :: Qt', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', - 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.8', 'Topic :: Scientific/Engineering :: Information Analysis', 'Topic :: Scientific/Engineering :: Physics', ] diff --git a/src/metro/devices/abstract/single_plot.py b/src/metro/devices/abstract/single_plot.py index a1bf541..b01ad0f 100755 --- a/src/metro/devices/abstract/single_plot.py +++ b/src/metro/devices/abstract/single_plot.py @@ -5,7 +5,7 @@ import metro -from metro.external import pyqtgraph +import pyqtgraph class Device(metro.WidgetDevice, metro.DisplayDevice): diff --git a/src/metro/devices/display/fast_plot.py b/src/metro/devices/display/fast_plot.py index f791d0b..dd2ab2a 100644 --- a/src/metro/devices/display/fast_plot.py +++ b/src/metro/devices/display/fast_plot.py @@ -542,7 +542,7 @@ def paintEvent(self, event): # Vertical annotation lines. if self.vlines is not None and self.show_annotations: div = self.plot_transform[0] - y_end = -self.plot_geometry[3] + y_end = int(-self.plot_geometry[3]) p.save() p.translate( @@ -558,8 +558,8 @@ def paintEvent(self, event): for x_data, text in vlines_it: if self.plot_axes[0] < x_data < self.plot_axes[1]: - x_widget = x_data * div - p.drawLine(x_widget, 0.0, x_widget, y_end) + x_widget = int(x_data * div) + p.drawLine(x_widget, 0, x_widget, y_end) if text is not None: p.drawText(p.boundingRect( @@ -571,7 +571,7 @@ def paintEvent(self, event): # Horizontal annotation lines. if self.hlines is not None and self.show_annotations: div = self.plot_transform[1] - x_end = self.plot_geometry[2] + x_end = int(self.plot_geometry[2]) p.save() p.translate( @@ -587,8 +587,8 @@ def paintEvent(self, event): for y_data, text in hlines_it: if self.plot_axes[2] < y_data < self.plot_axes[3]: - y_widget = -y_data * div - p.drawLine(0.0, y_widget, x_end, y_widget) + y_widget = int(-y_data * div) + p.drawLine(0, y_widget, x_end, y_widget) if text is not None: p.drawText(p.boundingRect( @@ -791,7 +791,7 @@ def on_render_completed(self): def _buildAxisLabel(self, value, x, y, p, flags): text = str(value) - r = p.boundingRect(x, y, 1, 1, flags, text) + r = p.boundingRect(int(x), int(y), 1, 1, flags, text) r._flags = flags r._str = text diff --git a/src/metro/devices/display/hist2d.py b/src/metro/devices/display/hist2d.py index 8f1cb59..b823fb8 100755 --- a/src/metro/devices/display/hist2d.py +++ b/src/metro/devices/display/hist2d.py @@ -271,8 +271,9 @@ def paintEvent(self, event): polygon = self.x_spectrum_polygon for i in range(0, n_points): - polygon.setPoint(i, i / x_scale, - 145 - int(x_spectrum[x_min+i] / y_scale)) + polygon.setPoint( + i, int(i / x_scale), + 145 - int(x_spectrum[x_min+i] / y_scale)) qp.drawPolyline(polygon) @@ -291,14 +292,14 @@ def paintEvent(self, event): x_scale = y_spectrum_max / 85 y_scale = n_points / self.data_img_dest.height() - x_offset = 5 + self.data_img_dest.right() + x_offset = 5 + int(self.data_img_dest.right()) polygon = self.y_spectrum_polygon for i in range(0, n_points): polygon.setPoint( - i, x_offset + int(y_spectrum[y_min+i] / x_scale), - 150 + self.data_img_dest.height() - i / y_scale - ) + i, + x_offset + int(y_spectrum[y_min+i] / x_scale), + 150 + int(self.data_img_dest.height() - i / y_scale)) qp.drawPolyline(polygon) @@ -347,12 +348,12 @@ def paintEvent(self, event): for label, line in zip(self.axes_tick_x_labels, self.axes_tick_x_lines): - qp.drawText(line.x1()-40, line.y1()-20, 80, 20, + qp.drawText(int(line.x1()) - 40, int(line.y1()) - 20, 80, 20, QtCore.Qt.AlignCenter, str(label)) for label, line in zip(self.axes_tick_y_labels, self.axes_tick_y_lines): - qp.drawText(line.x1()+10, line.y1()-10, 80, 20, + qp.drawText(int(line.x1()) + 10, int(line.y1()) - 10, 80, 20, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, str(label)) @@ -524,8 +525,8 @@ def _rebuildStaticObjects(self, qp): coords = roi['coords'] - roi_x = s_x * (coords[0] - self.axes['x_min']) - roi_y = s_y * (self.axes['y_max'] - coords[3]) + roi_x = int(s_x * (coords[0] - self.axes['x_min'])) + roi_y = int(s_y * (self.axes['y_max'] - coords[3])) top_visible = bottom_visible = left_visible = right_visible = True @@ -543,8 +544,8 @@ def _rebuildStaticObjects(self, qp): top_visible = False roi_y = img_height - roi_width = s_x * (coords[2] - coords[0]) - roi_height = s_y * (coords[3] - coords[1]) + roi_width = int(s_x * (coords[2] - coords[0])) + roi_height = int(s_y * (coords[3] - coords[1])) if (roi_x + roi_width) > img_width: right_visible = False @@ -1343,8 +1344,8 @@ def _buildColorPalette(self): mx_60 = max_value * 0.6 + z_min mx_80 = max_value * 0.8 + z_min - color_table = [QtGui.qRgb(255.0, 255.0, 255.0)] * 256 - color_table[0] = QtGui.qRgb(0.0, 0.0, 0.0) + color_table = [QtGui.qRgb(255, 255, 255)] * 256 + color_table[0] = QtGui.qRgb(0, 0, 0) grad = QtGui.QLinearGradient() grad.setStart(0, 0) @@ -1382,8 +1383,7 @@ def _buildColorPalette(self): blue = (i - mx_80) / (max_value - mx_80) # rising color_table[i] = QtGui.qRgb( - red * 255.0, green * 255.0, blue * 255.0 - ) + int(red * 255.0), int(green * 255.0), int(blue * 255.0)) grad.setColorAt(1.0 - (i / max_value), QtGui.QColor.fromRgb(color_table[i])) diff --git a/src/metro/devices/display/image.py b/src/metro/devices/display/image.py index 163fdf7..e3b06d1 100644 --- a/src/metro/devices/display/image.py +++ b/src/metro/devices/display/image.py @@ -3,7 +3,6 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. - from itertools import repeat import math import time @@ -13,18 +12,18 @@ from PyQt5 import QtCore from PyQt5 import QtGui from PyQt5 import QtWidgets +import pyqtgraph +pyqtgraph.setConfigOptions(antialias=False) import metro -from metro.external import pyqtgraph from metro.devices.abstract import fittable_plot -pyqtgraph.setConfigOptions(antialias=False) - # Create a default gradient, which is almost viridis. It adds a new # lower bracket though for very small values (< 1e-3 relatively) which # is pure black. -from metro.external.pyqtgraph.graphicsItems.GradientEditorItem \ - import Gradients # noqa +from pyqtgraph.graphicsItems.GradientEditorItem import Gradients +#from metro.external.pyqtgraph.graphicsItems.GradientEditorItem \ +# import Gradients # noqa default_gradient = Gradients['viridis'].copy() default_gradient['ticks'][0] = (1e-3, default_gradient['ticks'][0][1]) default_gradient['ticks'].insert(0, (0.0, (0, 0, 0, 255))) @@ -78,7 +77,9 @@ def raiseContextMenu(self, ev): class DataImageItem(pyqtgraph.ImageItem): def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + # ImageView requires the image to be array-like when constructed + # with an explicit (this) ImageItem. + super().__init__(*args, image=np.zeros((1, 1)), **kwargs) self._coords = None @@ -132,12 +133,15 @@ def _drawMarkers(self, p, view, markers): for label, pos in markers: m = view.mapViewToDevice(pos) - p.drawLine(m.x() - 20, m.y(), m.x() + 20, m.y()) - p.drawLine(m.x(), m.y() - 20, m.x(), m.y() + 20) + x = int(m.x()) + y = int(m.y()) + + p.drawLine(x - 20, y, x + 20, y) + p.drawLine(x, y - 20, x, y + 20) if label is not None: p.drawText(p.boundingRect( - m.x(), m.y() + 20, 1, 1, flags, label), flags, label) + x, y + 20, 1, 1, flags, label), flags, label) def paint(self, p, *args): # Verbatim copy of ImageItem.paint() except for the actual @@ -145,9 +149,9 @@ def paint(self, p, *args): if self.image is None: return - if self.qimage is None: + if self._renderRequired: self.render() - if self.qimage is None: + if self._unrenderable: return if self.paintMode is not None: p.setCompositionMode(self.paintMode) @@ -163,7 +167,6 @@ def paint(self, p, *args): if self.border is not None: p.setPen(self.border) p.drawRect(self.boundingRect()) - p.save() p.resetTransform() view = self.getViewBox() @@ -180,7 +183,7 @@ def paint(self, p, *args): vlines_it = zip(self.vlines, repeat(None)) for dx, text in vlines_it: - rx = view.mapViewToDevice(QtCore.QPointF(dx, 0)).x() + rx = int(view.mapViewToDevice(QtCore.QPointF(dx, 0)).x()) p.drawLine(rx, 0, rx, height) if text is not None: @@ -197,7 +200,7 @@ def paint(self, p, *args): hlines_it = zip(self.hlines, repeat(None)) for dy, text in hlines_it: - ry = view.mapViewToDevice(QtCore.QPointF(0, dy)).y() + ry = int(view.mapViewToDevice(QtCore.QPointF(0, dy)).y()) p.drawLine(0, ry, width, ry) if text is not None: @@ -222,7 +225,7 @@ def paint(self, p, *args): if text is not None: bl = rect.bottomLeft() p.drawText(p.boundingRect( - bl.x(), bl.y() + 1, 1, 1, flags, text + int(bl.x()), int(bl.y()) + 1, 1, 1, flags, text ), flags, text) if self.ellipses is not None: diff --git a/src/metro/devices/util/simulate/scalar_abstract.py b/src/metro/devices/util/simulate/scalar_abstract.py index 240dab0..18ac725 100644 --- a/src/metro/devices/util/simulate/scalar_abstract.py +++ b/src/metro/devices/util/simulate/scalar_abstract.py @@ -18,7 +18,7 @@ def prepare(self, args): self.offset = args['offset'] self.timer = metro.QTimer(self) - self.timer.setInterval(args['interval'] * 1000) + self.timer.setInterval(int(args['interval'] * 1000)) self.timer.timeout.connect(self.tick) def finalize(self): diff --git a/src/metro/devices/util/simulate/scalar_manual.py b/src/metro/devices/util/simulate/scalar_manual.py index 7d95682..ac39601 100644 --- a/src/metro/devices/util/simulate/scalar_manual.py +++ b/src/metro/devices/util/simulate/scalar_manual.py @@ -24,7 +24,7 @@ def prepare(self, args, state): self.offset = args['offset'] self.timer = metro.QTimer(self) - self.timer.setInterval(args['interval'] * 1000) + self.timer.setInterval(int(args['interval'] * 1000)) self.timer.timeout.connect(self.tick) self.measure_connect(self.measuringStarted, self.measuringStopped)