Skip to content

Commit c3ed0ee

Browse files
committed
Rework profile manager threaded execution to avoid seg fault with
pyside6.7
1 parent 1191989 commit c3ed0ee

1 file changed

Lines changed: 32 additions & 74 deletions

File tree

src/silx/gui/plot/tools/profile/manager.py

Lines changed: 32 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from silx.utils.weakref import WeakMethodProxy
3939
from silx.gui import icons
4040
from silx.gui.plot import PlotWidget
41+
from silx.gui.plot.tools.profile.core import ProfileRoiMixIn
4142
from silx.gui.plot.tools.roi import RegionOfInterestManager
4243
from silx.gui.plot.tools.roi import CreateRoiModeAction
4344
from silx.gui.plot import items
@@ -54,75 +55,41 @@
5455
class _RunnableComputeProfile(qt.QRunnable):
5556
"""Runner to process profiles
5657
57-
:param qt.QThreadPool threadPool: The thread which will be used to
58-
execute this runner. It is used to update the used signals
59-
:param ~silx.gui.plot.items.Item item: Item in which the profile is
60-
computed
61-
:param ~silx.gui.plot.tools.profile.core.ProfileRoiMixIn roi: ROI
62-
defining the profile shape and other characteristics
58+
:param item: Item in which the profile is computed
59+
:param roi: ROI defining the profile shape and other characteristics
6360
"""
6461

65-
class _Signals(qt.QObject):
62+
class Signal(qt.QObject):
6663
"""Signal holder"""
67-
68-
resultReady = qt.Signal(object, object)
6964
runnerFinished = qt.Signal(object)
7065

71-
def __init__(self, threadPool, item, roi):
72-
"""Constructor"""
66+
def __init__(self, item: items.Item, roi: ProfileRoiMixIn):
7367
super(_RunnableComputeProfile, self).__init__()
74-
self._signals = self._Signals()
75-
self._signals.moveToThread(threadPool.thread())
68+
self.setAutoDelete(False)
69+
self._signal = self.Signal()
7670
self._item = item
7771
self._roi = roi
78-
self._cancelled = False
79-
80-
def _lazyCancel(self):
81-
"""Cancel the runner if it is not yet started.
82-
83-
The threadpool will still execute the runner, but this will process
84-
nothing.
85-
86-
This is only used with Qt<5.9 where QThreadPool.tryTake is not available.
87-
"""
88-
self._cancelled = True
89-
90-
def autoDelete(self):
91-
return False
92-
93-
def getRoi(self):
94-
"""Returns the ROI in which the runner will compute a profile.
95-
96-
:rtype: ~silx.gui.plot.tools.profile.core.ProfileRoiMixIn
97-
"""
98-
return self._roi
99-
100-
@property
101-
def resultReady(self):
102-
"""Signal emitted when the result of the computation is available.
103-
104-
This signal provides 2 values: The ROI, and the computation result.
105-
"""
106-
return self._signals.resultReady
72+
self._profileData = None
10773

10874
@property
10975
def runnerFinished(self):
110-
"""Signal emitted when runner have finished.
76+
return self._signal.runnerFinished
11177

112-
This signal provides a single value: the runner itself.
113-
"""
114-
return self._signals.runnerFinished
78+
def getRoi(self) -> ProfileRoiMixIn:
79+
"""Returns the ROI in which the runner will compute a profile."""
80+
return self._roi
81+
82+
def getProfileData(self):
83+
"""Returns result data or None if failure or not finished"""
84+
return self._profileData
11585

116-
def run(self):
86+
def run(self) -> None:
11787
"""Process the profile computation."""
118-
if not self._cancelled:
119-
try:
120-
profileData = self._roi.computeProfile(self._item)
121-
except Exception:
122-
_logger.error("Error while computing profile", exc_info=True)
123-
else:
124-
self.resultReady.emit(self._roi, profileData)
125-
self.runnerFinished.emit(self)
88+
try:
89+
self._profileData = self._roi.computeProfile(self._item)
90+
except Exception:
91+
_logger.error("Error while computing profile", exc_info=True)
92+
self._signal.runnerFinished.emit(self)
12693

12794

12895
class ProfileWindow(qt.QMainWindow):
@@ -857,12 +824,8 @@ def requestUpdateProfile(self, profileRoi):
857824
if not inspect.isValid(runner):
858825
self._pendingRunners.remove(runner)
859826
continue
860-
if runner.getRoi() is profileRoi:
861-
if hasattr(threadPool, "tryTake"):
862-
if threadPool.tryTake(runner):
863-
self._pendingRunners.remove(runner)
864-
else: # Support Qt<5.9
865-
runner._lazyCancel()
827+
if runner.getRoi() is profileRoi and threadPool.tryTake(runner):
828+
self._pendingRunners.remove(runner)
866829

867830
item = self.getPlotItem()
868831
if item is None or not isinstance(item, profileRoi.ITEM_KIND):
@@ -874,26 +837,21 @@ def requestUpdateProfile(self, profileRoi):
874837
return
875838

876839
profileRoi._setPlotItem(item)
877-
runner = _RunnableComputeProfile(threadPool, item, profileRoi)
878-
runner.runnerFinished.connect(self.__cleanUpRunner)
879-
runner.resultReady.connect(self.__displayResult)
840+
runner = _RunnableComputeProfile(item, profileRoi)
841+
runner.runnerFinished.connect(self.__runnerFinished)
880842
self._pendingRunners.append(runner)
881843
threadPool.start(runner)
882844

883-
def __cleanUpRunner(self, runner):
884-
"""Remove a thread pool runner from the list of hold tasks.
885-
886-
Called at the termination of the runner.
887-
"""
845+
def __runnerFinished(self, runner: _RunnableComputeProfile):
846+
"""Clean-up profile runner and display its result"""
888847
if runner in self._pendingRunners:
889848
self._pendingRunners.remove(runner)
890849

891-
def __displayResult(self, roi, profileData):
892-
"""Display the result of a ROI.
850+
roi = runner.getRoi()
851+
profileData = runner.getProfileData()
852+
if profileData is None:
853+
return
893854

894-
:param ~core.ProfileRoiMixIn profileRoi: A managed ROI
895-
:param ~core.CurveProfileData profileData: Computed data profile
896-
"""
897855
if roi in self.__reentrantResults:
898856
# Store the data to process it in the main loop
899857
# And not a sub loop created by initProfileWindow

0 commit comments

Comments
 (0)