Skip to content

Commit

Permalink
Merge pull request #76 from ericpre/fix_rangeslider_issue
Browse files Browse the repository at this point in the history
Fix slider in image contrast editor on python >=3.10
  • Loading branch information
ericpre authored May 8, 2024
2 parents 7b70620 + 96fae29 commit 9e90eef
Show file tree
Hide file tree
Showing 7 changed files with 443 additions and 25 deletions.
1 change: 1 addition & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ on:
- '**/*.md'
- '**/*.rst'
- '**/*.txt'
- 'hyperspy_gui_traitsui/_external/*.py'
schedule:
# run once a week
- cron: '16 13 * * 5'
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ jobs:
# correct version with setuptools_scm
run: |
git remote add upstream https://github.com/hyperspy/hyperspy_gui_traitsui.git
git fetch --prune --unshallow
git fetch upstream --tags
- uses: actions/setup-python@v5
Expand Down Expand Up @@ -76,12 +75,16 @@ jobs:
- name: Install HyperSpy (RELEASE_next_major)
if: contains( matrix.LABEL, 'RnM')
run: |
pip install git+https://github.com/hyperspy/hyperspy.git@RELEASE_next_major
# pip install git+https://github.com/hyperspy/hyperspy.git@RELEASE_next_major
# revert back when hyperspy 2.1 is released
pip install git+https://github.com/hyperspy/hyperspy.git@RELEASE_next_minor
- name: Install HyperSpy (RELEASE_next_patch)
if: contains( matrix.LABEL, 'RnP')
run: |
pip install git+https://github.com/hyperspy/hyperspy.git@RELEASE_next_patch
# pip install git+https://github.com/hyperspy/hyperspy.git@RELEASE_next_patch
# revert back when hyperspy 2.1 is released
pip install git+https://github.com/hyperspy/hyperspy.git@RELEASE_next_minor
- name: Install exSpy
if: ${{ ! contains( matrix.LABEL, 'minimum') }}
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

## v2.1 (UNRELEASED)

