def testBasic(self):
     # Also related to bug #244, on existence of setVisible'''
     widget = QWidget()
     self.assertTrue(not widget.isVisible())
     widget.setVisible(True)
     self.assertTrue(widget.isVisible())
     self.assertTrue(widget.winId() is not 0)
    def testWindowButtonClickClose(self):
        button = QPushButton()
        window = QWidget()
        window.connect(button, SIGNAL('clicked()'), SLOT('close()'))

        window.show()
        self.assertTrue(window.isVisible())
        button.click()
        self.assertTrue(not window.isVisible())
    def testWindowButtonClickClose(self):
        button = QPushButton()
        window = QWidget()
        window.connect(button, SIGNAL('clicked()'), SLOT('close()'))

        window.show()
        self.assert_(window.isVisible())
        button.click()
        self.assert_(not window.isVisible())
 def _showRow(self, label: str, widget: QWidget, show: bool):
     if show and not widget.isVisible():
         widget.show()
         self._form_layout.addRow(label, widget)
     elif not show and widget.isVisible():
         label = self._form_layout.labelForField(widget)
         widget.hide()
         label.hide()
         self._form_layout.removeWidget(widget)
         self._form_layout.removeWidget(label)
         del label
Exemple #5
0
    def toggle_hide_show(self, widget: QWidget) -> None:
        """toggles visibiliy of a given widget
        Arg:
            widget: widget which is aimed to be hidden or shown
        Returs:
            None"""

        if widget.isVisible():
            widget.hide()
        else:
            widget.show()
Exemple #6
0
class PointerPropertyEditor(QGroupBox, PropertyWidget):
    def __init__(self, target_property_name, template):
        QGroupBox.__init__(self)
        PropertyWidget.__init__(self, target_property_name)

        main_layout = QVBoxLayout(self)
        button_layout = QHBoxLayout()
        self.make_unique_button = QPushButton("Make Unique")
        self.make_unique_button.setSizePolicy(
            QtWidgets.QSizePolicy.MinimumExpanding,
            QtWidgets.QSizePolicy.MinimumExpanding)
        self.clear_button = QPushButton("Clear")
        self.clear_button.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
                                        QtWidgets.QSizePolicy.MinimumExpanding)
        self.toggle_button = QPushButton("Toggle Editor")
        self.toggle_button.setSizePolicy(
            QtWidgets.QSizePolicy.MinimumExpanding,
            QtWidgets.QSizePolicy.MinimumExpanding)
        button_layout.addWidget(self.make_unique_button)
        button_layout.addWidget(self.clear_button)
        button_layout.addWidget(self.toggle_button)
        main_layout.addLayout(button_layout)

        self.property_form = PropertyForm(template)
        self.form_widget = QWidget()
        self.property_form.setContentsMargins(0, 0, 0, 0)
        self.form_widget.setLayout(self.property_form)
        main_layout.addWidget(self.form_widget)
        self.setLayout(main_layout)
        self.form_widget.setVisible(False)

        self.make_unique_button.clicked.connect(self._on_make_unique_clicked)
        self.clear_button.clicked.connect(self._on_clear_clicked)
        self.toggle_button.clicked.connect(lambda: self.form_widget.setVisible(
            not self.form_widget.isVisible()))

    def _on_make_unique_clicked(self):
        if self.target:
            self.target[self.target_property_name].make_unique()
            self._on_target_changed()  # Pointer value changes here.

    def _on_clear_clicked(self):
        if self.target:
            self.target[self.target_property_name].clear_value()
            self._on_target_changed()  # Pointer value changes here.

    def _on_target_changed(self):
        if self.target:
            target_for_children = self._get_target_value()
            self.property_form.update_target(target_for_children)
            self.form_widget.setEnabled(target_for_children is not None)
        else:
            self.property_form.update_target(None)
