Exemplo n.º 1
0
class DeusExSpawner:
    def __init__(self, scene, config, interval, action, locations):
        self.scene = scene
        self.config = config
        self.interval = interval
        self.action = action
        self.locations = locations
        self.deusExTypesList = [DeusExTypes.POSITIVE, DeusExTypes.NEGATIVE]
        self.positivePulseSound = oalOpen(self.config.sounds["nondangerZone"])
        self.negativePulseSound = oalOpen(self.config.sounds["dangerZone"])
        self.positiveEndingSound = oalOpen(
            self.config.sounds["nondangerZoneEnd"])
        self.negativeEndingSound = oalOpen(self.config.sounds["dangerZoneEnd"])
        self.spawnTimer = QTimer()
        self.spawnTimer.setTimerType(Qt.PreciseTimer)
        self.spawnTimer.timeout.connect(self.spawn)
        self.spawnTimer.setInterval(self.interval)
        self.spawnTimer.start()

    def spawn(self, isPositive=False):
        deusExType = None
        deusEx = None
        if isPositive:
            deusExType = DeusExTypes.POSITIVE
            deusEx = DeusEx(self.config, deusExType, self.positivePulseSound,
                            self.positiveEndingSound)
        else:
            deusExType = DeusExTypes.NEGATIVE
            deusEx = DeusEx(self.config, deusExType, self.negativePulseSound,
                            self.negativeEndingSound)
        deusEx.deusExActivateSignal.connect(self.action)
        deusEx.setZValue(2)
        deusEx.setPos(random.choice(self.locations))
        self.scene.addItem(deusEx)
Exemplo n.º 2
0
class BaseMeter(Tool):
    def __init__(self, engine):
        super().__init__(engine)

        self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
        self.setupUi(self)

        self._timer = QTimer()
        self._timer.setInterval(1000)
        self._timer.setTimerType(Qt.PreciseTimer)
        self._timer.timeout.connect(self.on_timer)
        self._timer.start()

        self.restore_state()
        self.finished.connect(self.save_state)

        self.chars = []
        engine.signal_connect("translated", self.on_translation)

    def on_translation(self, old, new):
        for action in old:
            remove = len(action.text)
            if remove > 0:
                self.chars = self.chars[:-remove]
            self.chars += _timestamp_items(action.replace)

        for action in new:
            remove = len(action.replace)
            if remove > 0:
                self.chars = self.chars[:-remove]
            self.chars += _timestamp_items(action.text)

    def on_timer(self):
        raise NotImplementedError()
Exemplo n.º 3
0
class VideoCapture(QWidget):
    def __init__(self, filename, parent):
        super(QWidget, self).__init__()
        self.cap = cv2.VideoCapture(str(filename[0]))
        self.length = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
        self.frame_rate = self.cap.get(cv2.CAP_PROP_FPS)
        #self.codec = self.cap.get(cv2.CAP_PROP_FOURCC)
        self.video_frame = QLabel()
        parent.layout.addWidget(self.video_frame)

    def nextFrameSlot(self):
        ret, frame = self.cap.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        img = QImage(frame, frame.shape[1], frame.shape[0],
                     QImage.Format_RGB888)
        pix = QPixmap.fromImage(img)
        self.video_frame.setPixmap(pix)

    def start(self):
        self.timer = QTimer()
        self.timer.setTimerType(Qt.PreciseTimer)
        self.timer.timeout.connect(self.nextFrameSlot)
        self.timer.start(1000.0 / self.frame_rate)

    def pause(self):
        self.timer.stop()

    def deleteLater(self):
        self.cap.release()
        super(QWidget, self).deleteLater()
Exemplo n.º 4
0
class MediaListPlayer(_ListPlayer):
    """WIP to provide most vlc.MediaPlayer functionality in the one class with the
    playlist.
    """

    newframe = pyqtSignal()
    slider_precision = 100

    def __init__(self, viewpoint_mngr, loop_mode_mngr, media_player):
        super().__init__(
            viewpoint_mngr=viewpoint_mngr,
            loop_mode_mngr=loop_mode_mngr,
            media_player=media_player,
        )
        self.timer = QTimer()
        self.timer.setTimerType(Qt.CoarseTimer)
        self.timer.timeout.connect(self.on_timeout)

        self.mp.playing.connect(self.timer.start)
        self.mp.stopped.connect(self.timer.stop)
        self.mp.paused.connect(self.timer.stop)

        self.mediachanged.connect(self.on_mediachanged)

    def on_timeout(self):
        self.newframe.emit()

    @pyqtSlot(MediaItem)
    def on_mediachanged(self, media_item: MediaItem):
        media_info = media_item.info()
        media_fps = media_info["avg_frame_rate"]
        self.timer.setInterval(media_fps)
Exemplo n.º 5
0
class FiringNotifier(QObject):
    firingSignal = pyqtSignal(int)

    def __init__(self, timerInterval):
        super().__init__()
        self.keys = []
        # will be false when the player announces he can't shoot
        # the board sets this flag according to player.announceCanShoot function
        self.canEmit = True

        self.thread = QThread()
        self.timerInterval = timerInterval
        self.emitTimer = QTimer()
        self.emitTimer.setTimerType(Qt.PreciseTimer)
        self.emitTimer.timeout.connect(self.emit)
        self.emitTimer.setInterval(self.timerInterval)
        self.moveToThread(self.thread)
        self.thread.started.connect(self.emitTimer.start)
        self.thread.start()

    def add_key(self, key):
        self.keys.append(key)

    def remove_key(self, key):
        if key in self.keys:
            self.keys.remove(key)

    def emit(self):
        if self.canEmit:
            for k in self.keys:
                self.firingSignal.emit(k)
Exemplo n.º 6
0
class Video(QObject):
    finish = pyqtSignal()
    t_changed = pyqtSignal(int)

    def __init__(self, clip):
        super(Video, self).__init__()
        self.clip = clip
        self.t = 0
        self.stride = 1 / self.clip.fps

    def work(self):
        self.timer = QTimer()
        self.timer.setTimerType(Qt.PreciseTimer)
        self.timer.timeout.connect(ui.openGLWindow.update)
        self.timer.timeout.connect(self.updatetime)
        self.timer.timeout.connect(ui.audio.update)
        self.timer.setInterval(1000 / self.clip.fps)
        self.timer.start()

    def updatetime(self):
        #print(threading.current_thread())
        if self.t < self.clip.duration:
            im = self.clip.get_frame(self.t)
            print(self.t)
            img = QImage(im.data, im.shape[1], im.shape[0], im.shape[1] * 3,
                         QImage.Format_RGB888)
            q.put(img)
            self.setT(self.t + self.stride)

        else:
            self.stop()
            self.finish.emit()

    def pause(self):
        self.timer.stop()

    def resume(self):
        self.timer.start(1000 / self.clip.fps)

    def speed(self):
        self.clip = speedx.speedx(self.clip, 2)

    def stop(self):
        self.setT(0)

        q.queue.clear()
        self.timer.stop()

    def tiktok(self):
        self.clip = self.clip.fl_image(tiktok_effect)

    def setClip(self, clip):
        self.clip.close()
        self.clip = clip
        self.setT(0)
        self.stride = 1 / self.clip.fps

    def setT(self, t):
        self.t = t
        self.t_changed.emit(self.t * 10)
Exemplo n.º 7
0
class LineEdit(QLineEdit):
    def __init__(self, parent=None):
        super(LineEdit, self).__init__(parent)
        self.text_focus = False
        # Clicking automatically selects all text, this allows clicks and drag
        # to highlight part of a url better
        self.clicklength = QTimer()
        self.clicklength.setSingleShot(True)
        self.clicklength.setTimerType(Qt.PreciseTimer)

    def mousePressEvent(self, e):
        if not self.text_focus:
            self.clicklength.start(120)
            self.text_focus = True
        else:
            super(LineEdit, self).mousePressEvent(e)

    def mouseReleaseEvent(self, e):
        if self.clicklength.isActive():
            self.selectAll()
        super(LineEdit, self).mouseReleaseEvent(e)

    def focusOutEvent(self, e):
        super(LineEdit, self).focusOutEvent(e)
        self.text_focus = False
Exemplo n.º 8
0
 def playerShield(self, pw=None):
     pw.player.isShielded = True
     pw.player.shieldTimer.start()
     playerShieldTimer = QTimer()
     playerShieldTimer.setTimerType(Qt.PreciseTimer)
     playerShieldTimer.setInterval(15000)
     playerShieldTimer.timeout.connect(lambda: self.afterPlayerShield(pw))
     playerShieldTimer.start()
     setattr(self, f"playerShieldTimer{pw.player.id}", playerShieldTimer)
Exemplo n.º 9
0
 def playerCantMove(self, pw):
     pw.firingNotifier.emitTimer.stop()
     pw.movementNotifier.emitTimer.stop()
     playerCantMoveTimer = QTimer()
     playerCantMoveTimer.setTimerType(Qt.PreciseTimer)
     playerCantMoveTimer.timeout.connect(
         lambda: self.afterPlayerCantMove(pw))
     playerCantMoveTimer.setInterval(10000)
     playerCantMoveTimer.start()
     setattr(self, f"playerCantMoveTimer{pw.player.id}",
             playerCantMoveTimer)
Exemplo n.º 10
0
def threaded_cooldown(func):
    """ A decorator that makes it so the decorate function will run
     in a thread, but prevents the same function from being rerun for a given time.
     After give time, the last call not performed will be executed.

     Purpose of this is to ensure writing to disc does not happen all too often,
     avoid IO operations reducing GUI smoothness.

     A drawback is that if a user "queues" a save, but reloads the file before the last save,
     they will load a version that is not up to date. This is not a problem for Grabber, as the
     settings are only read on startup. However, it's a drawback that prevents a more general use.

     This decorator requires being used in an instance which has a threadpool instance.
     """

    timer = QTimer()
    timer.setInterval(5000)
    timer.setSingleShot(True)
    timer.setTimerType(Qt.VeryCoarseTimer)

    def wrapper(self, *args, **kwargs):

        if not hasattr(self, 'threadpool'):
            raise AttributeError(
                f'{self.__class__.__name__} instance does not have a threadpool attribute.'
            )

        if not hasattr(self, 'force_save'):
            raise AttributeError(
                f'{self.__class__.__name__} instance does not have a force_save attribute.'
            )

        worker = Task(func, self, *args, **kwargs)

        if timer.receivers(timer.timeout):
            timer.disconnect()

        if self.force_save:
            timer.stop()
            self.threadpool.start(worker)
            self.threadpool.waitForDone()
            return

        if timer.isActive():
            timer.timeout.connect(partial(self.threadpool.start, worker))
            timer.start()
            return

        timer.start()
        self.threadpool.start(worker)
        return

    return wrapper
Exemplo n.º 11
0
class PloverWpmMeter(Tool, Ui_WpmMeter):
    TITLE = "WPM Meter"
    ROLE = "wpm_meter"

    _TIMEOUTS = {
        "wpm1": 10,
        "wpm2": 60,
    }

    def __init__(self, engine):
        super().__init__(engine)
        self.setupUi(self)

        self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
        self.wpm_method.addItem("NCRA", "ncra")
        self.wpm_method.addItem("Traditional", "traditional")

        self._timer = QTimer()
        self._timer.setInterval(1000)
        self._timer.setTimerType(Qt.PreciseTimer)
        self._timer.timeout.connect(self._update_wpms)
        self._timer.start()

        self._chars = []
        engine.signal_connect("translated", self._on_translation)

        self.restore_state()
        self.finished.connect(self.save_state)

    def _on_translation(self, old, new):
        for action in old:
            remove = len(action.text)
            if remove > 0:
                self._chars = self._chars[:-remove]
            self._chars += _timestamp_chars(action.replace)

        for action in new:
            remove = len(action.replace)
            if remove > 0:
                self._chars = self._chars[:-remove]
            self._chars += _timestamp_chars(action.text)

    @pyqtSlot()
    def _update_wpms(self):
        max_timeout = max(self._TIMEOUTS.values())
        self._chars = _filter_old_chars(self._chars, max_timeout)
        for name, timeout in self._TIMEOUTS.items():
            chars = _filter_old_chars(self._chars, timeout)
            wpm = _wpm_of_chars(chars, method=self.wpm_method.currentData())
            getattr(self, name).display(str(wpm))
Exemplo n.º 12
0
class MovementSoundHandler:
    def __init__(self, config):
        self.config = config
        self.movementSignals = []
        # # moving sound
        self.movementSound = oalOpen(self.config.sounds["tankMoving"])
        self.movementSound.set_looping(True)
        self.movementSound.set_gain(30.0)
        # not moving sound
        self.nonMovementSound = oalOpen(self.config.sounds["tankNotMoving"])
        self.nonMovementSound.set_looping(True)
        self.nonMovementSound.set_gain(30.0)

        self.soundTimer = QTimer()
        self.soundTimer.setTimerType(Qt.PreciseTimer)
        self.soundTimer.timeout.connect(self.stopMovementSound)
        self.soundTimer.setInterval(100)

    def playMovementSound(self):
        if self.nonMovementSound is not None and self.movementSound is not None:
            if self.nonMovementSound.get_state() == AL_PLAYING:
                self.nonMovementSound.pause()
            if self.movementSound.get_state() == AL_PAUSED or \
                    self.movementSound.get_state() == AL_STOPPED or \
                    self.movementSound.get_state() == AL_INITIAL:
                self.movementSound.play()
        self.soundTimer.start()

    def stopMovementSound(self):
        if self.nonMovementSound is not None and self.movementSound is not None:
            if self.movementSound.get_state() == AL_PLAYING:
                self.movementSound.pause()
            if self.nonMovementSound.get_state() == AL_PAUSED or \
                    self.nonMovementSound.get_state() == AL_STOPPED or \
                    self.nonMovementSound.get_state() == AL_INITIAL:
                self.nonMovementSound.play()
                self.soundTimer.stop()

    def activate(self):
        self.nonMovementSound.play()
        self.soundTimer.start()

    def deactivate(self):
        self.movementSound.stop()
        self.nonMovementSound.stop()
        self.soundTimer.stop()