* Fix slider in image contrast editor on python >=3.10 ([#76](https://github.com/hyperspy/hyperspy_gui_traitsui/pull/76)).
* Fix getting version with editable installation ([#75](https://github.com/hyperspy/hyperspy_gui_traitsui/pull/75)).
* Add releasing guide and release script ([#75](https://github.com/hyperspy/hyperspy_gui_traitsui/pull/75)).
* Fix regression with editable installation ([#74](https://github.com/hyperspy/hyperspy_gui_traitsui/pull/74)).
Expand Down
211 changes: 211 additions & 0 deletions hyperspy_gui_traitsui/_external/bounds_editor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
# (C) Copyright 2004-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

# Use upstream version when https://github.com/enthought/traitsui/pull/2048
# is merged and release

from pyface.qt import QtGui, QtCore

from traits.api import Float, Any, Str, Union

from traitsui.editors.api import RangeEditor
try:
from traitsui.qt.editor import Editor
except:
from traitsui.qt4.editor import Editor
from .range_slider import RangeSlider


class _BoundsEditor(Editor):

evaluate = Any()

min = Any()
max = Any()
low = Any()
high = Any()
format_str = Str()

def init(self, parent):
"""Finishes initializing the editor by creating the underlying toolkit
widget.
"""
factory = self.factory
if not factory.low_name:
self.low = factory.low

if not factory.high_name:
self.high = factory.high

self.max = factory.max
self.min = factory.min

self.format_str = factory.format_str

self.evaluate = factory.evaluate
self.sync_value(factory.evaluate_name, "evaluate", "from")

self.sync_value(factory.low_name, "low", "both")
self.sync_value(factory.high_name, "high", "both")

self.control = QtGui.QWidget()
panel = QtGui.QHBoxLayout(self.control)
panel.setContentsMargins(0, 0, 0, 0)

self._label_lo = QtGui.QLineEdit(self.format_str % self.low)
self._label_lo.editingFinished.connect(self.update_low_on_enter)
panel.addWidget(self._label_lo)

# The default size is a bit too big and probably doesn't need to grow.
sh = self._label_lo.sizeHint()
sh.setWidth(sh.width() // 2)
self._label_lo.setMaximumSize(sh)

self.control.slider = slider = RangeSlider(QtCore.Qt.Orientation.Horizontal)
slider.setTracking(factory.auto_set)
slider.setMinimum(0)
slider.setMaximum(10000)
slider.setPageStep(1000)
slider.setSingleStep(100)
slider.setLow(self._convert_to_slider(self.low))
slider.setHigh(self._convert_to_slider(self.high))

slider.sliderMoved.connect(self.update_object_on_scroll)
panel.addWidget(slider)

self._label_hi = QtGui.QLineEdit(self.format_str % self.high)
self._label_hi.editingFinished.connect(self.update_high_on_enter)
panel.addWidget(self._label_hi)

# The default size is a bit too big and probably doesn't need to grow.
sh = self._label_hi.sizeHint()
sh.setWidth(sh.width() // 2)
self._label_hi.setMaximumSize(sh)

self.set_tooltip(slider)
self.set_tooltip(self._label_lo)
self.set_tooltip(self._label_hi)

def update_low_on_enter(self):
try:
try:
low = eval(str(self._label_lo.text()).strip())
if self.evaluate is not None:
low = self.evaluate(low)
except Exception as ex:
low = self.low
self._label_lo.setText(self.format_str % self.low)

if not self.factory.is_float:
low = int(low)

if low > self.high:
low = self.high - self._step_size()
self._label_lo.setText(self.format_str % low)

self.control.slider.setLow(self._convert_to_slider(low))
self.low = low
except:
pass

def update_high_on_enter(self):
try:
try:
high = eval(str(self._label_hi.text()).strip())
if self.evaluate is not None:
high = self.evaluate(high)
except:
high = self.high
self._label_hi.setText(self.format_str % self.high)

if not self.factory.is_float:
high = int(high)

if high < self.low:
high = self.low + self._step_size()
self._label_hi.setText(self.format_str % high)

self.control.slider.setHigh(self._convert_to_slider(high))
self.high = high
except:
pass

def update_object_on_scroll(self, pos):
low = self._convert_from_slider(self.control.slider.low())
high = self._convert_from_slider(self.control.slider.high())

if self.factory.is_float:
self.low = low
self.high = high
else:
self.low = int(low)
self.high = int(high)

# update the sliders to the int values or the sliders
# will jiggle
self.control.slider.setLow(self._convert_to_slider(low))
self.control.slider.setHigh(self._convert_to_slider(high))

def update_editor(self):
return

def _check_max_and_min(self):
# check if max & min have been defined:
if self.max is None:
self.max = self.high
if self.min is None:
self.min = self.low

def _step_size(self):
slider_delta = (
self.control.slider.maximum() - self.control.slider.minimum()
)
range_delta = self.max - self.min

return float(range_delta) / slider_delta

def _convert_from_slider(self, slider_val):
self._check_max_and_min()
return self.min + slider_val * self._step_size()

def _convert_to_slider(self, value):
self._check_max_and_min()
return (
self.control.slider.minimum()
+ (value - self.min) / self._step_size()
)

def _low_changed(self, low):
if self.control is None:
return
if self._label_lo is not None:
self._label_lo.setText(self.format_str % low)

self.control.slider.setLow(self._convert_to_slider(low))

def _high_changed(self, high):
if self.control is None:
return
if self._label_hi is not None:
self._label_hi.setText(self.format_str % high)

self.control.slider.setHigh(self._convert_to_slider(self.high))


class BoundsEditor(RangeEditor):

min = Union(None, Float)
max = Union(None, Float)

def _get_simple_editor_class(self):
return _BoundsEditor

def _get_custom_editor_class(self):
return _BoundsEditor
Loading

0 comments on commit 9e90eef

Please sign in to comment.