Source code for gui.drawlock

##
##
##  SPDX-FileCopyrightText: © 2007-2023 Benedict Verhegghe <bverheg@gmail.com>
##  SPDX-License-Identifier: GPL-3.0-or-later
##
##  This file is part of pyFormex 3.4  (Thu Nov 16 18:07:39 CET 2023)
##  pyFormex is a tool for generating, manipulating and transforming 3D
##  geometrical models by sequences of mathematical operations.
##  Home page: https://pyformex.org
##  Project page: https://savannah.nongnu.org/projects/pyformex/
##  Development: https://gitlab.com/bverheg/pyformex
##  Distributed under the GNU General Public License version 3 or later.
##
##  This program is free software: you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation, either version 3 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program.  If not, see http://www.gnu.org/licenses/.
##
"""A locking mechanism for the drawing functions.

"""
import pyformex as pf
import threading
import time

# TODO: WE SHOULD REIMPLEMENT THIS USING PYTHON threading.Condition?

[docs]class DrawLock(): """A timed lock to slow down drawing processes""" def __init__(self): self.allowed = True self.locked = False self.timer = None
[docs] def wait(self): """Wait for the drawing lock to be released. This method can be called to wait until the lock is released, while still processing GUI events. """ if self.allowed: while self.locked: pf.canvas.update() pf.app.processEvents() time.sleep(0.01) # to avoid overusing the cpu
[docs] def lock(self, time=None): """Lock the drawing function for the next time seconds. If a no time is specified, a global value is used. """ if self.allowed and not self.locked: if time is None: time = pf.GUI.drawwait if time > 0.: pf.debug('STARTING TIMER', pf.DEBUG.SCRIPT) self.locked = True self.timer = threading.Timer(time, self.release) self.timer.start()
# ?? SHOULD block and release only be activated if self.allowed ??
[docs] def block(self): """Lock the drawing function indefinitely.""" if self.timer: self.timer.cancel() self.locked = True
[docs] def release(self): """Release the lock on the drawing function. If a timer is running, cancel it. """ self.locked = False if self.timer: self.timer.cancel()
[docs] def free(self): """Release the lock and prevent waits until allow() is called.""" self.allowed = False self.release()
[docs] def allow(self): """Allow draw waits. This is called after a free() to reinstall the draw locking. """ self.allowed = True
[docs]class Repeater(): """Repeatedly execute a function. The Repeater class provides functionality to repeatedly execute a function, while allowing the GUI to process events so that user interactivity can continue. It also avoids using too much CPU time while running empty. The function can be repeated until one of the following conditions is met: - the called function returns a value that evaluates to True - a specified time has elapsed - a number of executions has been reached - and external event stops the execution Parameters: - `func`: if callable, this function will be called repeatedly why the Repeater class is active. The function will be passed all the extra parameters `*args` and `**kargs`. If the function returns a value that does not evaluate to False, execution is halted. - `duration`: max duration for the repeated execution. If < 0, repeats indefinitely. - `maxcount`: max number of executions. If < 0, there is no limit. - `sleep`: extra time to sleep between two executions. The actual time between two executions may be higher, because any GUI events will also be executed. If your function does not do anything, setting a value > 0 is recommended to avoid high CPU usage while running idle. Execution is started by calling the start() method. The method returns after some event has made it to stop, with an exitcode of: - 1, if the stop() method was used - 2, if a timeout occurred - 3, if the maximum number of excutions occurred, - or else, with the value returned by the function. """ def __init__(self, func, duration=-1, maxcount=-1, sleep=0): """Create a new repeater""" pf.debug("REPEAT: %s, %s" % (duration, maxcount), pf.DEBUG.SCRIPT) self.exitcode = False self.func = func self.duration = duration self.maxcount = maxcount self.sleep = sleep
[docs] def start(self, *args, **kargs): """Start repeated execution""" timer = None self.exitcode = 0 if self.duration >= 0: timer = threading.Timer(self.duration, self.timeOut) timer.start() count = 0 while self.exitcode == 0: pf.debug("Loop Exitcode %s, Count: %s" % (self.exitcode, count), pf.DEBUG.SCRIPT) pf.app.processEvents() if callable(self.func): # DO NOT USE self.exitcode HERE # the timeOut might have passed while in the func exitcode = self.func(*args, **kargs) if exitcode: self.exitcode = exitcode break count += 1 if self.maxcount >= 0 and count >= self.maxcount: self.exitcode = 3 break if self.sleep > 0: time.sleep(self.sleep) pf.debug("Exit Repeater with Exitcode %s, Count: %s" % (self.exitcode, count), pf.DEBUG.SCRIPT)
[docs] def timeOut(self): """Stop the repeater because of a timeout""" self.exitcode = 2
[docs] def stop(self, exitcode=1): """Interrupt a repeater with given exitcode""" self.exitcode = exitcode
#### End