Exemplo n.º 13
0
class BaseMeter(Tool):
    def __init__(self, engine):
        super().__init__(engine)

        self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
        self.setupUi(self)

        self.is_pinned_checkbox.stateChanged.connect(self.set_is_pinned)

        self._timer = QTimer()
        self._timer.setInterval(1000)
        self._timer.setTimerType(Qt.PreciseTimer)
        self._timer.timeout.connect(self.on_timer)
        self._timer.start()

        self.restore_state()
        self.finished.connect(self.save_state)

        self.chars = []
        engine.signal_connect("translated", self.on_translation)

    def on_translation(self, old, new):
        output = CaptureOutput(self.chars)
        output_helper = OutputHelper(output, False, False)
        output_helper.render(None, old, new)

    def on_timer(self):
        raise NotImplementedError()

    def _save_state(self, settings):
        settings.setValue("is_pinned", self.is_pinned_checkbox.isChecked())

    def _restore_state(self, settings):
        is_pinned = settings.value("is_pinned", True, bool)
        self.is_pinned_checkbox.setChecked(is_pinned)

    def set_is_pinned(self):
        is_pinned = self.is_pinned_checkbox.isChecked()
        if is_pinned:
            self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
        else:
            self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
        self.show()
Exemplo n.º 14
0
class AutoReconnectSocket(ClientSocketBase):

    ConnectedState = QtNetwork.QAbstractSocket.ConnectedState
    ConnectingState = QtNetwork.QAbstractSocket.ConnectingState
    UnconnectedState = QtNetwork.QAbstractSocket.UnconnectedState

    def __init__(self):
        ClientSocketBase.__init__(self)
        self.KeepAliveOption = True
        self.LowDelayOption = True

        self.__connection_expected = False

        self.qurl = QUrl()

        # Timer for connection attempts
        self.connect_timer = QTimer()
        self.connect_timer.setTimerType(Qt.VeryCoarseTimer)

        self.stateChanged.connect(self._on_state_changed)

    def _attempt_open(self):
        self.connect_timer.singleShot(0, lambda: self.open(self.qurl))
        log.info(f"SOCKET OPEN ATTEMPT qurl={self.qurl}")

    def _on_state_changed(self, state: QtNetwork.QAbstractSocket.SocketState):
        log.info(f"SOCKET STATE CHANGED state={self.state_str()} qurl={self.qurl}")
        if state == QtNetwork.QAbstractSocket.UnconnectedState:
            if self.__connection_expected:
                self._attempt_open()
        elif state == QtNetwork.QAbstractSocket.ConnectedState:
            self.__connection_expected = True

    def connect(self, url):
        self.qurl = QUrl(url)
        self._attempt_open()

    def disconnect(self):
        self.__connection_expected = False
        self.connect_timer.stop()
        self.close()
Exemplo n.º 15
0
class MediaPlayerCustomSignals(MediaPlayerVlclibSignals):
    newframe = pyqtSignal()
    slider_precision = 100

    def __init__(self, vlc_media_player):
        super().__init__(vlc_media_player=vlc_media_player)
        self.timer = QTimer()
        self.timer.setTimerType(Qt.CoarseTimer)
        self.timer.timeout.connect(self.on_timeout)

        # self.playing.connect(self.timer.start)
        # self.stopped.connect(self.timer.stop)
        # self.paused.connect(self.timer.stop)

        # self.mediachanged.connect(self.on_mediachanged)

    def on_timeout(self):
        self.newframe.emit()

    def has_media(self):
        return True if self._vlc_obj.get_media() else False

    def on_mediachanged(self, e):
        media = self.get_media()
        if not media.is_parsed():
            media.parse()
        media_fps = self._get_media_fps(media)
        playback_fps = media_fps * self.get_rate()
        self.timer.setInterval(self.slider_precision / playback_fps)

    def _get_media_fps(self, vlc_media) -> float:
        if not vlc_media:
            return 30
        if not vlc_media.is_parsed():
            vlc_media.parse()
        tracks = vlc_media.tracks_get()
        if not tracks:
            return 30
        track = [t for t in tracks if t is not None][0]
        return track.video.contents.frame_rate_num
Exemplo n.º 16
0
class LineEdit(QLineEdit):
    def __init__(self, parent=None):
        super(LineEdit, self).__init__(parent)
        self.text_focus = False
        self.clicklength = QTimer()
        self.clicklength.setSingleShot(True)
        self.clicklength.setTimerType(Qt.PreciseTimer)

    def mousePressEvent(self, e):
        if not self.text_focus:
            self.clicklength.start(120)
            self.text_focus = True
        else:
            super(LineEdit, self).mousePressEvent(e)

    def mouseReleaseEvent(self, e):
        if self.clicklength.isActive():
            self.selectAll()
        super(LineEdit, self).mouseReleaseEvent(e)

    def focusOutEvent(self, e):
        super(LineEdit, self).focusOutEvent(e)
        self.text_focus = False
Exemplo n.º 17
0
class MovementNotifier(QObject):
    movementSignal = pyqtSignal(int)

    def __init__(self, timerInterval):
        super().__init__()
        self.keys = []
        self.isEmitting = False
        self.timerInterval = timerInterval

        self.thread = QThread()
        self.emitTimer = QTimer()
        self.emitTimer.setTimerType(Qt.PreciseTimer)
        self.emitTimer.timeout.connect(self.emit)
        self.moveToThread(self.thread)
        self.emitTimer.setInterval(self.timerInterval)
        self.thread.started.connect(self.emitTimer.start)
        self.thread.start()

    def add_key(self, key):
        self.keys.append(key)

    def remove_key(self, key):
        if key in self.keys:
            self.isEmitting = False
            self.keys.remove(key)

    def emit(self):
        keys = self.keys[:]
        if keys:
            # mainKey will be the first key that was registered
            # if any other key was pressed they will be omitted
            # with that, moving only in one direction at a time will be possible
            # NOTE: when holding a key (although add_key is called on the board, only one element will be
            # in the self.keys list)
            mainKey = keys[0]
            self.isEmitting = True
            self.movementSignal.emit(mainKey)
class SENSOR_TEST(QWidget):
    
    bars = None
    series = None
    chart = None
    labelWidgets = []
    comms = None
    commsStatus = False
    deviceID = "HARP"

    def __init__(self):
        QWidget.__init__(self)

        # ADD GUI WIDGETS
        self.setupLayout()

        # LAUNCH GUI
        self.show()
        self.resize(800,600)

    def setupLayout(self):
        parentLayout = QVBoxLayout()
        parentLayout.setContentsMargins(20,20,20,20)

        buttonLayout = QHBoxLayout()

        # CREATE SERIAL COMMUNICATION CONNECT BUTTON
        self.connectButton = QPushButton("CONNECT")
        self.connectButton.setFixedSize(150, 40)
        self.connectButton.setCheckable(True)
        self.connectButton.setStyleSheet("QPushButton {background-color: #66BB6A; color: black; border-radius: 20px}" 
                                    "QPushButton:hover {background-color: #4CAF50; color: black; border-radius: 20px}"
                                    "QPushButton:checked {background-color: #EF5350; color: white; border-radius: 20px}"
                                    "QPushButton:checked:hover {background-color: #F44336; color: white; border-radius: 20px}")

        # CREATE SENSORS CALIBRATION BUTTON
        self.calibrateButton = QPushButton("CALIBRATE")
        self.calibrateButton.setFixedSize(150, 40)
        self.calibrateButton.setStyleSheet("QPushButton {background-color: #E0E0E0; color: black; border-radius: 20px}" 
                                    "QPushButton:hover {background-color: #BDBDBD; color: black; border-radius: 20px}" 
                                    "QPushButton:pressed {background-color: #D5D5D5; color: black; border-radius: 20px}")

        # ADD BUTTONS TO HORIZONTAL LAYOUT
        buttonLayout.addWidget(self.connectButton, alignment = Qt.AlignLeft)
        buttonLayout.addWidget(self.calibrateButton, alignment = Qt.AlignRight)
        
        # CREATE BAR CHART TO DISPLAY SENSOR READINGS
        self.bars = QBarSet('Sensor Readings')
        self.bars.append([0 for i in range(5)])
        self.chart = QChart()
        self.chart.setBackgroundRoundness(20)
        self.chart.layout().setContentsMargins(0, 0, 0, 0)
        self.series = QBarSeries()
        self.series.append(self.bars)
        self.chart.addSeries(self.series)
        self.chart.setTitle('Sensor Readings')

        # CREATE X-AXIS AS SENSOR LABELS
        xAxis = QBarCategoryAxis()
        labels = ["Sensor {}".format(i+1) for i in range(5)]
        xAxis.append(labels)
        
        # CREATE Y-AXIS AND SENSOR READINGS
        yAxis = QValueAxis()
        yAxis.setRange(-100, 100)
        yAxis.setTickCount(11)
        yAxis.setTitleText("Pressure (%)")
        
        # ADD AXES TO CHART
        self.chart.addAxis(xAxis, Qt.AlignBottom)
        self.chart.addAxis(yAxis, Qt.AlignLeft)
        self.chart.legend().setVisible(False)

        # ATTACH AXES TO SERIES
        self.series.attachAxis(xAxis)
        self.series.attachAxis(yAxis)
        
        # ADD CHART TO A VIEW
        chartView = QChartView(self.chart)

        labelLayout = QFrame()
        labelLayout.setStyleSheet("background-color: white; border-radius: 20px")
        layout1 = QVBoxLayout()
        labelLayout.setLayout(layout1)
        layout2 = QHBoxLayout()
        layout3 = QHBoxLayout()
        
        for i in range(5):
            label = QLabel("Sensor {}".format(i+1))
            label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
            label.setStyleSheet("font-weight: bold;")
            layout2.addWidget(label)

            value = QLabel("0 mV")
            value.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
            self.labelWidgets.append(value)
            layout3.addWidget(value)

        layout1.addLayout(layout2, 1)
        layout1.addLayout(layout3, 1)

        parentLayout.addLayout(buttonLayout)
        parentLayout.addWidget(chartView, 5)
        parentLayout.addWidget(labelLayout, 1)

        # LINK WIDGETS
        self.connectButton.clicked.connect(self.serialToggle)
        self.calibrateButton.clicked.connect(self.sensorCalibrate)

        self.setLayout(parentLayout)

    def getSensorReadings(self):
        """
        PURPOSE

        Requests sensor readings from ROV and updates GUI.

        INPUT

        NONE

        RETURNS

        NONE
        """
        # SENSOR POLLING RATE (HZ)
        refreshRate = 100

        # START QTIMER TO REPEATEDLY UPDATE SENSORS AT THE DESIRED POLLING RATE 
        self.timer = QTimer()
        self.timer.setTimerType(Qt.PreciseTimer)
        self.timer.timeout.connect(self.getSensorReadings)
        self.timer.start(int(1000*1/refreshRate))
        
        # STOP REQUESTING SENSOR VALUES IF ROV IS DISCONNECTED
        if self.commsStatus == False:
            self.timer.stop()
        
        else:
            # REQEST SINGLE READING
            sensorReadings = self.getSensors()

            # UPDATE VOLTAGE READINGS
            self.updateVoltageReadings(sensorReadings)

            # SCALE SENSOR READINGS
            scaledReadings = self.mapPressureReadings(sensorReadings)
            
            try:
                # UPDATE GUI 
                self.series.remove(self.bars)
                self.bars = QBarSet("")
                self.bars.append([float(item) for item in scaledReadings])
                self.series.append(self.bars)
            except:
                self.teensyDisconnect()

    def mapPressureReadings(self, values):
        mappedValues = []

        for value in values:
            try:
                mappedValues.append((200/1023)*float(value) - 100)
            except:
                pass

        return mappedValues

    def updateVoltageReadings(self, values):
        voltageValues = [round((3300/1023)*float(i)) for i in values]
        for i, label in enumerate(self.labelWidgets):
            label.setText(str(voltageValues[i]) + " mV")

    def serialToggle(self, buttonState):
        """
        PURPOSE

        Determines whether to connect or disconnect from the ROV serial interface.

        INPUT

        - buttonState = the state of the button (checked or unchecked).

        RETURNS

        NONE
        """
        # CONNECT
        if buttonState:
            self.teensyConnect()

        # DISCONNECT
        else:
            self.teensyDisconnect()

    def sensorCalibrate(self):
        """
        PURPOSE

        INPUT

        RETURNS

        """
        self.calibrateSensors()

    ########################
    ### SERIAL FUNCTIONS ###
    ########################
    def teensyConnect(self):
        """
        PURPOSE

        Attempts to connect to the ROV using the comms library.
        Changes the appearance of the connect buttons.
        If connection is successful, the ROV startup procedure is initiated.

        INPUT

        NONE

        RETURNS

        NONE
        """
        # DISABLE BUTTONS TO AVOID DOUBLE CLICKS
        self.connectButton.setEnabled(False)
            
        # FIND ALL AVAILABLE COM PORTS
        availableComPorts, comPort, identity = self.findComPorts(115200, self.deviceID)
        print(availableComPorts, comPort, identity)
        
        # ATTEMPT CONNECTION TO ROV COM PORT
        status, message = self.serialConnect(comPort, 115200)
        print(status, message)

        # IF CONNECTION IS SUCCESSFUL
        if status == True:
            # MODIFY BUTTON STYLE
            self.connectButton.setText('DISCONNECT')

            # START READING SENSOR VALUES
            self.getSensorReadings()
        
        # IF CONNECTION IS UNSUCCESSFUL
        else:
            self.teensyDisconnect()
            
        # RE-ENABLE CONNECT BUTTONS
        self.connectButton.setEnabled(True)

    def teensyDisconnect(self):
        """
        PURPOSE

        Disconnects from the ROV using the comms library.
        Changes the appearance of the connect buttons

        INPUT

        NONE

        RETURNS

        NONE
        """
        # MODIFY BUTTON STYLE
        self.connectButton.setText('CONNECT')
        self.connectButton.setChecked(False)
        
        # CLOSE COM PORT
        if self.commsStatus:
            self.comms.close()
            self.commsStatus = False

    def findComPorts(self, baudRate, identity):
        """
        PURPOSE

        Find all available COM ports and requests the devices identity.

        INPUT

        - menuObject = pointer to the drop down menu to display the available COM ports.
        - baudRate = baud rate of the serial interface.
        - identity = string containing the required device identity to connect to.

        RETURNS

        - availableComPorts = list of all the available COM ports.
        - rovComPort = the COM port that belongs to the device.
        - identity = the devices response from an identity request.
        """
        # CREATE LIST OF ALL POSSIBLE COM PORTS
        ports = ['COM%s' % (i + 1) for i in range(256)] 

        deviceIdentity = ""
        comPort = None
        availableComPorts = []
        
        # CHECK WHICH COM PORTS ARE AVAILABLE
        for port in ports:
            try:
                comms = serial.Serial(port, baudRate, timeout = 1)
                availableComPorts.append(port)

                # REQUEST IDENTITY FROM COM PORT
                self.commsStatus = True
                deviceIdentity = self.getIdentity(comms, identity)
                comms.close()
                self.commsStatus = False
                
                # FIND WHICH COM PORT IS THE ROV
                if deviceIdentity == identity:
                    comPort = port
                    break
                    
            # SKIP COM PORT IF UNAVAILABLE
            except (OSError, serial.SerialException):
                pass

        return availableComPorts, comPort, deviceIdentity

    def getIdentity(self, serialInterface, identity):
        """
        PURPOSE

        Request identity from a defined COM port.

        INPUT

        - serialInterface = pointer to the serial interface object.
        - identity = the desired identity response from the device connected to the COM port.

        RETURNS

        - identity = the devices response.
        """
        identity = ""
        startTime = datetime.now()
        elapsedTime = 0
        # REPEATIDELY REQUEST IDENTIFICATION FROM DEVICE FOR UP TO 3 SECONDS
        while (identity == "") and (elapsedTime < 3):
            self.serialSend("?I", serialInterface)
            identity = self.serialReceive(serialInterface)
            elapsedTime = (datetime.now() - startTime).total_seconds()

        return identity

    def serialConnect(self, comPort, baudRate):
        """
        PURPOSE

        Attempts to initialise a serial communication interface with a desired COM port.

        INPUT

        - rovComPort = the COM port of the ROV.
        - baudRate = the baud rate of the serial interface.

        RETURNS

        NONE
        """
        self.commsStatus = False
        if comPort != None:
            try: 
                self.comms = serial.Serial(comPort, baudRate, timeout = 1)
                message = "Connection to ROV successful."
                self.commsStatus = True
            except:
                message = "Failed to connect to {}.".format(comPort)
        else:
            message = "Failed to recognise device identity."

        return self.commsStatus, message

    def serialSend(self, command, serialInterface):
        """
        PURPOSE

        Sends a string down the serial interface to the ROV.

        INPUT

        - command = the command to send.
        - serialInterface = pointer to the serial interface object.

        RETURNS

        NONE
        """
        if self.commsStatus:
            try:
                serialInterface.write((command + '\n').encode('ascii'))
            except:
                self.teensyDisconnect()
                print("Failed to send command.")

    def serialReceive(self, serialInterface):
        """
        PURPOSE

        Waits for data until a newline character is received.

        INPUT

        - serialInterface = pointer to the serial interface object.

        RETURNS

        NONE
        """
        received = ""
        try:
            received = serialInterface.readline().decode('ascii').strip()
        except:
            self.teensyDisconnect()
            print("Failed to receive data.")
            
        return(received)

    def getSensors(self):
        """
        PURPOSE

        Send request to device to return sensor readings.
        
        INPUT

        NONE

        RETURNS

        - results = array containing the sensor readings.
        """
        results = []

        # REQUEST SENSOR READINGS
        command = "?S"
        self.serialSend(command, self.comms)
        
        # READ RESPONSE INTO AN ARRAY
        results = self.serialReceive(self.comms).split(",")
        print(results)
        return results

    def calibrateSensors(self):
        """
        """
        # REQUEST SENSOR READINGS
        command = "?C"
        self.serialSend(command, self.comms)
