def __init__(self): super().__init__() self.setWindowTitle("Thread Example") form_layout = QVBoxLayout() self.setLayout(form_layout) self.resize(400, 800) self.button_start_threads = QPushButton() self.button_start_threads.clicked.connect(self.start_threads) self.button_start_threads.setText("Start {} threads".format(self.NUM_THREADS)) form_layout.addWidget(self.button_start_threads) self.button_stop_threads = QPushButton() self.button_stop_threads.clicked.connect(self.abort_workers) self.button_stop_threads.setText("Stop threads") self.button_stop_threads.setDisabled(True) form_layout.addWidget(self.button_stop_threads) self.log = QTextEdit() form_layout.addWidget(self.log) self.progress = QTextEdit() form_layout.addWidget(self.progress) QThread.currentThread().setObjectName('main') # threads can be named, useful for log output self.__workers_done = None self.__threads = None
def main_gui(): main_app = QApplication(sys.argv) QThread.currentThread().setPriority(QThread.HighPriority) main_app.setStyle('fusion') main_app.main_window = MainWindow() main_app.main_window.show() # main_app.controller = MainController(main_app) sys.exit(main_app.exec_())
def service_party_time_left(self): """ Update time left """ _service_name = 'Service::Timeleft' print('> {} is running...'.format(_service_name)) while self.services_are_running: try: for _name, _party in self.hosted_parties.items(): _state = _party[PartyKey.KEY_STATE] if _state == PartyState.STATE_WAITING_FOR_PLAYERS: if _party[PartyKey.KEY_TIME_LEFT] > 0: _party[PartyKey.KEY_TIME_LEFT] -= 1 else: print('> Party [{}] change state to [{}]'.format( _name, PartyState.STATE_PARTY_ENDED)) _party[PartyKey. KEY_STATE] = PartyState.STATE_PARTY_ENDED _party[PartyKey.KEY_TIME_LEFT] = 10 elif _state == PartyState.STATE_READY_TO_FIGHT: if _party[PartyKey.KEY_TIME_LEFT] > 0: _party[PartyKey.KEY_TIME_LEFT] -= 1 else: print('> Party [{}] change state to [{}]'.format( _name, PartyState.STATE_IN_PROGRESS)) _party[PartyKey. KEY_STATE] = PartyState.STATE_IN_PROGRESS _party[PartyKey.KEY_TIME_LEFT] = 0 elif _state == PartyState.STATE_IN_PROGRESS: if _party[PartyKey.KEY_TIME_LEFT] > 0: _party[PartyKey.KEY_TIME_LEFT] -= 1 else: print('> Party [{}] change state to [{}]'.format( _name, PartyState.STATE_PARTY_ENDED)) _party[PartyKey. KEY_STATE] = PartyState.STATE_PARTY_ENDED _party[PartyKey.KEY_TIME_LEFT] = 0 elif _state == PartyState.STATE_WINNER: if _party[PartyKey.KEY_TIME_LEFT] > 0: _party[PartyKey.KEY_TIME_LEFT] -= 1 else: print('> Party [{}] change state to [{}]'.format( _name, PartyState.STATE_PARTY_ENDED)) _party[PartyKey. KEY_STATE] = PartyState.STATE_PARTY_ENDED _party[PartyKey.KEY_TIME_LEFT] = 10 QThread.currentThread().msleep(SERVICE_TIME_LEFT_PERIOD_S * 1000) except Exception as e: pass print('> {} ended'.format(_service_name))
def service_debug(self): """ Debug display """ if not DEBUG_MODE: return _service_name = 'Service::Debug' print('> {} is running...'.format(_service_name)) while self.services_are_running: try: QThread.currentThread().msleep( int(DEBUG_DISPLAY_PERIOD_S * 1000)) except Exception as e: pass print('> {} ended'.format(_service_name))
def __init__(self, mainw): super().__init__() self.setupUi(mainw) self.CONFIG = '' self.template = '' self.xlsx_rows_list = '' self.parent = mainw self.pushButton_open_list_and_template.clicked.connect( self.open_xls_and_template) self.pushButton_ask_and_send.clicked.connect(self.send_msg) self.pushButton_cancel_send.clicked.connect(self.abort_workers) QThread.currentThread().setObjectName( 'main') # threads can be named, useful for log output self.__workers_done = None self.__threads = None
def run(self): self.manager.connection.core.ChangeWindowAttributesChecked( self.manager.screen.root, xcffib.xproto.CW.EventMask, [ xcffib.xproto.EventMask.PropertyChange | xcffib.xproto.EventMask.SubstructureNotify, ], ).check() current_thread = QThread.currentThread() while True: if current_thread.isInterruptionRequested(): return evt = self.manager.connection.poll_for_event() if evt is None: QThread.msleep(10) continue for wanted_type, handler in self.handlers.items(): if isinstance(evt, wanted_type): try: handler(evt) except: self.logger.error( "Error handling event %s", repr(evt), exc_info=True )
def registerThread(self): """ This slot shall be called from each activated nexxT thread with a direct connection. :return: """ t = QThread.currentThread() logger.internal("registering thread %s", t.objectName()) with self._lockThreadSpecific: if not t in self._threadSpecificProfiling: self._threadSpecificProfiling[t] = ThreadSpecificProfItem() self._threadSpecificProfiling[t].timer = QTimer( parent=self.sender()) self._threadSpecificProfiling[t].timer.timeout.connect( self._generateRecord, Qt.DirectConnection) self._threadSpecificProfiling[t].timer.setInterval( int(ThreadSpecificProfItem.THREAD_PROFILING_PERIOD_SEC * 1e3)) self.stopTimers.connect( self._threadSpecificProfiling[t].timer.stop) self.startTimers.connect( self._threadSpecificProfiling[t].timer.start) if self._loadMonitoringEnabled: self._threadSpecificProfiling[t].timer.start() tmain = QCoreApplication.instance().thread() if self._mi is None and not tmain in self._threadSpecificProfiling: self._mi = MethodInvoker( dict(object=self, method="registerThread", thread=tmain), Qt.QueuedConnection)
def __init__(self): QObject.__init__(self, None) self._mainthread = QThread.currentThread() self._signalthread = SignalThread() QObject.connect(self._signalthread, SIGNAL("triggerSignal()"), self.sensorQueueChanged) self._idletimer = QTimer() self._delaytimer = QTimer() self._timerqueuetimer = QTimer() self._idletimer.setSingleShot(True) self._delaytimer.setSingleShot(True) self._timerqueuetimer.setSingleShot(True) self.connect(self._idletimer, SIGNAL("timeout()"), self.idleTimeout) self.connect(self._delaytimer, SIGNAL("timeout()"), self.delayTimeout) self.connect(self._timerqueuetimer, SIGNAL("timeout()"), self.timerQueueTimeout) SoDB.getSensorManager().setChangedCallback(self.sensorQueueChangedCB, self) SoDB.setRealTimeInterval(1.0 / 25.0) SoRenderManager.enableRealTimeUpdate(False)
def get_db(): name = "db-{0}".format(str(QThread.currentThread())) if QSqlDatabase.contains(name): return QSqlDatabase.database(name) else: db = QSqlDatabase.addDatabase("QSQLITE", name) db.setDatabaseName("video.db") return db
def _generateRecord(self): """ This slot is automaticall called periodically :return: """ t = QThread.currentThread() with self._lockThreadSpecific: self._threadSpecificProfiling[t].update() self._emitData()
def sensorQueueChangedCB(self, closure): # if we get a callback from another thread, route the callback # through SignalThread so that we receive the callback in the # QApplication thread (needed since QTimer isn't thread safe) if QThread.currentThread() != closure._mainthread: if not closure._signalthread.isRunning(): closure._signalthread.start() self._signalthread.trigger() else: self.sensorQueueChanged()
def transmit(self, dataSample): """ transmit a data sample over this port :param dataSample: sample to transmit """ if not QThread.currentThread() is self.thread(): raise NexTRuntimeError( "OutputPort.transmit has been called from an unexpected thread." ) self.transmitSample.emit(dataSample)
def _receiveSample(self, dataSample): assert QThread.currentThread() is self.thread() while True: if self._stopped: logger.info( "The inter-thread connection is set to stopped mode; data sample discarded." ) break if self.semaphore.tryAcquire(1, 500): self.transmitInterThread.emit(dataSample, self.semaphore) break
def _receiveSync(self, dataSample): """ Called from framework only and implements the synchronous receive mechanism. :param dataSample: the transmitted DataSample instance :return: None """ if not QThread.currentThread() is self.thread(): raise NexTInternalError( "InputPort.receiveSync has been called from an unexpected thread." ) self._addToQueue(dataSample) self._transmit()
def deregisterThread(self): """ This slot shall be called from each deactivated nexxT thread with a direct connection :return: """ self._mi = None t = QThread.currentThread() logger.debug("deregistering thread %s", t.objectName()) with self._lockThreadSpecific: if t in self._threadSpecificProfiling: self._threadSpecificProfiling[t].timer.stop() del self._threadSpecificProfiling[t] self.threadDeregistered.emit(t.objectName())
def test(self): worker_thread_receiver = Receiver() worker_thread_receiver.moveToThread(self._worker_thread) self._worker_thread.started.connect( worker_thread_receiver.slot_function) main_thread = QThread.currentThread() main_thread_receiver = Receiver() self._worker_thread.started.connect(main_thread_receiver.slot_function) self._timer.start() self.app.exec_() self.assertEqual(worker_thread_receiver.senderThread, self._worker_thread) self.assertEqual(main_thread_receiver.senderThread, main_thread)
def afterPortDataChanged(self, portname): """ This slot is called after calling onPortDataChanged. :param portname: the fully qualified name of the port :param timeNs: the timestamp :return: """ if not self._portProfilingEnabled: return t = QThread.currentThread() timeNs = time.perf_counter_ns() with self._lockThreadSpecific: if t in self._threadSpecificProfiling: self._threadSpecificProfiling[t].registerPortChangeFinished( portname, timeNs)
def __init__(self): """ Intended to be called by `initialise` only. Instantiates the singleton, and checks if we're on the main thread. If not, raises a RuntimeError. """ super().__init__() app = QtWidgets.QApplication.instance() if app is None: print( 'Running without QT event queue. Callbacks will occur on the caller\'s thread, and UI bindings will ' 'be ignored.') elif app.thread() != QThread.currentThread(): raise RuntimeError( 'QWidgetUpdaterFactory must be created on the main thread') else: self._sig_make_object.connect(self._create_QWidgetUpdater_slot) self._sig_execute_function.connect(self._execute_slot)
def _getDB(self): if self.threadSafety == self.SINGLE_CONNECTION: return self.dbConn # create a new connection for each thread with QMutexLocker(self.mutex): tid = QThread.currentThread() if not tid in self.dbs: # Our custom argument db = sqlite3.connect(self.filename) # might need to use self.filename if len(self.dbs) == 0: db.execute( "CREATE TABLE IF NOT EXISTS " "debug(date datetime, loggername text, filename, srclineno integer, " "func text, level text, msg text)") db.commit() self.dbs[tid] = db return self.dbs[tid]
def execute(cls, fn: Callable[[], None], blocking: bool = False) -> None: app = QtWidgets.QApplication.instance() if app is None or app.thread() == QThread.currentThread(): # If already on main thread then just execute directly. This check works even if standard python threads # (not QThreads) are used. Function called explicitly (instead of relying on direct QT signal) as the QT # event queue may not be available. fn() return # On non-main thread cls._executor_mutex.acquire( ) # Will be released when object is created (on main thread) cls._in_function = fn cls._instance._sig_execute_function.emit() # releases lock if blocking: cls._executor_mutex.acquire() cls._executor_mutex.release()
def test_abstract_build_runner(qtbot): main_thread = QThread.currentThread() class Worker(QObject): call_started = Signal() def __init__(self, *args): QObject.__init__(self) @Slot() def my_slot(self): assert main_thread != self.thread() self.call_started.emit() class BuildRunner(build_runner.AbstractBuildRunner): call_started = Signal() thread_finished = Signal() worker_class = Worker def init_worker(self, fetch_config, options): build_runner.AbstractBuildRunner.init_worker(self, fetch_config, options) self.thread.finished.connect(self.thread_finished) self.worker.call_started.connect(self.call_started) return self.worker.my_slot # instantiate the runner runner = BuildRunner(Mock(persist='.')) assert not runner.thread with qtbot.waitSignal(runner.thread_finished, raising=True): with qtbot.waitSignal(runner.call_started, raising=True): runner.start( create_config('firefox', 'linux', 64, 'x86_64'), {'addons': (), 'profile': '/path/to/profile', 'profile_persistence': 'clone'}, ) runner.stop(True) assert not runner.pending_threads
def create_QWidgetUpdater( cls, widget: QWidget, widget_setter: Callable[[T], None], model_getter: Callable[[], T]) -> Callable[[], None]: """ Creates a new QWidgetUpdater. If this is called from a secondary thread, the QWidgetUpdater is created on the main thread via signal. Parameters ---------- widget The widget to be updated, required so signals can be blocked during update widget_setter Function to be called on the widget with input from model_getter() model_getter Retrieves the value to be provided to widget_setter Returns QWidgetUpdater constructed on the main thread ------- """ app = QtWidgets.QApplication.instance() if app is None: # Headless mode; updater doesn't need to do anything. return lambda: None if app.thread() == QThread.currentThread(): # If already on main thread then just make function directly. This check works even if standard python # threads (not QThreads) are used. Function called explicitly (instead of relying on direct QT signal) as # the QT event queue may not be available. return QWidgetUpdater(widget, widget_setter, model_getter) # On non-main thread # Acquire lock and set input variables. Lock will be released & inputs will be cleared on main thread. cls._updater_mutex.acquire() cls._in_widget = widget cls._in_widget_setter = widget_setter cls._in_model_getter = model_getter cls._instance._sig_make_object.emit() # Block until main thread does it's job, and get output & rest output variable. cls._updater_mutex.acquire() out = cls._updater_out cls._updater_out = None cls._updater_mutex.release() return out
def work(self): """ Pretend this worker method does work that takes a long time. During this time, the thread's event loop is blocked, except if the application's processEvents() is called: this gives every thread (incl. main) a chance to process events, which in this sample means processing signals received from GUI (such as abort). """ thread_name = QThread.currentThread().objectName() self.sig_step.emit( self.__id, 'Running worker #{} from thread "{}"'.format( self.__id, thread_name)) while True: batch_sender_app.processEvents( ) # this could cause change to self.__abort if self.__abort: self.sig_step.emit( self.__id, 'Worker #{} aborting work'.format(self.__id)) break try: mail = self.envelope.take_next_mail() qt_mail_id, xls_mail_id, sent_to = mail['qt_id'], mail[ 'xls_id'], mail['to_addrs'] except StopIteration: break # Это — победа # Теперь пытаемся отправить полученное письмо try: self.envelope.send_next(mail) except Exception as e: self.sig_step.emit(self.__id, 'Worker #{} error: {}'.format(self.__id, e)) self.sig_mail_error.emit(qt_mail_id, str(e)) else: self.sig_step.emit( self.__id, 'Worker #{} sent to {}'.format(self.__id, sent_to)) self.sig_mail_sent.emit(qt_mail_id, xls_mail_id) self.sig_done.emit(self.__id)
def _receiveAsync(self, dataSample, semaphore): """ Called from framework only and implements the asynchronous receive mechanism using a semaphore. :param dataSample: the transmitted DataSample instance :param semaphore: a QSemaphore instance :return: None """ if not QThread.currentThread() is self.thread(): raise NexTInternalError( "InputPort.receiveAsync has been called from an unexpected thread." ) self._addToQueue(dataSample) if not self._interthreadDynamicQueue: # usual behaviour semaphore.release(1) self._transmit() else: if semaphore not in self._semaphoreN: self._semaphoreN[semaphore] = 1 delta = self._semaphoreN[semaphore] - len(self.queue) if delta <= 0: # the semaphore's N is too small semaphore.release(1 - delta) self._semaphoreN[semaphore] += -delta logger.internal("delta = %d: semaphoreN = %d", delta, self._semaphoreN[semaphore]) self._transmit() elif delta > 0: # first acquire is done by caller self._semaphoreN[semaphore] -= 1 # the semaphore's N is too large, try acquires to reduce the size for i in range(1, delta): if semaphore.tryAcquire(1): self._semaphoreN[semaphore] -= 1 else: break logger.internal("delta = %d: semaphoreN = %d", delta, self._semaphoreN[semaphore]) self._transmit()
def work(self): """ Pretend this worker method does work that takes a long time. During this time, the thread's event loop is blocked, except if the application's processEvents() is called: this gives every thread (incl. main) a chance to process events, which in this sample means processing signals received from GUI (such as abort). """ thread_name = QThread.currentThread().objectName() thread_id = int(QThread.currentThreadId()) # cast to int() is necessary self.sig_msg.emit('Running worker #{} from thread "{}" (#{})'.format(self.__id, thread_name, thread_id)) for step in range(100): time.sleep(0.1) self.sig_step.emit(self.__id, 'step ' + str(step)) # check if we need to abort the loop; need to process events to receive signals; app.processEvents() # this could cause change to self.__abort if self.__abort: # note that "step" value will not necessarily be same for every thread self.sig_msg.emit('Worker #{} aborting work at step {}'.format(self.__id, step)) break self.sig_done.emit(self.__id)
def getData(self, delaySamples=0, delaySeconds=None): """ Return a data sample stored in the queue (called by the filter). :param delaySamples: 0 related the most actual sample, numbers > 0 relates to historic samples (None can be given if delaySeconds is not None) :param delaySeconds: if not None, a delay of 0.0 is related to the current sample, positive numbers are related to historic samples (TODO specify the exact semantics of delaySeconds) :return: DataSample instance """ if not QThread.currentThread() is self.thread(): raise NexTRuntimeError( "InputPort.getData has been called from an unexpected thread.") if delaySamples is not None: assert delaySeconds is None return self.queue[delaySamples] if delaySeconds is not None: assert delaySamples is None delayTime = delaySeconds / DataSample.TIMESTAMP_RES i = 0 while i < len(self.queue) and self.queue[0].getTimestamp( ) - self.queue[i].getTimestamp() < delayTime: i += 1 return self.queue[i] raise RuntimeError("delaySamples and delaySeconds are both None.")
def service_party_states(self): """ Solve party states """ _service_name = 'Service::States' print('> {} is running...'.format(_service_name)) while self.services_are_running: try: for _name, _model in self.hosted_parties.items(): _state = _model[PartyKey.KEY_STATE] if _state == PartyState.STATE_WAITING_FOR_PLAYERS: if len(_model[PartyKey.KEY_PLAYERS]) >= _model[ PartyKey.KEY_LIMIT]: print('> Party [{}] change state to [{}]'.format( _name, PartyState.STATE_READY_TO_FIGHT)) self.startup_party_elements(_name) _model[PartyKey. KEY_STATE] = PartyState.STATE_READY_TO_FIGHT _model[PartyKey. KEY_TIME_LEFT] = PartyConst.TIME_MAX_READY elif _state == PartyState.STATE_READY_TO_FIGHT: if _model[PartyKey.KEY_TIME_LEFT] <= 0: print('> Party [{}] change state to [{}]'.format( _name, PartyState.STATE_IN_PROGRESS)) _model[PartyKey. KEY_STATE] = PartyState.STATE_IN_PROGRESS _model[PartyKey. KEY_TIME_LEFT] = PartyConst.TIME_MAX_GAME elif _state == PartyState.STATE_IN_PROGRESS: if sum([ int(_mob.is_alive) for _mob in _model[PartyKey.KEY_MOBS][ PartyKey.KEY_MOB_PLAYERS] ]) <= 1: print('> Party [{}] change state to [{}]'.format( _name, PartyState.STATE_WINNER)) for _mob in _model[PartyKey.KEY_MOBS][ PartyKey.KEY_MOB_PLAYERS]: if _mob.is_alive: _model[PartyKey.KEY_DEAD_POOL].append( _mob.name) break _model[ PartyKey.KEY_STATE] = PartyState.STATE_WINNER _model[PartyKey. KEY_TIME_LEFT] = PartyConst.TIME_MAX_WIN _has_winner = False for _mob in _model[PartyKey.KEY_MOBS][ PartyKey.KEY_MOB_PLAYERS]: if _mob.is_alive: _has_winner = True print( '> Party [{}] Winner is: [{}]'.format( _name, _mob.name)) break if not _has_winner: print('> Party [{}] NO WINNER !') _model[PartyKey.KEY_PREV_STATE] = _state # ***** Delete dead mobs _bolt_temp = list() for _bolt in _model[PartyKey.KEY_MOBS][ PartyKey.KEY_MOB_BOLTS]: if _bolt.is_alive: _bolt_temp.append(_bolt) _asteroid_temp = list() for _asteroid in _model[PartyKey.KEY_MOBS][ PartyKey.KEY_MOB_ASTEROIDS]: if not -PartyConst.WIDTH < _asteroid.x < PartyConst.WIDTH: continue if not -PartyConst.HEIGHT < _asteroid.y < PartyConst.HEIGHT: continue _asteroid_temp.append(_asteroid) _model[PartyKey.KEY_MOBS][ PartyKey.KEY_MOB_ASTEROIDS] = _asteroid_temp _model[PartyKey.KEY_MOBS][ PartyKey.KEY_MOB_BOLTS] = _bolt_temp # ***** Delete party if _state == PartyState.STATE_PARTY_ENDED: self.delete_party(_name) QThread.currentThread().msleep( int(SERVICE_STATE_PERIOD_S * 1000)) except Exception as e: print('> [ERR] {} > {}: {}'.format(_service_name, type(e).__name__, e)) print('> {} ended'.format(_service_name))
def service_party_collision(self): """ Solve collisions """ _service_name = 'Service::Collision' print('> {} is running... '.format(_service_name)) while self.services_are_running: _begin = time.time() try: for _name, _party in self.hosted_parties.items(): if _party[PartyKey.KEY_STATE] in [ PartyState.STATE_IN_PROGRESS, PartyState.STATE_WINNER ]: _all_mobs = _party[PartyKey.KEY_MOBS][ PartyKey. KEY_MOB_PLAYERS] + _party[PartyKey.KEY_MOBS][ PartyKey.KEY_MOB_ASTEROIDS] + _party[ PartyKey.KEY_MOBS][PartyKey.KEY_MOB_BOLTS] for _i, _mob in enumerate(_all_mobs): # Dead zone damage _dt_mob_from_dz_center = np.linalg.norm( _mob.xy - _party[PartyKey.KEY_DEAD_ZONE][ PartyKey.KEY_DEAD_ZONE_CENTER]) if _dt_mob_from_dz_center > _party[ PartyKey.KEY_DEAD_ZONE][ PartyKey.KEY_DEAD_ZONE_RADIUS]: if _party[ PartyKey. KEY_STATE] == PartyState.STATE_IN_PROGRESS: if _mob.is_alive: _mob.set_damage( PartyConst.DEAD_ZONE_DAMAGE_PER_S * SERVICE_REAL_TIME_PERIOD_S) if _mob.type == ModelType.SHIP_MODEL and not _mob.is_alive: _party[ PartyKey.KEY_DEAD_POOL].append( _mob.name) # Detect collision for _collider in _all_mobs[_i:]: if not _collider.name == _mob.name: _dist = np.linalg.norm(_collider.xy - _mob.xy) if _dist == 0: continue if _dist < (_collider.radius + _mob.radius) / 2: if ModelType.BOLT_MODEL not in [ _mob.type, _collider.type ]: # print('COLLISION:', _mob.name, 'with', _collider.name) # Set position offset after collision _diff = (_collider.radius + _mob.radius) / 2 - _dist # _diff += 5 _diff_ratio1 = _mob.weight / ( _mob.weight + _collider.weight) * _diff _diff_ratio2 = _collider.weight / ( _mob.weight + _collider.weight) * _diff _mob.set_position( _mob.xy + _diff_ratio2 * (_mob.xy - _collider.xy) / _dist) _collider.set_position( _collider.xy + _diff_ratio1 * (_collider.xy - _mob.xy) / _dist) # Set orientation offset after collision # -- Normal _normal = (_collider.xy - _mob.xy) / _dist # -- Tangente _tx = -_normal[1] _ty = _normal[0] # -- dot product tangent _dp_tan_1 = _mob.dir_x * _mob.get_propulsion_speed( ) * _tx + _mob.dir_y * _mob.get_propulsion_speed( ) * _ty _dp_tan_2 = _collider.dir_x * _collider.get_propulsion_speed( ) * _tx + _collider.dir_y * _collider.get_propulsion_speed( ) * _ty # -- dot product normal _dp_normal_mob = _mob.dir_x * _mob.get_propulsion_speed( ) * _normal[ 0] + _mob.dir_y * _mob.get_propulsion_speed( ) * _normal[1] _dp_normal_collider = _collider.dir_x * _collider.get_propulsion_speed( ) * _normal[ 0] + _collider.dir_y * _collider.get_propulsion_speed( ) * _normal[1] # -- conservation of momentum _m1 = (_dp_normal_mob * (_mob.weight - _collider.weight) + 2 * _collider.weight * _dp_normal_collider) / ( _mob.weight + _collider.weight) _m2 = (_dp_normal_collider * (_collider.weight - _mob.weight) + 2 * _mob.weight * _dp_normal_mob ) / (_mob.weight + _collider.weight) _speed1 = np.array([ _tx * _dp_tan_1 + _normal[0] * _m1, _ty * _dp_tan_1 + _normal[1] * _m1 ]) _speed2 = np.array([ _tx * _dp_tan_2 + _normal[0] * _m2, _ty * _dp_tan_2 + _normal[1] * _m2 ]) _prop1 = np.linalg.norm(_speed1) _prop2 = np.linalg.norm(_speed2) _dir1 = _speed1 / _prop1 _mob.set_dir(_dir1) _mob.set_speed( np.array( [_prop1, _mob.speed[1]])) _dir2 = _speed2 / _prop2 _collider.set_dir(_dir2) _collider.set_speed( np.array([ _prop2, _collider.speed[1] ])) # SET DAMAGE if _mob.is_alive: _mob.set_damage(_collider.damage) if _mob.type == ModelType.SHIP_MODEL and not _mob.is_alive: _party[PartyKey. KEY_DEAD_POOL].append( _mob.name) if _collider.is_alive: _collider.set_damage(_mob.damage) if _collider.type == ModelType.SHIP_MODEL and not _collider.is_alive: _party[PartyKey. KEY_DEAD_POOL].append( _collider.name) except Exception as e: print('> [ERR] {} | {}:{}'.format(_service_name, type(e).__name__, e)) traceback.print_exc() _end = time.time() _run_time = SERVICE_REAL_TIME_PERIOD_S - (_end - _begin) if _run_time > 0: QThread.currentThread().msleep(int(_run_time * 1000)) print('> {} ended'.format(_service_name))