Exemple #7
0
    class WidgetPySignal(UsesQApplication):
        """Tests the connection of python signals to QWidget qt slots."""
        def setUp(self):
            super(WidgetPySignal, self).setUp()
            self.obj = Dummy()
            self.widget = QWidget()

        def tearDown(self):
            super(WidgetPySignal, self).tearDown()
            del self.obj
            del self.widget

        def testShow(self):
            """Emission of a python signal to QWidget slot show()"""
            self.widget.hide()

            QObject.connect(self.obj, SIGNAL('dummy()'), self.widget,
                            SLOT('show()'))
            self.assert_(not self.widget.isVisible())

            self.obj.emit(SIGNAL('dummy()'))
            self.assert_(self.widget.isVisible())
Exemple #8
0
    class WidgetPySignal(UsesQApplication):
        """Tests the connection of python signals to QWidget qt slots."""

        def setUp(self):
            super(WidgetPySignal, self).setUp()
            self.obj = Dummy()
            self.widget = QWidget()

        def tearDown(self):
            super(WidgetPySignal, self).tearDown()
            del self.obj
            del self.widget

        def testShow(self):
            """Emission of a python signal to QWidget slot show()"""
            self.widget.hide()

            QObject.connect(self.obj, SIGNAL("dummy()"), self.widget, SLOT("show()"))
            self.assert_(not self.widget.isVisible())

            self.obj.emit(SIGNAL("dummy()"))
            self.assert_(self.widget.isVisible())
class PointerPropertyEditor(QGroupBox, PropertyWidget):
    def __init__(self, target_property_name, template):
        QGroupBox.__init__(self)
        PropertyWidget.__init__(self, target_property_name)

        main_layout = QVBoxLayout(self)
        button_layout = QHBoxLayout()
        self.make_unique_button = QPushButton("Make Unique")
        self.toggle_button = QPushButton("Toggle Editor")
        button_layout.addWidget(self.make_unique_button)
        button_layout.addWidget(self.toggle_button)
        main_layout.addLayout(button_layout)

        form_layout = QFormLayout()
        self.form_widget = QWidget()
        self.editors = []
        for (key, value) in template.items():
            label = QLabel(key)
            editor = value.create_editor()
            self.editors.append(editor)
            form_layout.addRow(label, editor)
        form_layout.setContentsMargins(0, 0, 0, 0)
        self.form_widget.setLayout(form_layout)
        main_layout.addWidget(self.form_widget)
        self.setLayout(main_layout)
        self.form_widget.setVisible(False)

        self.make_unique_button.clicked.connect(self._on_make_unique_clicked)
        self.toggle_button.clicked.connect(lambda: self.form_widget.setVisible(
            not self.form_widget.isVisible()))

    def _on_make_unique_clicked(self):
        if self.target:
            self.target[self.target_property_name].make_unique(self.target)
            self._on_target_changed()  # Pointer value changes here.

    def _on_target_changed(self):
        if self.target:
            target_for_children = self.target[self.target_property_name].value
            for editor in self.editors:
                editor.update_target(target_for_children)
        else:
            for editor in self.editors:
                editor.update_target(None)
Exemple #10
0
 def testBasic(self):
     # Also related to bug #244, on existence of setVisible'''
     widget = QWidget()
     self.assert_(not widget.isVisible())
     widget.setVisible(True)
     self.assert_(widget.isVisible())