Exemplo n.º 19
0
class ServiceManager(QThread):
    start_job_signal = pyqtSignal(object)
    abort_running_job_signal = pyqtSignal()
    force_psd_creation_signal = pyqtSignal()
    job_widget_signal = pyqtSignal(object)
    tcp_respond_signal = pyqtSignal(object)

    # LED signals
    response_led_start = pyqtSignal()
    response_led_stop = pyqtSignal()
    request_led_signal = pyqtSignal()

    # Green LED alive timer, signal the user we are alive
    alive_led_signal = pyqtSignal()

    job_active = False
    pickle_cache = b''
    transfer_cache = b''

    def __init__(self, control_app, app, ui, logging_queue):
        super(ServiceManager, self).__init__()
        # global LOGGER
        # LOGGER = setup_queued_logger(__name__, logging_queue)

        self.control_app, self.app, self.ui = control_app, app, ui

        self.job_working_queue = list()
        self.job_queue = list()
        self.empty_job = Job(_('Kein Job'), '', get_user_directory(),
                             'mayaSoftware')
        self.current_job = self.empty_job

        # Control app signals
        self.start_job_signal.connect(self.control_app.add_render_job)
        self.job_widget_signal.connect(self.control_app.update_job_widget)
        self.abort_running_job_signal.connect(
            self.control_app.abort_running_job)
        self.force_psd_creation_signal.connect(
            self.control_app.watcher_force_psd_creation)

        # Run service manager socket server
        self.server = None
        self.address = ('', 0)
        self.hostname = ''

        # Timer's must be created inside thread event loop, I guess...
        self.alive_led_timer = None
        self.validate_queue_timer = None

    def response_start_led(self):
        self.response_led_start.emit()

    def response_stop_led(self):
        self.response_led_stop.emit()

    def alive_led_blink(self):
        self.alive_led_signal.emit()

    def run(self):
        # Run service manager socket server
        self.address = (get_valid_network_address(),
                        SocketAddress.service_port)
        sig_dest = (self.receive_server_msg, self.response_start_led,
                    self.response_stop_led)
        self.server = rsm_server(sig_dest, self.address)

        # Setup queue validation
        self.validate_queue_timer = QTimer()
        self.validate_queue_timer.setTimerType(Qt.VeryCoarseTimer)
        self.validate_queue_timer.setInterval(300000)
        self.validate_queue_timer.timeout.connect(self.validate_queue)
        self.validate_queue_timer.start()

        # Connect Service Manager Server LED's
        self.response_led_start.connect(self.control_app.led_socket_recv_start)
        self.response_led_stop.connect(self.control_app.led_socket_recv_end)
        self.response_led_stop.connect(self.control_app.led_socket_send_end)
        self.request_led_signal.connect(self.control_app.led_socket_send_start)
        self.alive_led_signal.connect(self.control_app.alive_led_blink)

        # Setup alive LED
        self.alive_led_timer = QTimer()
        self.alive_led_timer.setInterval(1500)
        self.alive_led_timer.timeout.connect(self.alive_led_blink)
        self.alive_led_timer.start()

        try:
            self.hostname = socket.gethostbyaddr(self.address[0])[0]
        except Exception as e:
            LOGGER.error('%s', e)

        LOGGER.info('Service manager available at %s - %s', self.address[0],
                    self.hostname)

        # Clean local work directory
        try:
            MoveJobSceneFile.clear_local_work_dir()
        except Exception as e:
            LOGGER.error('Error cleaning local work directory: %s', e)
        LOGGER.info('Service manager cleaned up local work directory.')

        # Run thread event loop
        self.exec()

        LOGGER.info(
            'Service manager received exit signal and is shutting down.')

        self.server.shutdown()
        self.server.server_close()

        LOGGER.info('Service manager Socket server shut down.')

    def validate_queue(self):
        """ Test if job items have expired """
        for job in self.job_queue:
            if job.status < JobStatus.finished:
                # Skip unfinished or queued jobs
                continue

            created = datetime.fromtimestamp(job.created)
            if (datetime.now() - created) > timedelta(hours=24):
                self._clear_local_job_file(job)
                self.job_queue.remove(job)

        # Update Remote Index
        for idx, job in enumerate(self.job_queue):
            job.remote_index = idx

    def prepare_queue_transfer(self):
        """ Transfer the job queue to the client as serialized json dictonary """
        if not self.transfer_cache:
            # Create serialized queue byte encoded
            serialized_queue = self.serialize_queue(self.job_queue)
            serialized_queue = serialized_queue.encode(encoding='utf-8')
            # Cache the result
            self.cache_transfer_queue(serialized_queue)
        else:
            serialized_queue = self.transfer_cache

        response = serialized_queue + JOB_DATA_EOS

        if self.is_queue_finished():
            # All Jobs finished, tell clients to stop query's
            response = serialized_queue + b'Queue-Finished' + JOB_DATA_EOS
            LOGGER.debug(
                'Service Manager adding Queue finished data to job transfer queue.'
            )

        return response

    @staticmethod
    def serialize_queue(queue):
        """ Return the queued Job class instances as serialized json dictonary """
        job_dict = dict()

        for idx, job in enumerate(queue):
            # Update Remote Index
            job.remote_index = idx

            # Update Job object
            job_dict.update({idx: job.__dict__})

        return json.dumps(job_dict)

    def cache_transfer_queue(self, serialized_queue):
        LOGGER.debug('Caching serialized job data queue in transfer cache.')
        self.transfer_cache = serialized_queue

    def invalidate_transfer_cache(self):
        if self.transfer_cache:
            LOGGER.debug('Invalidating transfer cache.')
            self.transfer_cache = b''

    def start_job_file_transfer(self, job: Job):
        LOGGER.debug('Starting Job File Transfer for %s', job.title)
        file_transfer = JobFileTransfer(self, self._file_transfer_finished,
                                        job)
        file_transfer.start()

    @pyqtSlot(Job)
    def _file_transfer_finished(self, job: Job):
        self.replace_job_in_queue(job, self.job_queue)
        self.job_working_queue.append(job)

        LOGGER.debug('Finished Job File Transfer for %s', job.title)
        self.start_job()

    def job_finished(self):
        """ Called from app if last job finished """
        self.job_active = False
        self.start_job()

    def start_job(self):
        """ Start the next job in the queue if no job is running """
        if not self.job_working_queue:
            return

        if not self.job_active:
            self.current_job = self.job_working_queue.pop(0)

            self.current_job.render_dir = create_unique_render_path(
                self.current_job.file, self.current_job.render_dir)

            self.start_job_signal.emit(copy_job(self.current_job))
            self.job_active = True

    def add_job(self, job_data, client: str = None):
        if type(job_data) is str:
            # Remove trailing semicolon
            if job_data.endswith(';'):
                job_data = job_data[:-1]
            # Convert to tuple
            job_data = tuple(job_data.split(';'))

        if not len(job_data) > 2:
            return False

        try:
            job_item = Job(*job_data)
        except Exception as e:
            LOGGER.error('Error creating job from socket stream: %s %s',
                         job_data, e)
            return False

        if not job_item.file or not job_item.render_dir:
            return False

        if client:
            job_item.client = client

        if not os.path.exists(job_item.file):
            return False
        if not os.path.exists(job_item.render_dir):
            return False

        self.invalidate_transfer_cache()

        job_item.remote_index = len(self.job_queue)
        self.job_queue.append(job_item)
        self.job_widget_signal.emit(job_item)
        self.start_job_file_transfer(job_item)

        return True

    def cancel_job(self, job):
        if job.in_progress:
            LOGGER.info('Aborting currently running Job.')
            self.abort_running_job_signal.emit()

        job.set_canceled()

        # Remove from working queue
        if job in self.job_working_queue:
            self.job_working_queue.remove(job)

        self.invalidate_transfer_cache()

    def move_job(self, job, to_top=True):
        self.update_queue_order(job, self.job_working_queue, to_top)
        new_idx = self.update_queue_order(job, self.job_queue, to_top)

        if new_idx:
            job.remote_index = new_idx

        # Find job in progress and move it to top
        job_in_progress = None
        for __j in self.job_queue:
            if __j.in_progress:
                job_in_progress = __j
                break

        if job_in_progress:
            self.update_queue_order(job_in_progress, self.job_queue, True)

        self.update_control_app_job_widget()

        self.invalidate_transfer_cache()

    def update_control_app_job_widget(self):
        # Clear job widget
        self.job_widget_signal.emit(None)

        # Re-create job manager widget
        for __j in self.job_queue:
            self.job_widget_signal.emit(__j)

    def set_job_failed(self):
        self.current_job.set_failed()
        self._clear_local_job_file(self.current_job)
        self.invalidate_transfer_cache()

    def set_job_canceled(self):
        self.current_job.set_canceled()
        self._clear_local_job_file(self.current_job)
        self.invalidate_transfer_cache()

    def set_job_finished(self):
        self.current_job.set_finished()
        self._clear_local_job_file(self.current_job)
        self.invalidate_transfer_cache()

    def set_job_status(self, status: int):
        self.current_job.status = status
        self.invalidate_transfer_cache()

    def set_job_status_name(self, status_name):
        self.current_job.status_name = status_name
        self.invalidate_transfer_cache()

    def set_job_img_num(self, img_num: int = 0, total_img_num: int = 0):
        if total_img_num:
            self.current_job.total_img_num = total_img_num
        if img_num:
            self.current_job.img_num = img_num
        self.invalidate_transfer_cache()

    @staticmethod
    def replace_job_in_queue(job_item, job_queue) -> bool:
        if job_item.remote_index > len(job_queue):
            return False

        job_queue[job_item.remote_index] = job_item
        return True

    @staticmethod
    def update_queue_order(job_item, list_queue, to_top):
        new_idx = None

        if to_top:
            insert_idx = 0
        else:
            insert_idx = len(list_queue)

        if job_item in list_queue:
            list_queue.remove(job_item)
            list_queue.insert(insert_idx, job_item)
            new_idx = list_queue.index(job_item)

        return new_idx

    @staticmethod
    def _clear_local_job_file(job):
        if not job.scene_file_is_local:
            return

        MoveJobSceneFile.delete_local_scene_files(job.file)

    def is_queue_finished(self):
        """ Return True if all jobs in the queue are finished """
        finished = False
        for job in self.job_queue:
            if job.status < JobStatus.finished:
                break
        else:
            # No unfinished jobs in the queue, mark queue finished
            if len(self.job_queue):
                # Only mark finished if there are actually jobs in the queue
                finished = True

        return finished

    def is_file_in_transfer(self):
        for job in self.job_queue:
            if job.status == JobStatus.file_transfer:
                return True

        return False

    def get_job_from_index(self, idx):
        job = None

        if len(self.job_queue) > idx:
            job = self.job_queue[idx]

        return job

    def receive_server_msg(self, msg, client_name=None, tcp_handler=None):
        """ Receive client requests from socket server and respond accordingly """
        self.request_led_signal.emit()
        if client_name:
            if msg != 'GET_JOB_DATA':
                LOGGER.debug('Service Manager received: "%s" from client %s',
                             msg, client_name)
        else:
            LOGGER.debug('Service manager received: %s', msg)

        response = 'Unknown command or Job you referred to is no longer in the queue.'

        # ----------- CLIENT CONNECTED ------------
        if msg.startswith('GREETING'):
            try:
                version = int(msg[-1:])
            except ValueError:
                version = 0

            if version and version > 2:
                LOGGER.info('Client with version %s connected.', version)
                response = _('Render Dienst verfuegbar @ {} '
                             'Maya Version: {}').format(
                                 self.hostname,
                                 self.ui.comboBox_version.currentText())
            else:
                LOGGER.info('Invalid Client version connected!')
                response = _(
                    'Render Dienst verfuegbar @ {}<br>'
                    '<span style="color:red;">Die Client Version wird nicht unterstuetzt! '
                    'Client Aktualisierung erforderlich!</span>').format(
                        self.hostname)

        # ----------- TRANSFER RENDERER ------------
        elif msg == 'GET_RENDERER':
            response = 'RENDERER '

            # Convert list to ; separated string
            for __r in AVAILABLE_RENDERER:
                response += __r + ';'

            # Remove trailing semicolon
            response = response[:-1]

        # ----------- ADD REMOTE JOB ------------
        elif msg.startswith('ADD_JOB'):
            job_string_data = msg[len('ADD_JOB '):]
            msg_job_idx = len(self.job_queue)
            result = self.add_job(job_string_data, client_name)

            if result:
                response = _('Job #{0:02d} eingereiht in laufende Jobs.'
                             ).format(msg_job_idx)
            else:
                response = _(
                    '<b>Job abgelehnt! </b><span style="color:red;">'
                    'Die Szenendatei oder das Ausgabeverzeichnis sind für den Server nicht verfügbar!</span>'
                )

        # ----------- SEND JOB STATUS MESSAGE ------------
        elif msg == 'GET_STATUS':
            response = _('Momentan im Rendervorgang: '
                         '{0} - {1:03d} / {2:03d} Layer erzeugt.<br/>'
                         '{3:02d} Jobs in der Warteschlange.').format(
                             self.control_app.current_job.title,
                             self.control_app.current_job.img_num,
                             self.control_app.current_job.total_img_num,
                             len(self.job_working_queue))

        # ----------- TRANSFER JOB QUEUE ------------
        elif msg == 'GET_JOB_DATA':
            # Send the queue as serialized JSON
            response = self.prepare_queue_transfer()

        # ----------- MOVE JOB ------------
        elif msg.startswith('MOVE_JOB'):
            job_index, job, to_top = None, None, False

            if msg.startswith('MOVE_JOB_TOP'):
                job_index = msg[len('MOVE_JOB_TOP '):]
                to_top = True
            elif msg.startswith('MOVE_JOB_BACK'):
                job_index = msg[len('MOVE_JOB_BACK '):]
                to_top = False

            if job_index:
                job = self.get_job_from_index(int(job_index))

            if job and not self.is_file_in_transfer():
                try:
                    self.move_job(job, to_top)
                    response = _('{} in Warteschlange bewegt.').format(
                        job.title)
                except Exception as e:
                    LOGGER.error(e)
                    response = _('Job mit index {} konnte nicht bewegt werden.'
                                 ).format(job_index)
            else:
                response = _(
                    'Jobs können nicht bewegt werden während laufenden Dateitransfers.'
                )

        # ----------- CANCEL JOB ------------
        elif msg.startswith('CANCEL_JOB'):
            job_index = msg[len('CANCEL_JOB '):]
            job = self.get_job_from_index(int(job_index))

            if job:
                self.cancel_job(job)
                response = _('{} wird abgebrochen.').format(job.title)
            else:
                response = _(
                    'Job mit index {} konnte nicht abgebrochen werden.'
                ).format(job_index)

        # ----------- FORCE PSD REQUEST ------------
        elif msg.startswith('FORCE_PSD_CREATION'):
            job_index = msg[len('FORCE_PSD_CREATION '):]
            job = self.get_job_from_index(int(job_index))

            if job:
                if job is self.current_job:
                    response = _(
                        'PSD Erstellung fuer Job {} wird erzwungen.').format(
                            job.title)
                    self.force_psd_creation_signal.emit()
                else:
                    response = _(
                        'Kann PSD Erstellung fuer Job {} nicht erzwingen.'
                    ).format(job.title)

        if tcp_handler:
            self.tcp_respond_signal.connect(tcp_handler.respond)
            if type(response) is str:
                LOGGER.debug('Sending response: %s', response)
            else:
                LOGGER.debug('Sending response: transfer cache - %s',
                             len(response))
            self.tcp_respond_signal.emit(response)
Exemplo n.º 20
0
Arquivo: player.py Projeto: hodoje/dcs
class Player(QGraphicsObject):
    canShootSignal = pyqtSignal(CanPlayerShootSignalData)

    def __init__(self,
                 playerDetails,
                 config,
                 color,
                 firingKey,
                 movementKeys,
                 playerLevels,
                 field,
                 targetType,
                 animationTimer,
                 bulletTimer,
                 killEmitter,
                 playerDeadEmitter,
                 gameOverEmitter):
        super().__init__()
        # only some of the properties from playerDetails are used
        # that are needed for logic of the player
        self.id = playerDetails.id
        self.points = playerDetails.points
        if playerDetails.lives is None:
            self.lives = 2
        else:
            self.lives = playerDetails.lives
        if playerDetails.level is None:
            self.level = 1
        else:
            self.level = playerDetails.level
        self.isAlive = playerDetails.isAlive
        self.config = config
        self.color = color
        self.isShielded = False
        self.firingKey = firingKey
        self.movementKeys = movementKeys
        self.playerLevels = playerLevels
        self.field = field
        self.targetType = targetType
        self.animationTimer = animationTimer
        self.bulletTimer = bulletTimer
        self.killEmitter = killEmitter
        self.playerDeadEmitter = playerDeadEmitter
        self.gameOverEmitter = gameOverEmitter
        self.startingPos = None

        # used to determine if the player can shoot
        self.firedBullets = 0

        self.rateOfFire = self.playerLevels[f"star{self.level}"]["rateOfFire"]
        self.bulletSpeed = self.playerLevels[f"star{self.level}"]["bulletSpeed"]

        # initial cannon direction
        self.canonDirection = Direction.UP

        self.__init_ui__()

        # texture animation
        self.textureTimer = animationTimer
        self.textureTimer.timeout.connect(self.updateUi)

        # sounds
        #self.shotSound = QSound(self.config.sounds["playerShot"])
        self.shotSound = oalOpen(self.config.sounds["playerShot"])

    def __init_ui__(self):
        # set up player textures, refresh rate and transformation origin point
        self.textures = []
        self.textures.append(QImage(f"Resources/Images/Tanks/{self.color}/{self.color}FP.v{self.level}.png"))
        self.textures.append(QImage(f"Resources/Images/Tanks/{self.color}/{self.color}SP.v{self.level}.png"))
        self.currentTexture = 0
        self.height = self.textures[0].height()
        self.width = self.textures[0].width()
        self.shieldTextures = []
        self.shieldTextures.append(QImage(f"Resources/Images/Shield/shield{self.width}FP.png"))
        self.shieldTextures.append(QImage(f"Resources/Images/Shield/shield{self.width}SP.png"))
        self.currentShieldTexture = 0
        self.shieldTimer = QTimer()
        self.shieldTimer.setTimerType(Qt.PreciseTimer)
        self.shieldTimer.setInterval(50)
        self.shieldTimer.timeout.connect(self.shieldUi)
        self.m_boundingRect = QRectF(0, 0, self.width, self.height)
        # setting transform origin point to center of the player so the rotation will be in regard to the center
        self.setTransformOriginPoint(QPoint(self.boundingRect().width() / 2, self.boundingRect().height() / 2))

    # override default bounding rect
    def boundingRect(self):
        return self.m_boundingRect

    # override default paint
    def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget):
        QPainter.drawImage(0, 0, self.textures[self.currentTexture])
        if self.isShielded:
            QPainter.drawImage(0, 0, self.shieldTextures[self.currentShieldTexture])

    def updateUi(self):
        self.currentTexture = 1 if self.currentTexture == 0 else 0
        self.update()

    def shieldUi(self):
        self.currentShieldTexture = 1 if self.currentShieldTexture == 0 else 0
        self.update()

    # movements
    def moveRight(self):
        self.setX(self.x() + 1)

    def moveLeft(self):
        self.setX(self.x() - 1)

    def moveDown(self):
        self.setY(self.y() + 1)

    def moveUp(self):
        self.setY(self.y() - 1)

    # rotations, absolute degrees
    def rotate(self, direction):
        if direction == Direction.RIGHT:
            self.setRotation(90)
        elif direction == Direction.LEFT:
            self.setRotation(-90)
        elif direction == Direction.DOWN:
            self.setRotation(180)
        elif direction == Direction.UP:
            self.setRotation(0)

    def canMove(self, direction):
        canMove = True
        allObjects = self.scene().items()
        x1 = self.x()
        y1 = self.y()
        if direction == Direction.RIGHT:
            x1 += 1
        elif direction == Direction.LEFT:
            x1 -= 1
        elif direction == Direction.DOWN:
            y1 += 1
        elif direction == Direction.UP:
            y1 -= 1
        x2 = x1 + self.width
        y2 = y1 + self.height
        for obj in allObjects:
            # don't camper to self and field
            oType = type(obj)
            if self != obj and oType != QGraphicsRectItem:
                if type(obj) == Block:
                    # omit bushes and ice
                    if obj.type == BlockType.bush or obj.type == BlockType.ice:
                        continue
                if type(obj) == DeusEx:
                    continue
                objParent = obj.parentItem()
                objX1 = 0
                objY1 = 0
                if objParent is None:
                    objX1 = obj.x()
                    objY1 = obj.y()
                else:
                    objSceneCoords = obj.mapToScene(obj.pos())
                    objX1 = objSceneCoords.x()
                    objY1 = objSceneCoords.y()
                objX2 = objX1 + obj.boundingRect().width()
                objY2 = objY1 + obj.boundingRect().height()
                if x1 < objX2 and x2 > objX1 and y1 < objY2 and y2 > objY1:
                    canMove = False
                    break
        return canMove

    # NOTE: on 'Right' and 'Down' you will notice that there is a magic '-1'
    # it's there because there is some reason the bounding rect of the field will be
    # one pixel larger than the defined size
    def updatePosition(self, key):
        # before each move check if move is possible
        if key == self.movementKeys["Right"]:
            # check if the element is on the edge of the field
            if self.pos().x() + self.width < self.field.x() + self.field.boundingRect().width() - 1:
                if self.canMove(Direction.RIGHT):
                    # else move in the desired direction
                    self.moveRight()
            # check if the canon is facing the desired direction
            # if not, rotate to it and set the direction
            if not self.canonDirection == Direction.RIGHT:
                self.rotate(Direction.RIGHT)
            self.canonDirection = Direction.RIGHT
        elif key == self.movementKeys["Left"]:
            if self.pos().x() > self.field.x():
                if self.canMove(Direction.LEFT):
                    self.moveLeft()
            if not self.canonDirection == Direction.LEFT:
                self.rotate(Direction.LEFT)
            self.canonDirection = Direction.LEFT
        elif key == self.movementKeys["Down"]:
            if self.pos().y() + self.height < self.field.y() + self.field.boundingRect().height() - 1:
                if self.canMove(Direction.DOWN):
                    self.moveDown()
            if not self.canonDirection == Direction.DOWN:
                self.rotate(Direction.DOWN)
            self.canonDirection = Direction.DOWN
        elif key == self.movementKeys["Up"]:
            if self.pos().y() > self.field.y():
                if self.canMove(Direction.UP):
                    self.moveUp()
            if not self.canonDirection == Direction.UP:
                self.rotate(Direction.UP)
            self.canonDirection = Direction.UP

    def shoot(self, key):
        if self.firedBullets < self.rateOfFire:
            if key == self.firingKey:
                # create the bullet
                bullet = Bullet(self.canonDirection, self)
                # announce that the player can't shoot until the bullet calls this function again
                self.announceCanShoot(False)
                # set the bullet in the center of the tank
                # 0.37 is the 37% aspect ration of the width/height of the tank so the bullet
                # (with its width/height will be in the middle)
                # magic numbers are based on the size of the image itself and the black margin
                # between the image end and the tank object itself in that image
                if self.canonDirection == Direction.UP:
                    bullet.setPos(self.x() + self.boundingRect().width() * 0.37, self.y() - 15)
                elif self.canonDirection == Direction.DOWN:
                    bullet.setPos(self.x() + self.boundingRect().width() * 0.37, self.y() + self.boundingRect().height() + 5)
                elif self.canonDirection == Direction.LEFT:
                    bullet.setPos(self.x() - 15, self.y() + self.boundingRect().height() * 0.37)
                elif self.canonDirection == Direction.RIGHT:
                    bullet.setPos(self.x() + self.boundingRect().width() + 5, self.y() + self.boundingRect().height() * 0.37)
                # add the bullet to the scene
                self.scene().addItem(bullet)
                self.shotSound.play()

    def announceCanShoot(self, canShoot):
        # if canShoot is true, that means the bullet is destroyed, decrease fired bullets number and emit it can shoot
        # if canShoot is false, that means the the player fired a bullet, but don't emit the player can shoot
        # immediately but first check if fired bullets reached rate of fire, if reached, emit player cannot shoot
        # else, don't emit anything (which means that the firing notifier will still have canEmit flag set to True)
        if canShoot:
            self.firedBullets -= 1
            self.canShootSignal.emit(CanPlayerShootSignalData(self.id, canShoot))
        else:
            self.firedBullets += 1
            if self.firedBullets == self.rateOfFire:
                self.canShootSignal.emit(CanPlayerShootSignalData(self.id, canShoot))