Exemple #11
0
class Vfo():
    mqtt: MqttHandler

    def __init__(self, name: str, mqtt: MqttHandler,
                 networkHandler: NetworkHandler, warnings: WarningHandler,
                 rxButton: QPushButton, txButton: QPushButton, fcLabel: QLabel,
                 bwLabel: QLabel, modeButton: QPushButton,
                 freqButton: QPushButton):
        self.name = name
        self.mqtt = mqtt
        self.networkHandler = networkHandler
        self.warnings = warnings
        self.rxButton = rxButton
        self.txButton = txButton

        self.bwLabel = bwLabel
        self.modeButton = modeButton
        self.modeButton.setStyleSheet(
            self.modeButton.styleSheet() +
            "font: Waree; font-size: 64px; font-weight: bold;")
        self.modeButton.setText("")

        # Replace freq QPushButton with one that supports rich text
        layout = freqButton.parent().layout()
        self.freqButton = RichTextPushButton()

        self.freqButton.setStyleSheet(
            "background-color: transparent;\nborder: 1px solid transparent;"
            "font: Waree; font-size: 80px; font-weight: bold;")

        layout.replaceWidget(freqButton, self.freqButton)
        freqButton.setParent(None)  # Delete old button
        self.freqButton.setText("Click to select freq")

        self.transverter = None
        self.transverterMutex = Lock()
        self.transverterResponded = Event()
        self.sdrChannel = None
        self.mqttTopic = None
        self.transverterMutex.acquire()

        self._stepSize = 10000

        # Popup for mode selection
        self.modeWindow = QWidget()
        ui = modeSelector.Ui_main()
        ui.setupUi(self.modeWindow)
        ui.button_cw.clicked.connect(lambda: self.publish_mode("CW"))
        ui.button_usb.clicked.connect(lambda: self.publish_mode("USB"))
        ui.button_lsb.clicked.connect(lambda: self.publish_mode("LSB"))
        ui.button_tone.clicked.connect(lambda: self.publish_mode("Tone"))
        self.modeWindow.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup)

        # Popup for frequency entry
        self.freqWindow = QWidget()
        ui = freqWindow.Ui_main()
        ui.setupUi(self.freqWindow)
        ui.button_0.clicked.connect(lambda: self.handle_keypress(0))
        ui.button_1.clicked.connect(lambda: self.handle_keypress(1))
        ui.button_2.clicked.connect(lambda: self.handle_keypress(2))
        ui.button_3.clicked.connect(lambda: self.handle_keypress(3))
        ui.button_4.clicked.connect(lambda: self.handle_keypress(4))
        ui.button_5.clicked.connect(lambda: self.handle_keypress(5))
        ui.button_6.clicked.connect(lambda: self.handle_keypress(6))
        ui.button_7.clicked.connect(lambda: self.handle_keypress(7))
        ui.button_8.clicked.connect(lambda: self.handle_keypress(8))
        ui.button_9.clicked.connect(lambda: self.handle_keypress(9))
        ui.button_G.clicked.connect(lambda: self.handle_keypress("G"))
        ui.button_M.clicked.connect(lambda: self.handle_keypress("M"))
        ui.button_k.clicked.connect(lambda: self.handle_keypress("k"))
        ui.button_x.clicked.connect(lambda: self.handle_keypress("x"))
        ui.button_backspace.clicked.connect(lambda: self.handle_keypress(-1))
        ui.button_dp.clicked.connect(lambda: self.handle_keypress("."))

        self.enteredFreqLabel = ui.label_enteredFreq
        self.freqWindow.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup)

        self.freqButton.clicked.connect(self.open_freq_window)

        self.freq = None
        self.enteredFreqString = ""
        self.publishedFreq = None
        self.mode = None
        self.channel = None

        self.rxEnabled = False
        self.txEnabled = False

        self.enable_rx(False)
        self.enable_tx(False)

        # Can connect toggle RX to button
        # TX has to be handled at higher level as there must be <= 1 TX VFOs
        self.rxButton.clicked.connect(self.toggle_rx)

    def enable_rx(self, enabled: bool) -> None:
        if not self.transverter and enabled:
            logging.warning(
                f"Cannot enable RX on VFO {self.name}without transverter")
            return
        self.rxEnabled = enabled
        if self.rxEnabled:
            self.rxButton.setIcon(QIcon('resources/img/icon_rx_enabled.png'))
        else:
            self.rxButton.setIcon(QIcon('resources/img/icon_rx_disabled.png'))

    def toggle_rx(self):
        """ Toggles whether RX is enabled """
        if self.transverter:
            self.enable_rx(not self.rxEnabled)

    def enable_tx(self, enabled: bool) -> None:
        if not self.transverter and enabled:
            logging.warning(
                f"Cannot enable TX on VFO {self.name}without transverter")
            return
        self.txEnabled = enabled
        if self.txEnabled:
            self.txButton.setIcon(QIcon('resources/img/icon_tx_enabled.png'))
        else:
            self.txButton.setIcon(QIcon('resources/img/icon_tx_disabled.png'))

    def toggle_tx(self):
        """ Toggles whether TX is enabled """
        if self.transverter:
            self.enable_tx(not self.txEnabled)

    def set_transverter(self, transverter: Transverter) -> None:
        self.transverterMutex.acquire()
        self.transverter = transverter
        logging.info(f"VFO {self.name} now controlling transverter"
                     " \"{transverter.name}\"")
        self.transverterMutex.release()

    def handle_keypress(self, key):
        constants = {"G": 1e9, "M": 1e6, "k": 1e3, "x": 1}
        if key in ["G", "M", "k", "x"]:
            freq = float(self.enteredFreq) * constants[key]
            if self.set_freq(freq):
                # Successfully set frequency
                self.freqWindow.hide()
        elif key == -1:
            self.enteredFreq = self.enteredFreq[:-1]
            self.enteredFreqLabel.setText(self.enteredFreq)
        else:
            self.enteredFreq += str(key)
            self.enteredFreqLabel.setText(self.enteredFreq)

    def open_freq_window(self):
        self.enteredFreq = ""
        self.enteredFreqLabel.setText(self.enteredFreq)
        self.freqWindow.show()

    def set_freq(self, freq) -> bool:
        """ Attempts to set VFO to frequency. Returns True if it succeeds """
        if not self.transverter or \
                freq < self.transverter.minFreq or \
                freq > self.transverter.maxFreq:
            # Current transverter is unsuitable or we don't have one
            if not self.get_suitable_transverter(freq):
                # Can't find a suitable one
                return False
        # We now have a suitable transverter
        self.publish_freq(freq)
        logging.info(f"VFO {self.name} set to {readable_freq(freq)}")
        return True

    def get_suitable_transverter(self, freq) -> bool:
        """
        Attempts to find a transverter that can operate at a frequency and
        requests control of that transverter. Returns True if succeeds
        """
        # Have to find a suitable transverter
        candidates = self.networkHandler.get_supported_transverters(freq)
        if not candidates:
            self.warnings.add_status("No transverter found for {}".format(
                readable_freq(freq)))
            return False
        elif len(candidates) == 1:
            transverter = candidates[0]
            if transverter.channel:
                # @TODO How to behave if transverter is already controlled
                raise NotImplementedError
            else:
                if self.request_transverter_control(transverter):
                    return True
                else:
                    # Request failed
                    return False  # @TODO
        else:
            # TODO decide between multiple possibilities
            raise NotImplementedError

    def request_transverter_control(self, transverter: Transverter) -> bool:
        """ Requests control of a specific transverter """
        if self.transverter:
            self.surrender_transverter_control()

        assert self.transverter is None
        assert self.sdrChannel is None

        logging.info('Attempting to take control of transverter "{}"'.format(
            transverter.name))

        self.transverterMutex.release()

        self.mqtt.register_callback(
            "/{}/requestResponse".format(transverter.sdrMac),
            self.process_transverter_control_request_response,
            requiresMainThread=False)

        self.transverterResponded.clear()

        try:
            self.mqtt.publish(
                "/{}/requests".format(transverter.sdrMac),
                json.dumps({
                    "address": transverter.address,
                    "controllerName": NAME,
                    "controllerMac": get_mac(),
                    "vfo": self.name
                }))
            if not self.transverterResponded.wait(timeout=5):
                # Requested timed out
                self.warnings.add_warning(
                    NAME, "MQTT",
                    f"Timed out waiting for response from {transverter.sdrMac}"
                )
            else:
                # Got here so SDR responded, self.sdrChannel now either
                # contains the channel name if everything was successfuly
                # or None if it failed
                if self.sdrChannel:
                    self.transverter = transverter
                    self.mqttTopic = "/{}/channel{}".format(
                        transverter.sdrMac, self.sdrChannel)
                    self.mqtt.register_callback(self.mqttTopic, self.rx_status)
                else:
                    # SDR responded but refused connection
                    self.warnings.add_warning(
                        NAME, "MQTT",
                        f"Refused transverter control by {transverter.sdrMac}")

        finally:
            self.mqtt.remove_callback("/{}/requestResponse".format(
                transverter.sdrMac))
            self.transverterMutex.acquire()

        return bool(self.transverter)

    def process_transverter_control_request_response(self, msg):
        self.transverterMutex.acquire()
        self.transverterResponse = msg
        if (msg["status"] == "success" and msg["controllerMac"] == get_mac()
                and msg["vfo"] == self.name):
            self.sdrChannel = msg["channel"]

        self.transverterMutex.release()
        self.transverterResponded.set()

    def process_transverter_control_surrender_response(self, msg):
        self.transverterMutex.acquire()
        self.transverterResponse = msg
        if (msg["status"] == "success" and msg["controllerMac"] is None
                and msg["vfo"] is None):
            self.sdrChannel = None

        self.transverterMutex.release()
        self.transverterResponded.set()

    def surrender_transverter_control(self) -> None:
        """ Stops this VFO being the controller of a transverter """
        logging.info("Surrendering control of transverter {}".format(
            self.transverter.name))

        self.transverterMutex.release()

        self.mqtt.register_callback(
            "/{}/requestResponse".format(self.transverter.sdrMac),
            self.process_transverter_control_surrender_response,
            requiresMainThread=False)

        self.transverterResponded.clear()
        sdrMac = self.transverter.sdrMac

        try:
            self.mqtt.publish(
                "/{}/requests".format(sdrMac),
                json.dumps({
                    "address": self.transverter.address,
                    "controllerName": None,
                    "controllerMac": None,
                    "vfo": None
                }))
            if not self.transverterResponded.wait(timeout=5):
                # Requested timed out
                self.warnings.add_warning(
                    NAME, "MQTT",
                    f"Timed out waiting for response from {sdrMac}")
            else:
                # Got here so SDR responded, self.sdrChannel now either
                # contains None if everything was successfuly
                # or the old channel name if it failed
                if self.sdrChannel is None:
                    self.mqtt.remove_callback(self.mqttTopic)
                    self.sdrChannel = None
                    self.mqttTopic = None
                    self.transverter = None
                else:
                    # SDR responded but refused connection
                    self.warnings.add_warning(
                        NAME, "MQTT",
                        f"Refused release of transverter control by {sdrMac}")

        finally:
            self.mqtt.remove_callback("/{}/requestResponse".format(sdrMac))
            self.transverterMutex.acquire()

    def publish_freq(self, freq):

        x = {"freq": freq}

        self.mqtt.publish("{}/set".format(self.mqttTopic), json.dumps(x))
        self.publishedFreq = int(freq)

    def publish_mode(self, mode):
        x = {"mode": mode}

        self.mqtt.publish("{}/set".format(self.mqttTopic), json.dumps(x))
        self.modeWindow.hide()

    def increment(self):
        if self.transverter:
            self.publish_freq(self.publishedFreq + self._stepSize)

    def decrement(self):
        if self.transverter:
            self.publish_freq(self.publishedFreq - self._stepSize)

    def rx_status(self, msg):

        freq = msg['freq']
        mode = msg['mode']
        if (freq != self.freq):
            self.freq = freq

            # Easier to work with strings now
            freq = str(freq)

            prettyFreq = ""

            stepIndex = log10(self._stepSize)

            for i in range(len(freq)):
                if (i == stepIndex):
                    prettyFreq = "<u>" + freq[len(freq) - 1 -
                                              i] + "</u>" + prettyFreq
                else:
                    prettyFreq = freq[len(freq) - 1 - i] + prettyFreq

                if ((i + 1) % 3 == 0):
                    prettyFreq = "." + prettyFreq

            self.freqButton.setText(prettyFreq.lstrip("."))

            logging.debug(f"VFO {self.name} set to {readable_freq(self.freq)}")

        if mode != self.mode:
            if self.mode is None:
                self.modeButton.clicked.connect(self.modeWindow.show)

            self.mode = mode
            self.modeButton.setText(mode)

            if self.modeWindow.isVisible():
                self.modeWindow.hide()

            logging.debug(f"VFO {self.name} set to {self.mode}")