# will be used for DeusEx and will be wrapped in player wrapper
    def levelUp(self):
        if not self.level == 4:
            self.level += 1
            self.rateOfFire = self.playerLevels[f"star{self.level}"]["rateOfFire"]
            self.bulletSpeed = self.playerLevels[f"star{self.level}"]["bulletSpeed"]
            self.updateTextures()

    def levelDown(self):
        if self.level != 1:
            self.level -= 1
            self.rateOfFire = self.playerLevels[f"star{self.level}"]["rateOfFire"]
            self.bulletSpeed = self.playerLevels[f"star{self.level}"]["bulletSpeed"]
            self.updateTextures()

    def resetPlayer(self):
        self.lives -= 1
        if self.lives < 0:
            self.playerDeadEmitter.playerDeadSignal.emit(self.id)
            return
        self.level = 1
        self.rateOfFire = self.playerLevels[f"star{self.level}"]["rateOfFire"]
        self.bulletSpeed = self.playerLevels[f"star{self.level}"]["bulletSpeed"]
        self.updateTextures()
        self.setPos(self.startingPos)

    def updateTextures(self):
        self.__init_ui__()
Exemplo n.º 21
0
class Danmaku(QWidget):
    onModified = pyqtSignal(str)

    def __init__(self, word, paraphrase, y, show_paraphrase=None, color=None):
        super().__init__()
        self._word = word
        self._paraphrase = paraphrase
        self._stop_move = False
        self._show_detail = False

        self.modified = False
        self._show_paraphrase = show_paraphrase \
            if show_paraphrase is not None else \
            utils.get_setting("danmaku_default_show_paraphrase")
        self._color = color if color is not None else \
            utils.get_setting("danmaku_default_color")
        self._cleared = False

        self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint
                            | Qt.FramelessWindowHint | Qt.ToolTip
                            | Qt.X11BypassWindowManagerHint  # for gnome
                            )
        self.setAttribute(Qt.WA_TranslucentBackground, True)
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.initUI()
        self.initPosition(y)

        # self.installEventFilter(self)

    # def eventFilter(self, o, e):
    #     # due to flag `X11BypassWindowManagerHint`, event `WindowDeactivate` doesn't work
    #     if e.type() == QEvent.WindowDeactivate:
    #         self._show_detail = False
    #         self._stop_move = False
    #         self.setWindowOpacity(0.5)
    #         self._continenter.hide()
    #         return False

    #     return super().eventFilter(o, e)

    @property
    def show_paraphrase(self):
        return self._show_paraphrase

    @show_paraphrase.setter
    def show_paraphrase(self, value):
        self._show_paraphrase = value
        self.modified = True
        self.onModified.emit('show_paraphrase')

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, value):
        self._color = value
        self.modified = True
        self.onModified.emit('color')

    @property
    def cleared(self):
        return self._cleared

    @cleared.setter
    def cleared(self, value):
        self._cleared = value
        self.modified = True
        self.onModified.emit('cleared')

    def setWordQss(self):
        bg_color, font_color = utils.COLORS[self.color]
        self._word_label.setStyleSheet(
            f"QLabel{{background-color:rgb({bg_color}); color:rgb({font_color}); padding:5; border-radius:6px}}"
        )

    def initUI(self):
        self.setWindowOpacity(utils.get_setting("danmaku_transparency"))

        word = WordLabel(self._word)
        if self.show_paraphrase:
            word.setText(word.text() + " " + self._paraphrase.splitlines()[0])

        word.onEnter.connect(self.enterWordEvent)
        word.onLeave.connect(self.leaveWordEvent)
        word.onMousePress.connect(self.mousePressWordEvent)
        word.onMouseMove.connect(self.mouseMoveWordEvent)
        word.onMouseRelease.connect(self.mouseReleaseWordEvent)

        self._word_label = word

        head = QHBoxLayout()
        head.addWidget(word)
        head.addStretch(1)
        word.setFont(QFont("Consolas", 18))

        body = QVBoxLayout()
        body.addLayout(head)
        self.setLayout(body)

        self.setWordQss()

        continenter = QWidget(self)
        continenter.setObjectName("continenter")
        continenter.setStyleSheet(
            "#continenter{background-color:white; padding:5; border-radius:6px}"
        )
        continenter.hide()
        self._continenter = continenter

        body.addStretch(1)
        body.addWidget(continenter)

        detail = QVBoxLayout()
        continenter.setLayout(detail)

        paraphrase = QLabel(self._paraphrase)
        paraphrase.setFont(QFont("Consolas", 15))
        paraphrase.setStyleSheet("QLabel{color:black;}")
        paraphrase.setWordWrap(True)
        paraphrase.setMaximumWidth(300)

        detail.addWidget(paraphrase)

        rbtns = QHBoxLayout()
        for name, (color, _) in utils.COLORS.items():
            rbtn = QRadioButton(None)
            qss = f"""
            QRadioButton::indicator {{ width:13px; height:13px; background-color:rgb({color}); border: 2px solid rgb({color}); }}
            QRadioButton::indicator:checked {{ border: 2px solid black; }}
            """
            rbtn.setStyleSheet(qss)
            rbtn.setChecked(name == self.color)
            rbtn.toggled.connect(self.clickColor)
            rbtn.color = name

            rbtns.addWidget(rbtn)

        detail.addLayout(rbtns)

        btns = QHBoxLayout()

        clear = QPushButton("Clear")
        clear.setStyleSheet("QPushButton{color:black;}")
        clear.clicked.connect(self.clickClear)
        btns.addWidget(clear)

        switch_paraphrase = QPushButton(
            "Hide Paraphrase" if self.show_paraphrase else "Show Paraphrase")
        switch_paraphrase.setStyleSheet("QPushButton{color:black;}")
        switch_paraphrase.clicked.connect(self.clickSwitch)
        btns.addWidget(switch_paraphrase)
        self._switch_paraphrase = switch_paraphrase

        detail.addLayout(btns)
        self._clear = clear

        self.show()

    def clickColor(self, e):
        if e:
            sender = self.sender()
            self.color = sender.color
            self.setWordQss()

    def clickClear(self):
        self.cleared = not self.cleared
        self._clear.setText("Redo" if self.cleared else "Clear")

    def clickSwitch(self):
        self.show_paraphrase = not self.show_paraphrase
        if self.show_paraphrase:
            self._word_label.setText(self._word + " " +
                                     self._paraphrase.splitlines()[0])
        else:
            self._word_label.setText(self._word)

        self._switch_paraphrase.setText(
            "Hide Paraphrase" if self.show_paraphrase else "Show Paraphrase")

    def enterWordEvent(self):
        self.setWindowOpacity(1)

    def leaveWordEvent(self):
        if not self._show_detail:
            self.setWindowOpacity(utils.get_setting("danmaku_transparency"))

    def mousePressWordEvent(self, e):
        if e.button() == Qt.LeftButton:
            self._press_point = e.globalPos() - self.pos()
            self._press_start = e.globalPos()
            e.accept()

    def mouseMoveWordEvent(self, e):
        if e.buttons() & Qt.LeftButton:
            self.move(e.globalPos() - self._press_point)
            e.accept()

    def mouseReleaseWordEvent(self, e):
        if (e.globalPos() - self._press_start).manhattanLength() < 10:
            self._show_detail = not self._show_detail
            if self._show_detail:
                self._stop_move = True
                self._continenter.show()
            else:
                self._stop_move = False
                self._continenter.hide()
                self.adjustSize()

    def initPosition(self, y):
        self._timer = QTimer(self)
        self._timer.setTimerType(Qt.PreciseTimer)
        self._timer.timeout.connect(self.update)
        speed = utils.get_setting("danmaku_speed")
        delta = 1
        while round(delta / speed) < 17:
            delta += 1

        self._timer.start(round(delta / speed))
        self._delta = delta

        w = QDesktopWidget().availableGeometry().width()
        self.move(w, y)

    def update(self):
        if self._stop_move: return
        x = self.x() - self._delta
        self.move(x, self.y())
        if x < -self.width():
            self._timer.stop()
            self.close()
Exemplo n.º 22
0
class Video(QObject):
    finish = pyqtSignal()
    t_changed = pyqtSignal(int)

    # 初始化视频信息
    def __init__(self, clip):
        super(Video, self).__init__()
        self.clip = clip
        self.t = 0
        self.fps = self.clip.fps if self.clip else 30
        self.stride = 1 / self.fps
        self.duration = self.clip.duration if self.clip else ui.audio_backend.duration

    # 设置定时器,定时画视频帧并提醒音频对象同步读取
    def work(self):
        self.timer = QTimer()
        self.timer.setTimerType(Qt.PreciseTimer)
        if self.clip:
            self.timer.timeout.connect(ui.openGLWidget.update)
        self.timer.timeout.connect(self.updatetime)
        self.timer.timeout.connect(ui.audio.update)
        self.timer.setInterval(1000 * self.stride)
        self.timer.start()

    # 根据进度入队新视频数据,若超时长则结束
    def updatetime(self):
        if self.t < self.duration:
            if self.clip:
                im = self.clip.get_frame(self.t)
                img = QImage(im.data, im.shape[1], im.shape[0],
                             im.shape[1] * 3, QImage.Format_RGB888)
                q.put(img)
            self.setT(self.t + self.stride)
        else:
            self.stop()
            self.finish.emit()

    def pause(self):
        self.timer.stop()

        self.t -= (2 * self.stride)

    def resume(self):
        self.timer.start(1000 * self.stride)

    def stop(self):
        self.timer.stop()
        self.setT(0)

    # 某一片段的抖音效果
    def tiktok(self):
        if isinstance(self.clip, VideoFileClip):
            self.clip = self.clip.fl_image(tiktok_effect)
            ui.cur_listWidget.currentItem().setClip(self.clip, self.clip.audio)
            ui.openGLWidget.update()

    # 设置要播放的新视频
    def setClip(self, clip):
        self.clip = clip
        self.setT(0)
        self.fps = self.clip.fps if self.clip else 30
        self.stride = 1 / self.fps
        self.duration = self.clip.duration if self.clip else ui.audio_backend.duration
        self.timer = QTimer()
        self.timer.setTimerType(Qt.PreciseTimer)
        if self.clip:
            self.timer.timeout.connect(ui.openGLWidget.update)
        self.timer.timeout.connect(self.updatetime)
        self.timer.timeout.connect(ui.audio.update)
        self.timer.setInterval(1000 * self.stride)

    # 调整播放进度
    def setT(self, t):
        self.t = t

        self.t_changed.emit(self.t * 10)
Exemplo n.º 23
0
Arquivo: deusex.py Projeto: hodoje/dcs
class DeusEx(QGraphicsObject):
    deusExActivateSignal = pyqtSignal(DeusExSignalData)

    def __init__(self, config, type, pulseSound, endingSound):
        super().__init__()
        self.type = type
        self.config = config
        self.width = 200
        self.height = 200
        self.m_boundingRect = QRectF(0, 0, self.width, self.height)
        self.m_painterPath = QPainterPath()
        self.m_painterPath.addEllipse(self.m_boundingRect)
        # radial gradient settings
        self.rgcx = self.m_boundingRect.center().x()
        self.rgcy = self.m_boundingRect.center().y()
        self.rgMinRadius = 50
        self.rgMaxRadius = 300
        self.rgCurrentRadius = 50
        self.rgfx = self.rgcx
        self.rgfy = self.rgcy
        self.rg = QRadialGradient(self.rgcx, self.rgcy, self.rgCurrentRadius, self.rgfx, self.rgfy)
        if self.type is DeusExTypes.POSITIVE:
            firstClr = QColor(Qt.green)
            firstClr.setAlphaF(0.7)
            secondClr = QColor(Qt.darkGreen)
            secondClr.setAlphaF(0.7)
            self.rg.setColorAt(0.0, firstClr)
            self.rg.setColorAt(1.0, secondClr)
        else:
            firstClr = QColor(Qt.red)
            firstClr.setAlphaF(0.7)
            secondClr = QColor(Qt.darkRed)
            secondClr.setAlphaF(0.7)
            self.rg.setColorAt(0.0, firstClr)
            self.rg.setColorAt(1.0, secondClr)
        # pulsing sound
        self.pulseSound = pulseSound
        self.endingSound = endingSound
        # pulsing timer
        self.pulseTimer = QTimer()
        self.pulseTimer.setTimerType(Qt.PreciseTimer)
        self.pulseTimer.timeout.connect(self.pulse)
        self.pulseTimer.start(100)
        # pre activate timer
        self.preActivateTimer = QTimer()
        self.preActivateTimer.setTimerType(Qt.PreciseTimer)
        self.preActivateTimer.timeout.connect(self.preActivate)
        if self.type is DeusExTypes.POSITIVE:
            self.preActivateTimer.start(10000)
        else:
            self.preActivateTimer.start(3000)
        # activate timer
        self.activateTimer = QTimer()
        self.activateTimer.setTimerType(Qt.PreciseTimer)
        self.activateTimer.timeout.connect(self.endingSoundFinished)

    def boundingRect(self):
        return self.m_boundingRect

    def shape(self):
        return self.m_painterPath

    def paint(self, QPainter, QStyleOptionGraphicsItem, widget=None):
        pen = QPen()
        if self.type is DeusExTypes.POSITIVE:
            pen.setColor(Qt.darkGreen)
        else:
            pen.setColor(Qt.darkRed)
        brush = QBrush(self.rg)
        QPainter.setPen(pen)
        QPainter.setBrush(brush)
        QPainter.drawEllipse(self.m_boundingRect)

    def pulse(self):
        if self.rgCurrentRadius == 50:
            self.pulseSound.play()
        self.rgCurrentRadius += 20
        self.rg.setCenterRadius(self.rgCurrentRadius)
        if self.rgCurrentRadius >= self.rgMaxRadius:
            self.rgCurrentRadius = self.rgMinRadius

    def preActivate(self):
        firstClr = QColor(Qt.yellow)
        firstClr.setAlphaF(0.7)
        secondClr = QColor(Qt.darkYellow)
        secondClr.setAlphaF(0.7)
        self.rg.setColorAt(0.0, firstClr)
        self.rg.setColorAt(1.0, secondClr)
        self.pulseSound.stop()
        self.pulseTimer.timeout.disconnect()
        self.preActivateTimer.timeout.disconnect()
        self.pulseTimer.stop()
        self.preActivateTimer.stop()

        # activate
        self.endingSound.play()
        self.activateTimer.start()

    def endingSoundFinished(self):
        if self.endingSound.get_state() == AL_STOPPED:
            deusExSignalData = DeusExSignalData(self.type)
            for obj in self.collidingItems():
                if type(obj).__name__ == "Player":
                    deusExSignalData.markedPlayers.append(obj)
            self.activateTimer.timeout.disconnect()
            self.activateTimer.stop()
            self.deusExActivateSignal.emit(deusExSignalData)
            self.scene().removeItem(self)
            del self
Exemplo n.º 24
0
class Canvas(QWidget):
    """
    Canvas class for PyQt5. Every time it is redrawn, it calls self.redraw.
    Every redraw is complete, pixel values are not remembered between redraws.
    There is self.frame_counter available that is incremented every timeout.
    self.is_timeout is only true for first redraw and redraws caused by self.timeout()
    """

    def __init__(self,
                 width=500,
                 height=500,
                 anim_period=-1,
                 antialiasing=True,
                 precise_timer=True):

        super().__init__()

        self.setFixedSize(width, height)
        self.anim_period = anim_period
        self.antialiasing = antialiasing
        self.p = QPainter()

        self.timer = QTimer(self)
        if precise_timer:
            self.timer.setTimerType(Qt.PreciseTimer)
        self.timer.timeout.connect(self.timeout)

        if anim_period >= 0:
            self.timer.start(anim_period)

        self.frame_counter = 0
        self.is_timeout = True

    def timeout(self):

        self.frame_counter += 1
        self.is_timeout = True
        self.update()

    def redraw(self):

        raise NotImplementedError("This method is abstract, you need to implement it.")

    def paintEvent(self, event):

        self.p.begin(self)
        if self.antialiasing:
            self.p.setRenderHint(QPainter.Antialiasing)
        self.redraw()
        self.p.end()
        self.is_timeout = False

    def keyPressEvent(self, e):

        if e.key() == Qt.Key_Space:
            if self.timer.isActive():
                self.timer.stop()
            else:
                if self.anim_period >= 0:
                    self.timer.start(self.anim_period)

        if e.key() == Qt.Key_S:
            if not self.timer.isActive():
                self.timeout()

        if e.key() == Qt.Key_Escape:
            sys.exit()
Exemplo n.º 25
0
    ui.roomRedButton.clicked.connect(on_room_red)
    ui.roomBlueButton.clicked.connect(on_room_blue)
    ui.roomWarmButton.clicked.connect(on_room_warm)
    ui.roomVerticalSlider.valueChanged.connect(on_room_slider)

    ui.exitButton.clicked.connect(on_exit)
    ui.openWindowButton.clicked.connect(on_open_window)
    ui.closeWindowButton.clicked.connect(on_close_window)
    ui.autoWindowButton.clicked.connect(on_auto_window)


    Widget.showFullScreen()

    slidetimer = QTimer()
    slidetimer.setInterval(1500)
    slidetimer.setTimerType(Qt.PreciseTimer)
    slidetimer.timeout.connect(on_slidetimer)
    slidetimer.setSingleShot(True)

    timer = QTimer()
    timer.setInterval(1000)
    timer.setTimerType(Qt.PreciseTimer)
    timer.timeout.connect(on_timer)
    timestamp_start = datetime.now()
    timer.start(1000)

    Widget.setMouseTracking(True)


    eventFilter = UiEventFilter()
    app.installEventFilter(eventFilter)
Exemplo n.º 26
0
class VideoModeWindow(object):

    def __init__(self, window, main_ui):
        self.main_ui = main_ui
        self.window = window

        # init signal and slot
        self.init_signal()

        self.sendMethod = None
        self.video = VideoProcess()

        # timer
        self.timer = QTimer(self.window)
        self.timer.timeout.connect(self.videoSend)

        self.lastTimeStamp = time.time()

    def init_signal(self):
        # connect signal and slot
        # Threshold value
        self.main_ui.spinBox_videoBWThreshold.valueChanged['int'].connect(self.main_ui.horizontalSlider_videoBWThreshold.setValue)
        self.main_ui.horizontalSlider_videoBWThreshold.valueChanged['int'].connect(self.main_ui.spinBox_videoBWThreshold.setValue)
        self.videoBWThresholdValue = 127
        self.main_ui.spinBox_videoBWThreshold.valueChanged.connect(self.videoBWThresholdValueUpdate)
        self.videoPreviewBWSize = '256*128'
        self.main_ui.checkBox_videoPreviewBW2x.clicked.connect(self.previewBWSizeUpdate)
        # import image
        self.main_ui.pbt_videoOpenFile.clicked.connect(self.openVideo)
        self.lastFolderPath = ''
        # BM invert
        self.main_ui.checkBox_videoBWInvert.clicked.connect(self.BWInvertUpdate)
        self.videoBWInvert = False
        # preview mode
        self.main_ui.radioButton_videoPreviewRaw.clicked.connect(self.previewModeUpdate)
        self.main_ui.radioButton_videoPreviewGray.clicked.connect(self.previewModeUpdate)
        self.main_ui.radioButton_videoPreviewBW.clicked.connect(self.previewModeUpdate)
        self.previewMode = 'raw'
        self.main_ui.horizontalSlider_videoPreviewFrameOffset.valueChanged.connect(self.previewSilderUpdate)
        self.main_ui.horizontalSlider_videoPreviewFrameOffset.valueChanged['int'].connect(self.main_ui.spinBox_videoFrameOffset.setValue)
        self.main_ui.spinBox_videoFrameOffset.valueChanged['int'].connect(self.main_ui.horizontalSlider_videoPreviewFrameOffset.setValue)

        self.currentFrameIndex = 0
        # send to OLED
        self.main_ui.pbt_videoStartSending.clicked.connect(self.videoSendStateUpdate)
        self.videoSendFramerate = 1
        self.videoSending = False

        # disable widget before import video file
        self.setPreviewSliderAndSpinboxEnable(False)
        self.setVideoWidgetEnable(False)

    def setPreviewSliderAndSpinboxEnable(self, enable):
        self.main_ui.horizontalSlider_videoPreviewFrameOffset.setEnabled(enable)
        self.main_ui.spinBox_videoFrameOffset.setEnabled(enable)

    def setVideoWidgetEnable(self, enable):
        self.main_ui.spinBox_videoBWThreshold.setEnabled(enable)
        self.main_ui.horizontalSlider_videoBWThreshold.setEnabled(enable)
        self.main_ui.checkBox_videoBWInvert.setEnabled(enable)
        self.main_ui.checkBox_videoPreviewBW2x.setEnabled(enable)

        self.main_ui.radioButton_videoPreviewRaw.setEnabled(enable)
        self.main_ui.radioButton_videoPreviewGray.setEnabled(enable)
        self.main_ui.radioButton_videoPreviewBW.setEnabled(enable)

        self.main_ui.pbt_videoStartSending.setEnabled(enable)

    def openVideo(self):
        filename, filetype = QFileDialog.getOpenFileName(self.window, 'Select a video file', self.lastFolderPath)
        if filename != '':
            self.lastFolderPath = path.dirname(filename)
            self.main_ui.lineEdit_videoFileName.setText(filename)
            self.video.open(filename)
            w = self.video.frameWidth
            h = self.video.frameHeight
            k = w / h
            height = self.main_ui.label_videoPreviewWindow.height()
            width = int(k * height) - 1
            self.video.setPreviewSize(width, height)
            self.video.requestFrame()
            self.main_ui.lineEdit_videoInfoFramerate.setText('%d' % (int(self.video.frameRate)))
            self.main_ui.lineEdit_videoInfoDuration.setText('%d|%.1f' % (int(self.video.frameCount), self.video.frameCount / self.video.frameRate))
            self.main_ui.horizontalSlider_videoPreviewFrameOffset.setMinimum(0)
            self.main_ui.horizontalSlider_videoPreviewFrameOffset.setMaximum(int(self.video.frameCount)-1)
            self.main_ui.spinBox_videoFrameOffset.setMinimum(0)
            self.main_ui.spinBox_videoFrameOffset.setMaximum(int(self.video.frameCount)-1)
            self.main_ui.horizontalSlider_videoPreviewFrameOffset.setSliderPosition(0)

            self.setPreviewSliderAndSpinboxEnable(True)
            self.setVideoWidgetEnable(True)

            self.video.requestFrame()
            self.timer.setSingleShot(True)
            self.timer.start(100)

    def videoBWThresholdValueUpdate(self):
        self.videoBWThresholdValue = self.main_ui.spinBox_videoBWThreshold.value()

        if not self.timer.isActive():
            self.video.setBWThreshold(value=self.videoBWThresholdValue)
            self.video.setIndex(self.currentFrameIndex)
            self.video.requestFrame()
            self.timer.setSingleShot(True)
            self.timer.start(50)

    def previewBWSizeUpdate(self):
        if self.main_ui.checkBox_videoPreviewBW2x.isChecked():
            self.videoPreviewBWSize = '256*128'
        else:
            self.videoPreviewBWSize = '128*64'

        if not self.timer.isActive():
            self.video.setIndex(self.currentFrameIndex)
            self.video.requestFrame()
            self.timer.setSingleShot(True)
            self.timer.start(50)

    def BWInvertUpdate(self):
        if self.main_ui.checkBox_videoBWInvert.isChecked():
            self.video.setBWThreshold(invert=True)
        else:
            self.video.setBWThreshold(invert=False)

        if not self.timer.isActive():
            self.video.setIndex(self.currentFrameIndex)
            self.video.requestFrame()
            self.timer.setSingleShot(True)
            self.timer.start(50)

    def previewModeUpdate(self):
        if self.main_ui.radioButton_videoPreviewRaw.isChecked():
            self.previewMode = 'raw'
        elif self.main_ui.radioButton_videoPreviewGray.isChecked():
            self.previewMode = 'gray'
        else:
            self.previewMode = 'BW'

        if not self.timer.isActive():
            self.video.setIndex(self.currentFrameIndex)
            self.video.requestFrame()
            self.timer.setSingleShot(True)
            self.timer.start(50)

    def previewSilderUpdate(self):
        self.currentFrameIndex = self.main_ui.horizontalSlider_videoPreviewFrameOffset.value()

        if not self.timer.isActive():
            self.video.setIndex(self.currentFrameIndex)
            self.video.requestFrame()
            self.timer.setSingleShot(True)
            self.timer.start(50)

    def updatePreview(self):
        # update preview
        if self.previewMode == 'raw':
            image = self.image_raw
        elif self.previewMode == 'gray':
            image = self.image_gray
        else:
            image = self.image_bw
        image_pix = OpenCVImage2QPixMap(image)
        self.main_ui.label_videoPreviewWindow.setPixmap(QPixmap.fromImage(image_pix))

    def updateBWPreview(self):
        if self.videoPreviewBWSize == '256*128':
            image = self.image_out_bw2x
        else:
            image = self.image_out_bw
        image_pix = OpenCVImage2QPixMap(image)
        self.main_ui.label_videoPreviewOutputBW.setPixmap(QPixmap.fromImage(image_pix))

    def videoSendStateUpdate(self):
        if self.videoSending:
            self.videoSending = False
            self.setPreviewSliderAndSpinboxEnable(True)
            self.main_ui.pbt_videoStartSending.setText('Start Sending')
            self.timer.stop()
            self.video.clearFrames()
        else:
            self.videoSending = True
            self.setPreviewSliderAndSpinboxEnable(False)
            self.video.clearFrames()
            self.video.setIndex(self.currentFrameIndex)
            self.video.requestFrame()
            self.main_ui.pbt_videoStartSending.setText('Stop Sending')
            timeout = int(1000 / self.main_ui.spinBox_videoSendFramerate.value())
            self.timer.setInterval(timeout)
            self.timer.setTimerType(0)
            self.timer.setSingleShot(False)
            self.timer.start()
            self.lastTimeStamp = time.time()

    def readAllImages(self):
        result = self.video.readFrames()
        if result != None:
            self.image_raw, self.image_gray, self.image_bw, self.image_out_bw, self.image_out_bw2x, self.image_data_frame = result
            return True
        else:
            return False

    def videoSend(self):
        if self.readAllImages():
            self.updatePreview()
            self.updateBWPreview()
            if self.videoSending:
                self.video.requestFrame()
                self.currentFrameIndex += 1
                self.send(self.image_data_frame)

        self.lastTimeStamp = time.time()

        self.main_ui.horizontalSlider_videoPreviewFrameOffset.setSliderPosition(self.currentFrameIndex)
        self.main_ui.spinBox_videoFrameOffset.setValue(self.currentFrameIndex)

        if self.currentFrameIndex + 1 >= int(self.video.frameCount):
            self.videoSendStateUpdate()


    def addSendMethod(self, sendMethod):
        self.sendMethod = sendMethod

    def send(self, data):
        if self.sendMethod != None:
            self.sendMethod.send(data)

    def exit(self):
        if self.video != None:
            self.video.stop()
Exemplo n.º 27
0
class MainWindow(QWidget):
    def __init__(self, parent=None, flags=Qt.WindowFlags()):
        super(MainWindow, self).__init__(parent=parent, flags=flags)
        self.subcarriers_index = get_subcarriers_index(bw=BW, ng=NG)
        self.configUI()
        self.configLayout()
        self.show()

    def configUI(self):
        self.setWindowTitle("CSIVIEWER")
        self.setWindowFlags(Qt.WindowCloseButtonHint
                            | Qt.WindowMinimizeButtonHint
                            | Qt.WindowMaximizeButtonHint)
        self.resize(800, 450)
        self.move(100, 100)
        self.setStyleSheet("border:0;background-color:#e8e8e8")

        # pygtgrapg global config
        pg.setConfigOptions(antialias=True)  # Antialiasing
        pg.setConfigOptions(background=(232, 232, 232))  # White
        pg.setConfigOptions(foreground=(25, 25, 25))  # Black

        # plot area
        self.plot = pg.GraphicsWindow()
        self.initPlot()

        # ctrl area
        self.ctrl = QMenuBar(self)
        self.initCtrl()

        # update cache
        self.task = GetDataThread(self)
        self.task.start()

        # plot refresh
        self.PlotTimer()

    def configLayout(self):
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.plot)
        layout.addWidget(self.ctrl)

    @staticmethod
    def color_define(index):
        return (int(np.sin(index * (2 * np.pi) / 30 + 2) * 127 + 128),
                int(np.cos(index * (2 * np.pi) / 30 - 1) * 127 + 128),
                int(np.sin(index * (2 * np.pi) / 30 - 2) * 127 + 128), 127)

    def initPlot(self):
        """Plot here"""
        # csi amplitude (time view) ======================================================
        pk_num = cache_amptiA.shape[1]
        p = self.plot.addPlot(title="Amplitude of CSI_800_%s_0_0" %
                              (SUBCARRIERS_NUM))
        p.showGrid(x=False, y=True)
        p.setLabel('left', "Amplitude")
        p.setLabel('bottom', "Packets", units='')
        p.enableAutoRange('xy', False)
        p.setXRange(-int(pk_num / 2), int(pk_num / 2), padding=0.01)
        p.setYRange(0, YRange_A, padding=0.01)

        self.curvesA = []
        for i in range(SUBCARRIERS_NUM):
            color = self.color_define(i)
            self.curvesA.append(p.plot(pen=color, name='subcarrier=%d' % (i)))
        # x axis
        self.X = np.linspace(-int(pk_num / 2), int(pk_num / 2), pk_num)

        # csi amplitude (subcarriers view) ===============================================
        p2 = self.plot.addPlot(title="CFR of CSI_10_%s_3_0" %
                               (SUBCARRIERS_NUM))
        p2.showGrid(x=False, y=True)
        p2.setLabel('left', "Amplitude")
        p2.setLabel('bottom', "Subcarriers", units='')
        p2.enableAutoRange('xy', False)
        p2.setXRange(-30, 30, padding=0.01)
        p2.setYRange(0, YRange_B, padding=0.01)

        self.curvesB = []
        for i in range(10 * 3):
            color = self.color_define(i)
            self.curvesB.append(p2.plot(pen=color))

        self.plot.nextRow()
        # csi phase (subcarriers view) ===================================================
        p3 = self.plot.addPlot(title="Phase of CSI_10_%s_3_0" %
                               (SUBCARRIERS_NUM))
        p3.showGrid(x=False, y=True)
        p3.setLabel('left', "Phase")
        p3.setLabel('bottom', "Subcarriers", units='')
        p3.enableAutoRange('xy', False)
        p3.setXRange(-30, 30, padding=0.01)
        p3.setYRange(-np.pi, np.pi, padding=0.01)

        self.curvesC = []
        for i in range(10 * 3):
            color = self.color_define(i)
            self.curvesC.append(p3.plot(pen=color))

        # cir csi (time view) ============================================================
        p4 = self.plot.addPlot(title="CIR of CSI_10_%s_3_0" %
                               (SUBCARRIERS_NUM))
        p4.showGrid(x=False, y=True)
        p4.setLabel('left', "Amplitude")
        p4.setLabel('bottom', "Time", units='50ns')
        p4.enableAutoRange('xy', False)
        p4.setXRange(0, 64, padding=0.01)
        p4.setYRange(0, YRange_D, padding=0.01)

        self.curvesD = []
        for i in range(10 * 3):
            color = self.color_define(i)
            self.curvesD.append(p4.plot(pen=color))

    def initCtrl(self):
        """Write your control here."""
        self.ctrl.addMenu("&File")
        self.ctrl.addMenu("&Edit")
        self.ctrl.addMenu("&Help")
        self.ctrl.setVisible(False)

    def PlotTimer(self):
        self.timer = QTimer()
        self.timer.setTimerType(0)
        self.timer.timeout.connect(self.updatePlot)
        self.timer.start(1000 // 30)

    @pyqtSlot()
    def updatePlot(self):
        global cache_amptiA, cache_amptiB, cache_phaseC, cache_cirD, mutex, state
        mutex.lock()
        if state:
            for i in range(SUBCARRIERS_NUM):
                self.curvesA[i].setData(self.X, cache_amptiA[i])
            for i in range(10 * 3):
                self.curvesB[i].setData(self.subcarriers_index,
                                        cache_amptiB[:, i % 10, i // 10])
                self.curvesC[i].setData(self.subcarriers_index,
                                        cache_phaseC[:, i % 10, i // 10])
                self.curvesD[i].setData(np.arange(64), cache_cirD[:, i % 10,
                                                                  i // 10])
            state = False
        mutex.unlock()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_F11:
            self.setWindowState(self.windowState() ^ Qt.WindowFullScreen)
        if event.key() == Qt.Key_F1:
            self.ctrl.setVisible(not self.ctrl.isVisible())
        event.accept()

    def closeEvent(self, event):
        self.task.stop()
        event.accept()
Exemplo n.º 28
0
class MissionPage(QWidget):
    mission_ended = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)

        self.record = -1
        self.inspected = None
        self.oob_update = False
        prefs = QSettings()
        prefs.beginGroup("/General")
        timeout = prefs.value("/Timeout")
        dark_mode = prefs.value("/DarkMode")
        prefs.endGroup()

        # Instantiate core objects
        self.timeout_timer = QTimer()
        self.timeout_timer.setTimerType(Qt.VeryCoarseTimer)
        self.timeout_timer.setInterval(timeout * 1000)
        self.timeout_timer.setSingleShot(True)
        self.timeout_timer.timeout.connect(self.update_temp_log)
        self.systems = ActionsWidget(LogSource.SYSTEM)
        self.systems.acted.connect(self.log_item)
        self.events = ActionsWidget(LogSource.EVENT)
        self.events.acted.connect(self.log_item)

        self.compass = Compass()
        self.compass_widget = QWidget()
        compass_layout = QHBoxLayout()
        self.compass_widget.setLayout(compass_layout)
        compass_layout.addWidget(self.compass)
        self.compass.angle_event.connect(self.log_item)

        self.exact_angle = ExactAngle()
        self.exact_angle_widget = QWidget()
        exact_angle_layout = QHBoxLayout()
        self.exact_angle_widget.setLayout(exact_angle_layout)
        exact_angle_layout.addWidget(self.exact_angle)
        self.exact_angle.btn_event.connect(self.reset_timer)
        self.exact_angle.angle_event.connect(self.log_item)

        tab_widget = QTabWidget()
        tab_bar = tab_widget.tabBar()
        tab_bar.setFont(QFont('Consolas', 12, 3))
        tab_widget.addTab(self.compass_widget, "Compass")
        tab_widget.addTab(self.exact_angle_widget, "Precise Angle")
        tab_widget.setStyleSheet("""
                QTabWidget::pane {
                    border-top: 2px solid #C2C7CB;
                }
                /* Style the tab using the tab sub-control. Note that
                    it reads QTabBar _not_ QTabWidget */
                QTabBar::tab {
                    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                stop: 0 #E1E1E1, stop: 0.4 #DDDDDD,
                                stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3);
                    border: 2px solid #C4C4C3;
                    border-bottom-color: #C2C7CB; /* same as the pane color */
                    border-top-left-radius: 4px;
                    border-top-right-radius: 4px;
                    min-width: 8ex;
                    padding: 2px;
                    color: black;
                }

                QTabBar::tab:selected, QTabBar::tab:hover {
                    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                stop: 0 #fafafa, stop: 0.4 #f4f4f4,
                                stop: 0.5 #e7e7e7, stop: 1.0 #fafafa);
                }

                QTabBar::tab:selected {
                    border-color: #ff0000;
                    border-bottom-color: #C2C7CB; /* same as pane color */
                }

                QTabBar::tab:!selected {
                    margin-top: 2px; /* make non-selected tabs look smaller */
                }
            """)

        header_layout = QHBoxLayout()
        self.zulu_time_label = QLabel()
        self.assessor_label = QLabel()
        self.date_label = QLabel()
        self.dl_label = QLabel()
        self.mnemonic_label = QLabel()
        header_layout.addWidget(self.zulu_time_label)
        header_layout.addWidget(self.assessor_label)
        header_layout.addWidget(self.date_label)
        header_layout.addWidget(self.dl_label)
        header_layout.addWidget(self.mnemonic_label)
        res = QApplication.primaryScreen().size()
        w, h = res.width(), res.height()
        if w > 1920 or h > 1080:
            hdr_font = QFont("Consolas", 16, 2)
            end_font = QFont("Consolas", 32, 5)
        else:
            hdr_font = QFont("Consolas", 14, 2)
            end_font = QFont("Consolas", 28, 5)
        for index in range(header_layout.count()):
            widget = header_layout.itemAt(index).widget()
            widget.setSizePolicy(
                QSizePolicy.Preferred, QSizePolicy.Maximum
            )
            widget.setFont(hdr_font)
            widget.setAlignment(Qt.AlignCenter)

        # Setup logging state machine
        self.init_log_sm()

        # Setup splitters
        actions_splitter = QSplitter(
            Qt.Horizontal,
            frameShape=QFrame.StyledPanel,
            frameShadow=QFrame.Plain
        )
        actions_splitter.addWidget(self.systems)
        actions_splitter.addWidget(self.events)
        actions_splitter.addWidget(tab_widget)
        actions_splitter.setChildrenCollapsible(False)
        main_splitter = QSplitter(
            Qt.Vertical,
            frameShape=QFrame.StyledPanel,
            frameShadow=QFrame.Plain
        )
        self.log_area = QTableWidget(0, 3)
        self.log_area.cellDoubleClicked.connect(self.entry_inspected)
        self.log_area.cellChanged.connect(self.entry_changed)
        self.log_area.setHorizontalHeaderLabels(
            ["Time", "System", "Events"]
        )
        self.log_area.horizontalHeader().setStretchLastSection(True)
        self.set_dark_mode(dark_mode)
        end_msn_btn = QPushButton("END\r\nMISSION")
        end_msn_btn.clicked.connect(self.end_mission)
        end_msn_btn.setFont(end_font)
        end_msn_btn.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        end_msn_btn.setStyleSheet("background-color: red; color: white")
        bottom_layout = QGridLayout()
        bottom_widget = QWidget()
        bottom_widget.setLayout(bottom_layout)
        bottom_layout.addWidget(self.log_area, 0, 0, 1, 7)
        bottom_layout.addWidget(end_msn_btn, 0, 8, 1, 1)
        main_splitter.addWidget(actions_splitter)
        main_splitter.addWidget(bottom_widget)
        main_splitter.setChildrenCollapsible(False)
        handle_css = """
            QSplitter::handle {
                background-image: url(:/imgs/dot_pattern.png);
                background-repeat: repeat-xy;
                background-color: none;
                border: 1px solid gray;
            }
            QSplitter::handle:pressed {
                background-image: url(:/imgs/pressed.png);
            }
        """
        actions_splitter.setStyleSheet(handle_css)
        main_splitter.setStyleSheet(handle_css)

        # Finalize layout
        main_layout = QVBoxLayout()
        main_layout.addLayout(header_layout)
        main_layout.addWidget(main_splitter)
        self.setLayout(main_layout)

    def load_mission(self, config, timer, time, recovered=None):
        cfg_systems = []
        cfg_events = []
        for system in config['systems']:
            cfg_systems.append(system)
        for event in config['events']:
            cfg_events.append(event)
        self.systems.add_actions(cfg_systems)
        self.events.add_actions(cfg_events)
        self.timer = timer
        self.timer.timeout.connect(self.inc_time)
        self.time = time
        self.assessor = config['assessor']
        self.assessor_label.setText("Assessor: " + self.assessor)
        self.date = config['date']
        self.date_label.setText("Date: " + self.date)
        self.dl = config['dl']
        self.dl_label.setText("Mission: DL-" + self.dl)
        self.mnemonic = config['mnemonic']
        self.mnemonic_label.setText("Mnemonic: " + self.mnemonic)
        date = QDate.fromString(self.date, "dd/MM/yyyy").toString("yyyyMMdd")
        self.file_name = f"DL-{self.dl} {self.mnemonic} {date}"
        temp_path = Path(__file__).parents[1] / "temp"
        temp_cfg = temp_path / f"{self.file_name}.cfg"
        os.makedirs(os.path.dirname(temp_cfg), exist_ok=True)
        self.temp_log = temp_path / f"{self.file_name}.csv"
        os.makedirs(os.path.dirname(self.temp_log), exist_ok=True)
        if temp_cfg:
            with open(temp_cfg, 'w') as save_cfg_file:
                save_cfg_file.write(json.dumps(config))
        else:
            QMessageBox.critical(
                self,
                "Error",
                f"Unable to load recovered config file: {temp_cfg}"
            )
            return
        if recovered:
            self.recover_log(recovered)

    def recover_log(self, log):
        with open(log, 'r', newline='') as infile:
            reader = csv.reader(infile, delimiter=',')
            for row in reader:
                self.record = self.record + 1
                self.log_area.insertRow(self.record)
                self.log_area.setItem(
                    self.record,
                    0,
                    QTableWidgetItem(row[0])
                )
                self.log_area.setItem(
                    self.record,
                    1,
                    QTableWidgetItem(row[1])
                )
                self.log_area.setItem(
                    self.record,
                    2,
                    QTableWidgetItem(row[2])
                )
        # TODO: This isn't working...
        self.log_area.scrollToBottom()

    @pyqtSlot()
    def inc_time(self):
        self.time = self.time.addSecs(1)
        self.zulu_time_label.setText(
            "Time: {} ZULU".format(self.time.toString("HH:mm:ss"))
        )

    def init_log_sm(self):
        self.log_state = QStateMachine()
        pre_system = QState()
        pre_event = QState()
        post_event = QState()
        self.log_state.addState(pre_system)
        self.log_state.addState(pre_event)
        self.log_state.addState(post_event)
        self.log_state.setInitialState(pre_system)
        pre_system.assignProperty(self.events, "enabled", False)
        pre_system.assignProperty(self.compass, "enabled", False)
        pre_system.assignProperty(self.exact_angle, "enabled", False)
        pre_event.assignProperty(self.events, "enabled", True)
        pre_event.assignProperty(self.compass, "enabled", False)
        pre_event.assignProperty(self.exact_angle, "enabled", False)
        post_event.assignProperty(self.compass, "enabled", True)
        post_event.assignProperty(self.exact_angle, "enabled", True)
        pre_system.addTransition(
            self.systems.acted, pre_event
        )
        pre_system.addTransition(self.timeout_timer.timeout, pre_system)
        pre_event.addTransition(self.timeout_timer.timeout, pre_system)
        post_event.addTransition(self.timeout_timer.timeout, pre_system)
        pre_event.addTransition(
            self.systems.acted, pre_event
        )
        post_event.addTransition(
            self.systems.acted, pre_event
        )
        post_event.addTransition(
            self.events.acted, post_event
        )
        pre_event.addTransition(
            self.events.acted, post_event
        )
        pre_system.entered.connect(self.events.switch_active)
        pre_system.entered.connect(self.systems.switch_active)
        pre_event.entered.connect(self.events.switch_active)
        post_event.exited.connect(self.compass.clear_state)
        post_event.exited.connect(lambda: self.exact_angle.log_angle(False))
        self.log_state.setRunning(True)

    def log_system(self, system):
        self.record = self.record + 1
        event_time = self.time.toString("HH:mm:ss")
        self.log_area.insertRow(self.record)
        self.log_area.setItem(
            self.record,
            0,
            QTableWidgetItem(event_time)
        )
        self.log_area.setItem(
            self.record,
            1,
            QTableWidgetItem(system)
        )
        self.log_area.setItem(
            self.record,
            2,
            QTableWidgetItem("")
        )
        self.log_area.scrollToBottom()

    def log_event(self, event):
        current = self.log_area.item(self.record, 2).text()
        if len(current) > 0:
            current = current + "; "
        current = current + event
        self.log_area.setItem(
            self.record,
            2,
            QTableWidgetItem(current)
        )

    def log_compass(self, range):
        current = self.log_area.item(self.record, 2).text()
        current = current + range
        self.log_area.setItem(
            self.record,
            2,
            QTableWidgetItem(current)
        )

    def log_angle(self, angle):
        current = self.log_area.item(self.record, 2).text()
        current = f"{current} at {angle}°"
        self.log_area.setItem(
            self.record,
            2,
            QTableWidgetItem(current)
        )

    @pyqtSlot(Angle)
    @pyqtSlot(int)
    def log_item(self, data):
        last_unlogged = self.timeout_timer.isActive()
        self.timeout_timer.start()
        src = self.sender()
        if type(src) is ActionsWidget:
            if self.exact_angle.has_valid():
                if self.exact_angle.is_valid():
                    self.log_angle(self.exact_angle.calc_angle())
                self.exact_angle.clear_state()
            if src.source is LogSource.SYSTEM:
                self.log_system(src.get_action(data))
                if last_unlogged:
                    self.update_temp_log()
            elif src.source is LogSource.EVENT:
                self.log_event(src.get_action(data))
        elif type(src) is Compass:
            self.log_compass(Angle.to_string(data))
        elif type(src) is ExactAngle:
            self.log_angle(str(data))

    @pyqtSlot(int, int)
    def entry_inspected(self, row, col):
        self.inspected = row, col

    @pyqtSlot(int, int)
    def entry_changed(self, row, col):
        if (row, col) == self.inspected:
            if not self.timeout_timer.isActive():
                self.update_temp_log(False)
            elif row == self.record - 1:
                self.oob_update = True

    def update_temp_log(self, append=True):
        if self.oob_update:
            append = False
            self.oob_update = False
        if self.temp_log:
            if append:
                with open(self.temp_log, 'a', newline='') as outfile:
                    writer = csv.writer(outfile)
                    rowdata = []
                    row = self.log_area.rowCount() - 1
                    for column in range(self.log_area.columnCount()):
                        item = self.log_area.item(row, column)
                        if item is not None:
                            rowdata.append(
                                item.text()
                            )
                        else:
                            rowdata.append('')
                    writer.writerow(rowdata)
            else:
                with open(self.temp_log, 'w', newline='') as outfile:
                    writer = csv.writer(outfile)
                    for row in range(self.log_area.rowCount()):
                        rowdata = []
                        for column in range(self.log_area.columnCount()):
                            item = self.log_area.item(row, column)
                            if item is not None:
                                rowdata.append(
                                    item.text()
                                )
                            else:
                                rowdata.append('')
                        writer.writerow(rowdata)

    def save_log(self):
        path, _ = QFileDialog.getSaveFileName(
                self, 'Save File', self.file_name, 'CSV(*.csv)'
            )
        if path:
            with open(path, 'w', newline='') as outfile:
                writer = csv.writer(outfile)
                for row in range(self.log_area.rowCount()):
                    rowdata = []
                    for column in range(self.log_area.columnCount()):
                        item = self.log_area.item(row, column)
                        if item is not None:
                            rowdata.append(
                                item.text()
                            )
                        else:
                            rowdata.append('')
                    writer.writerow(rowdata)
            return True
        return False

    @pyqtSlot()
    def end_mission(self):
        quit_prompt = QMessageBox.question(
                    self,
                    "End mission?",
                    "If you choose to end this mission, the time hack will end and logging will stop. Really end?"  # noqa: E501
                )
        if quit_prompt == QMessageBox.Yes:
            if self.save_log():
                QMessageBox.information(
                        self,
                        "Mission Ended",
                        "Mission has been ended and your logs have been saved."
                    )
                temp_path = Path(__file__).parents[1] / "temp"
                log_files = [
                    file for file in temp_path.rglob("*.csv") if file.is_file()
                ]
                cfg_files = [
                    file for file in temp_path.rglob("*.cfg") if file.is_file()
                ]
                if log_files and cfg_files:
                    try:
                        for file in log_files + cfg_files:
                            file.unlink()
                    except OSError as e:
                        QMessageBox.critical(
                            self,
                            "Error",
                            f"Error encountered attempting to delete temp files: { e.strerror }"  # noqa: E501
                        )
                self.mission_ended.emit()

    @pyqtSlot()
    def reset_timer(self):
        self.timeout_timer.start()

    @pyqtSlot(int)
    def set_timeout(self, timeout):
        self.timeout_timer.setInterval(timeout * 1000)

    @pyqtSlot(QSize)
    def window_resized(self, size):
        self.events.resize(size.height())
        self.systems.resize(size.height())

    @pyqtSlot(int)
    def set_dark_mode(self, enable):
        if enable:
            self.log_area.setStyleSheet("QTableWidget::item { color: white }")
        else:
            self.log_area.setStyleSheet("QTableWidget::item { color: none }")
Exemplo n.º 29
0
 def startMoving(self):
     # print(self.dotsAnimations)
     timer = QTimer()
     timer.setTimerType(0)
     for i, animation in enumerate(self.dotsAnimations):
         timer.singleShot(i * self.separationTime, animation.start)
Exemplo n.º 30
0
class MainWindow(QWidget):
    def __init__(self, parent=None, flags=Qt.WindowFlags()):
        super(MainWindow, self).__init__(parent=parent, flags=flags)
        self.configUI()
        self.configLayout()
        self.show()

    def configUI(self):
        screen_size = QApplication.primaryScreen().size()
        self.setWindowTitle("CSIRatioA")
        self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint)
        self.resize(800, 450)
        self.move(screen_size.width() - 800 - 100, 100)

        # pygtgrapg global config
        pg.setConfigOptions(antialias=True)                 # Antialiasing
        pg.setConfigOptions(foreground=(232, 232, 232))     # Black
        pg.setConfigOptions(background=(25, 25, 25))        # White

        # plot area
        self.win = pg.GraphicsWindow()

        # plot init
        self.initPlot()

        # update cache
        self.task = GetDataThread(self)
        self.task.start()

        # plot refresh
        self.PlotTimer()

    def configLayout(self):
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.win)

    def initPlot(self):
        lens = cache.shape[-1]

        p = self.win.addPlot(title="Ratio of CSI(30x)")
        p.showGrid(x=False, y=True)
        p.setLabel('left', "Amplitude", units='A')
        p.setLabel('bottom', "Packets", units='')
        p.enableAutoRange('xy', False)
        p.setXRange(-int(lens/2), int(lens/2), padding=0.01)
        p.setYRange(-10, 70, padding=0.01)
        p.hideAxis('left')
        # p.addLegend()

        # curves
        self.curves = []
        for i in range(len(cache)):
            color = (int(np.sin(i * (2 * np.pi) / 30 + 2) * 127 + 128),
                     int(np.cos(i * (2 * np.pi) / 30 - 1) * 127 + 128),
                     int(np.sin(i * (2 * np.pi) / 30 - 2) * 127 + 128),
                     127)
            self.curves.append(p.plot(pen=color, name='subcarrier=%d' % (i)))

        # x axis
        self.X = np.linspace(-int(lens/2), int(lens/2), lens)

    def PlotTimer(self):
        self.timer = QTimer()
        self.timer.setTimerType(0)
        self.timer.timeout.connect(self.updatePlot)
        self.timer.start(1000//30)

    @pyqtSlot()
    def updatePlot(self):
        global cache, mutex
        mutex.lock()
        for i in range(len(cache)):
            xscale = 1 - i / 200.
            self.curves[i].setData(xscale * self.X, i/2 + cache[i])
        mutex.unlock()

    def closeEvent(self, event):
        self.task.stop()
        event.accept()