예제 #1
0
def initScores(container: QWidget):
    container.removeWidget(state["scoreWidget"])
    state["scoreWidget"].deleteLater()
    newScoreWidget = QWidget()
    newScoreWidget.hide()
    scoreLayout = QGridLayout()
    scoreLayout.setAlignment(Qt.AlignTop)
    diff1 = QLabel("Easy")
    scoreLayout.addWidget(diff1, 0, 1)
    for i, el in enumerate(state["highScores"]["easy"]):
        text = "{} {}".format(el[0], el[1])
        topPlayer = QLabel(text)
        scoreLayout.addWidget(topPlayer, i + 1, 1)
    diff2 = QLabel("Normal")
    scoreLayout.addWidget(diff2, 0, 2)
    for i, el in enumerate(state["highScores"]["normal"]):
        text = "{} {}".format(el[0], el[1])
        topPlayer = QLabel(text)
        scoreLayout.addWidget(topPlayer, i + 1, 2)
    diff3 = QLabel("Hard")
    scoreLayout.addWidget(diff3, 0, 3)
    for i, el in enumerate(state["highScores"]["hard"]):
        text = "{} {}".format(el[0], el[1])
        topPlayer = QLabel(text)
        scoreLayout.addWidget(topPlayer, i + 1, 3)
    newScoreWidget.setLayout(scoreLayout)
    state["scoreWidget"] = newScoreWidget
    container.addWidget(newScoreWidget)
class Folder(QWidget):
    def __init__(self, parent, open_file_call, folder_path):
        super().__init__(parent)
        self.folder_path = folder_path
        self.folder_open = False

        self.setStyleSheet(styles.file_path_button)

        self.main_v_layout = QVBoxLayout()
        self.main_v_layout.setSpacing(2)
        self.main_v_layout.setMargin(0)

        self.files = QWidget(self)
        self.files.hide()
        self.files.setStyleSheet(styles.files_list)
        self.files.setContentsMargins(15, 0, 0, 0)
        self.files_v_layout = QVBoxLayout()

        self.folder_btn = QPushButton(QIcon(icon.FOLDER_I()),
                                      self.folder_path.split(os.path.sep)[-1],
                                      self)
        self.folder_btn.clicked.connect(self.open_folder)

        self.main_v_layout.addWidget(self.folder_btn)

        _list = sorted(os.listdir(self.folder_path))

        for file in _list:
            file_absolute_path = self.folder_path + os.path.sep + file

            if os.path.isdir(file_absolute_path):
                self.files_v_layout.addWidget(
                    Folder(self.files, open_file_call,
                           self.folder_path + os.path.sep + file))
                continue

            if os.path.splitext(file_absolute_path)[1] != '.csv':
                continue
            file_btn = FilePathButton(self.files, file, file_absolute_path,
                                      open_file_call)
            self.files_v_layout.addWidget(file_btn)

        self.files.setLayout(self.files_v_layout)

        self.main_v_layout.addWidget(self.files)

        self.setLayout(self.main_v_layout)

    def open_folder(self):
        self.close() if self.folder_open else self.open()
        self.folder_open = not self.folder_open

    def open(self):
        self.files.show()
        self.folder_btn.setIcon(QIcon(icon.OPENED_FOLDER_I()))

    def close(self):
        self.files.hide()
        self.folder_btn.setIcon(QIcon(icon.FOLDER_I()))
예제 #3
0
 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
예제 #4
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()
예제 #5
0
 def build_export(self,
                  has_table: bool = True,
                  has_plot: bool = True) -> QWidget:
     """Construct the export layout but set it into a widget because we
      want to hide it."""
     export_layout = super().build_export(has_table, has_plot)
     export_widget = QWidget()
     export_widget.setLayout(export_layout)
     # Hide widget until MC is calculated
     export_widget.hide()
     return export_widget
예제 #6
0
def initStats(container: QWidget, stateInfo: dict):
    container.removeWidget(stateInfo["statsWidget"])
    stateInfo["statsWidget"].deleteLater()
    newstatsWidget = QWidget()
    newstatsWidget.hide()
    statsLayout = QVBoxLayout()
    for el in stateInfo["statistics"]:
        infoLabel = QLabel(el)
        statsLayout.addWidget(infoLabel)
    newstatsWidget.setLayout(statsLayout)
    container.addWidget(newstatsWidget)
    stateInfo["statsWidget"] = newstatsWidget
예제 #7
0
class ActionWidget(QWidget):

    _icon_picker: Optional[icon_picker.IconPicker] = None

    def __init__(self, parent: QWidget, manager: logic.DeviceManager):
        QWidget.__init__(self)

        self._manager = manager
        self._key_task = None

        self._action_widget = QWidget(self)
        self._action_widget.hide()
        layout = QGridLayout()

        self._icon_button = QPushButton(self._action_widget,
                                        clicked=self._icon_clicked)
        self._icon_button.setFixedSize(72, 72)
        layout.addWidget(self._icon_button, 0, 0)

        self._title = QLabel(self._action_widget)
        layout.addWidget(self._title, 0, 1)

        layout.addWidget(QLabel('Name'), 1, 0)
        self._name = QLineEdit(self._action_widget)
        layout.addWidget(self._name, 1, 1)

        self._action_widget.setLayout(layout)

        layout_container = QHBoxLayout()
        layout_container.addWidget(self._action_widget)
        self.setLayout(layout_container)

    def select(self, key_index):
        if key_index is not None:
            self._title.setText('Key ' + str(key_index))

            self._key_task = self._manager.config_screen.get_key_task(
                key_index)
            if self._key_task is None:
                self._name.setText(self._manager.make_unique_task_name('Task'))
            else:
                self._name.setText(self._key_task.name)

            self._action_widget.show()
        else:
            self._action_widget.hide()

    def _icon_clicked(self):
        if ActionWidget._icon_picker is None:
            ActionWidget._icon_picker = icon_picker.IconPicker(
                None, self._manager.get_icon_folder())

        ActionWidget._icon_picker.show()
예제 #8
0
class VarietyOptionWidget(QWidget):
    def __init__(self, *args, **kwargs):
        super(VarietyOptionWidget, self).__init__(*args, **kwargs)
        layout = QVBoxLayout()
        layout.setContentsMargins(QMargins(1, 1, 1, 1))
        opts_layout = QHBoxLayout()
        opts_layout.setContentsMargins(QMargins(0, 0, 0, 0))

        self.add_button = QPushButton("新建品种", self)
        opts_layout.addWidget(self.add_button)
        opts_layout.addStretch()
        layout.addLayout(opts_layout)

        # 新建品种的widget
        self.new_variety_widget = QWidget(self)
        add_layout = QGridLayout()
        add_layout.setContentsMargins(QMargins(100, 50, 100, 50))

        add_layout.addWidget(QLabel("所属类别:", self), 0, 0)
        self.belong_group = QComboBox(self)
        add_layout.addWidget(self.belong_group, 0, 1)

        add_layout.addWidget(QLabel("属交易所", self), 1, 0)
        self.belong_exchange = QComboBox(self)
        add_layout.addWidget(self.belong_exchange, 1, 1)

        add_layout.addWidget(QLabel("中文名称", self), 2, 0)
        self.zh_name = QLineEdit(self)
        add_layout.addWidget(self.zh_name, 2, 1)

        add_layout.addWidget(QLabel("交易代码", self), 3, 0)
        self.en_name = QLineEdit(self)
        add_layout.addWidget(self.en_name, 3, 1)

        self.commit_new_button = QPushButton("确定提交", self)
        add_layout.addWidget(self.commit_new_button, 4, 0, 1, 2)

        self.new_variety_widget.setLayout(add_layout)

        layout.addWidget(self.new_variety_widget)
        self.new_variety_widget.hide()  # 隐藏新建填写信息的界面

        self.variety_table = QTableWidget(self)
        layout.addWidget(self.variety_table)

        self.setLayout(layout)
예제 #9
0
    def _create_controls_section(self) -> QWidget:
        btn_allow = QPushButton('Allow')
        btn_allow.clicked.connect(self._on_allow_click)

        btn_block = QPushButton('Block')
        btn_block.clicked.connect(self._on_block_click)

        btn_reject = QPushButton('Reject')
        btn_reject.clicked.connect(self._on_reject_click)

        layout = QHBoxLayout()
        layout.addWidget(btn_allow)
        layout.addWidget(btn_block)
        layout.addWidget(btn_reject)
        layout.setContentsMargins(0, 0, 0, 0)

        controls_section = QWidget()
        controls_section.setLayout(layout)
        controls_section.hide()  # will show only on selection

        return controls_section
예제 #10
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())
예제 #11
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())
예제 #12
0
class ItemWidget(QWidget):
    ItemUpdated = Signal()

    _KEYS = (
        "type",
        "title",
        "authors",
        "authors_bais",
        "editors",
        "editors_bais",
        "abstract",
        "date",
        "publication",
        "volume",
        "issue",
        "pages",
        "year",
        "edition",
        "series",
        "publisher",
        "isbns",
        "institution",
        "degree",
        "texkey",
        "inspire_id",
        "arxiv_id",
        "dois",
        "urls",
        "files"
    )

    _FORMAT_TYPE = {
        "A": "Article",
        "B": "Book",
        "C": "Book Chapter",
        "N": "Note",
        "P": "Conference Proceedings",
        "R": "Report",
        "T": "Thesis"
    }

    _FORMAT_FUNCTIONS = {
        "type": lambda x: ItemWidget._FORMAT_TYPE.get(x, ""),
        "authors": "\n".join,
        "editors": "\n".join,
        "isbns": "\n".join,
        # Alternatively one could use
        # lambda x: "\n".join([a.split(",", 1)[0] for a in x])
        "inspire_id": lambda x: str(x) if x is not None else None,
        "year": lambda x: str(x) if x is not None else None,
        "dois": "\n".join,
        "urls": "\n".join
    }

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

        self._source = None
        self._table = None
        self._id = -1

        self._updating = False

        self._scroll = QScrollArea()
        # self._scroll.setBackgroundRole(QPalette.Base)
        self._scroll.setWidgetResizable(True)

        self._date = LineEdit()
        min_height = self._date.sizeHint().height()
        self._type = ComboBox()
        self._title = MultiLineTextEdit(min_height)
        self._authors = LineSplitTextEdit(min_height)
        self._editors = LineSplitTextEdit(min_height)
        self._abstract = MultiLineTextEdit(min_height)
        self._publication = LineEdit()
        self._volume = LineEdit()
        self._issue = LineEdit()
        self._pages = LineEdit()
        self._year = LineEdit()
        self._edition = LineEdit()
        self._series = LineEdit()
        self._publisher = LineEdit()
        self._isbns = LineSplitTextEdit(min_height)
        self._institution = LineEdit()
        self._degree = LineEdit()
        self._texkey = LineEdit()
        self._inspire_id = LineEdit()
        self._arxiv_id = LineEdit()
        self._dois = LineSplitTextEdit(min_height)
        self._urls = LineSplitTextEdit(min_height)
        self._files = FileList()

        for (k, v) in ItemWidget._FORMAT_TYPE.items():
            self._type.addItem(v, k)
        # self._type.currentIndexChanged[int].connect(self._RefreshTypeFields)

        bold_font = self._title.document().defaultFont()
        bold_font.setBold(True)
        self._title.document().setDefaultFont(bold_font)

        self._files.FileDropRequested.connect(self._DropFile)

        self._fields = {
            "type": self._type,
            "title": self._title,
            "authors": self._authors,
            "editors": self._editors,
            "abstract": self._abstract,
            "date": self._date,
            "publication": self._publication,
            "volume": self._volume,
            "issue": self._issue,
            "pages": self._pages,
            "year": self._year,
            "edition": self._edition,
            "series": self._series,
            "publisher": self._publisher,
            "isbns": self._isbns,
            "institution": self._institution,
            "degree": self._degree,
            "texkey": self._texkey,
            "inspire_id": self._inspire_id,
            "arxiv_id": self._arxiv_id,
            "dois": self._dois,
            "urls": self._urls
        }

        self._details_frame = QGroupBox()
        self._details_layout = QFormLayout()

        self._scroll_widget = QWidget()
        self._SetupScrollUI()
        self._scroll.setWidget(self._scroll_widget)
        self._scroll_widget.hide()

        self._save = QToolButton()
        self._save.setIcon(QIcon(icons.SAVE))
        self._save.clicked.connect(self._Save)
        self._reload = QToolButton()
        self._reload.setIcon(QIcon(icons.RELOAD))
        self._reload.clicked.connect(self.DisplayItem)
        self._file_add = QToolButton()
        self._file_add.setIcon(QIcon(icons.FILE_ADD))
        self._file_add.clicked.connect(self._AddFile)
        self._tool_widget = QWidget()
        self._SetupToolUI()

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self._scroll)
        layout.addWidget(self._tool_widget)
        self.setLayout(layout)
        self.Clear()

    def _SetupScrollUI(self):
        self._details_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
        self._details_layout.addRow("Journal", self._publication)
        self._details_layout.addRow("Editors", self._editors)
        self._details_layout.addRow("Volume", self._volume)
        self._details_layout.addRow("Issue", self._issue)
        self._details_layout.addRow("Pages", self._pages)
        self._details_layout.addRow("Edition", self._edition)
        self._details_layout.addRow("Series", self._series)
        self._details_layout.addRow("Publisher", self._publisher)
        self._details_layout.addRow("ISBNs", self._isbns)
        self._details_layout.addRow("Institution", self._institution)
        self._details_layout.addRow("Degree", self._degree)
        self._details_layout.addRow("Year", self._year)
        self._details_layout.setVerticalSpacing(0)
        self._details_frame.setLayout(self._details_layout)

        form_layout = QFormLayout()
        form_layout.setContentsMargins(0, 0, 0, 0)
        form_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
        form_layout.addRow("Type", self._type)
        form_layout.addRow("Title", self._title)
        form_layout.addRow("Authors", self._authors)
        form_layout.addRow("Abstract", self._abstract)
        form_layout.addRow("Date", self._date)
        form_layout.addRow("", self._details_frame)
        form_layout.addRow("BibTeX", self._texkey)
        form_layout.addRow("INSPIRE", self._inspire_id)
        form_layout.addRow("arXiv", self._arxiv_id)
        form_layout.addRow("DOIs", self._dois)
        form_layout.addRow("URLs", self._urls)
        form_layout.addRow("Files", self._files)
        form_layout.setVerticalSpacing(0)
        form = QWidget()
        form.setLayout(form_layout)

        scroll_layout = QVBoxLayout()
        scroll_layout.addWidget(form)
        scroll_layout.addStretch(1)

        self._scroll_widget.setLayout(scroll_layout)

    def _SetupToolUI(self):
        tool_layout = QHBoxLayout()
        tool_layout.setContentsMargins(0, 0, 0, 0)
        tool_layout.addWidget(self._file_add)
        tool_layout.addStretch(1)
        tool_layout.addWidget(self._reload)
        tool_layout.addWidget(self._save)
        self._tool_widget.setLayout(tool_layout)

        self._reload.setEnabled(False)
        self._save.setEnabled(False)
        self._file_add.setEnabled(False)

        for w in self._fields.values():
            w.Edited.connect(self._EnableRefreshSave)

    # def _RefreshTypeFields(self, index):
    #     pass

    def _EnableRefreshSave(self):
        self._reload.setEnabled(True)
        self._save.setEnabled(True)

    def _Save(self):
        data = {}
        for (k, w) in self._fields.items():
            data[k] = w.Read()

        authors_raw = [" ".join(a.split()) for a in data["authors"]]
        authors = []
        authors_bais = []
        for a in authors_raw:
            (n, b) = ItemWidget._ParseAuthor(a)
            authors.append(n)
            authors_bais.append(b)
        data["authors"] = authors
        data["authors_bais"] = authors_bais

        editors_raw = [" ".join(e.split()) for e in data["editors"]]
        editors = []
        editors_bais = []
        for e in editors_raw:
            (n, b) = ItemWidget._ParseAuthor(e)
            editors.append(n)
            editors_bais.append(b)
        data["editors"] = editors
        data["editors_bais"] = editors_bais

        if data["date"] is not None:
            try:
                data["date"] = ItemWidget._ParseDate(data["date"])
            except:
                del data["date"]

        if data["year"] is not None:
            try:
                data["year"] = datetime.strptime(data["year"], "%Y").strftime("%Y")
            except:
                del data["year"]

        if data["inspire_id"] is not None:
            try:
                data["inspire_id"] = int(data["inspire_id"])
            except:
                del data["inspire_id"]

        self._table.EditRow(self._id, data)

        self.ItemUpdated.emit()

    def _AddFile(self):
        files_dir = self._source.FilesDir()
        if files_dir is None:
            QMessageBox.critical(None, "Error", "Cannot access storage folder.")
            return

        (file_path, _) = QFileDialog.getOpenFileName(
            None, "Open Document", files_dir, "Documents (*.pdf *.djvu)"
        )
        if file_path == "":
            return
        file_name = os.path.basename(file_path)

        try:
            renaming = self._source.SaveFiles((file_path,))
        except:
            QMessageBox.critical(None, "Error", "Error while copying the file.")
        else:
            self._AppendFile(renaming.get(file_name, file_name))

    def _AppendFile(self, file_name):
        data = self._table.GetRow(self._id, ("files",))
        if file_name in data:
            return
        data["files"].append(file_name)

        # Here self._updating is used to prevent calls to DisplayItem()
        # triggered by an Updated() signal emitted by the database.
        self._updating = True
        self._table.EditRow(self._id, data)
        self._updating = False

        self._files.Write(data["files"])

    def _DropFile(self, file_name):
        data = self._table.GetRow(self._id, ("files",))
        data["files"] = [f for f in data["files"] if f != file_name]

        # Here self._updating is used to prevent calls to DisplayItem()
        # triggered by an Updated() signal emitted by the database.
        self._updating = True
        self._table.EditRow(self._id, data)
        self._updating = False

        self._files.Write(data["files"])

    def SetLocalSource(self, source):
        if self._source == source:
            return

        self._source = source
        self._SetTable(source.table)

    def SetTable(self, database_table):
        if self._table == database_table:
            return

        self._source = None
        self._SetTable(database_table)

    def _SetTable(self, database_table):
        if self._table is not None:
            self._table.Cleared.disconnect(self.Clear)

        self._table = database_table
        self._table.Cleared.connect(self.Clear)

        if self._source is None:
            self._files.setEnabled(False)
            self._files.SetFolder(None)
        else:
            self._files.setEnabled(True)
            try:
                self._files.SetFolder(self._source.FilesDir())
            except:
                self._files.SetFolder(None)

    def Clear(self):
        self._scroll_widget.hide()
        self._scroll.verticalScrollBar().hide()

    def DisplayItem(self, id_=False):
        if self._updating:
            return

        if id_:
            self._id = id_

        if self._id == -1:
            self.Clear()
            self._reload.setEnabled(False)
            self._save.setEnabled(False)
            self._file_add.setEnabled(False)
            return

        record = self._table.GetRow(self._id, ItemWidget._KEYS)
        authors = []
        for (a, b) in zip(record["authors"], record["authors_bais"]):
            if b is None:
                authors.append(a)
            else:
                authors.append(a + " (" + b + ")")
        record["authors"] = authors
        editors = []
        for (e, b) in zip(record["editors"], record["editors_bais"]):
            if b is None:
                editors.append(e)
            else:
                editors.append(e + " (" + b + ")")
        record["editors"] = editors
        for (k, v) in ItemWidget._FORMAT_FUNCTIONS.items():
            record[k] = v(record[k])

        for (k, w) in self._fields.items():
            w.Write(record[k])
        self._files.Write(record["files"])

        self._scroll.verticalScrollBar().show()
        self._scroll_widget.show()

        self._reload.setEnabled(False)
        self._save.setEnabled(False)
        if self._source is not None:
            self._file_add.setEnabled(True)

    @staticmethod
    def _ParseAuthor(author):
        split = author.split("(", 1)
        author = split[0].strip()
        if len(split) == 1:
            return(author, None)

        bai = split[1].split(")", 1)
        if len(bai) == 1:
            return(author, None)

        bai = bai[0].replace(" ", "")
        # The sanity check on the BAI could be further refined

        return(author, bai)

    @staticmethod
    def _ParseDate(date_string):
        n_dashes = date_string.count("-")
        if n_dashes == 2:
            return datetime.strptime(date_string, "%Y-%m-%d").strftime("%Y-%m-%d")
        if n_dashes == 1:
            return datetime.strptime(date_string, "%Y-%m").strftime("%Y-%m")
        return datetime.strptime(date_string, "%Y").strftime("%Y")
예제 #13
0
class GoalPreviewerLayout(QVBoxLayout):
    def __init__(self):

        super().__init__()

        # ─────────────────── Check boxes ────────────────── #

        self.code_mode_checkbox = QCheckBox('L∃∀N mode')
        checkbox_lyt = QHBoxLayout()
        checkbox_lyt.addStretch()
        checkbox_lyt.addWidget(self.code_mode_checkbox)

        self.code_mode_checkbox.clicked.connect(self.toggle_code_mode)

        # ───────────────── Friendly widget ──────────────── #

        self.friendly_wgt = QWidget()
        friendly_wgt_lyt = QVBoxLayout()
        friendly_wgt_lyt.setContentsMargins(0, 0, 0, 0)

        propobj_lyt = QHBoxLayout()
        objects, properties = QListWidget(), QListWidget()
        objects_lyt, properties_lyt = QVBoxLayout(), QVBoxLayout()
        objects_lyt.addWidget(QLabel('Objects:'))
        properties_lyt.addWidget(QLabel('Properties:'))
        objects.setFont(QFont('Fira Code'))
        properties.setFont(QFont('Fira Code'))
        objects.addItems(['X : a set', 'x : X'])
        properties.addItems(['X is compact'])
        objects_lyt.addWidget(objects)
        properties_lyt.addWidget(properties)
        propobj_lyt.addLayout(objects_lyt)
        propobj_lyt.addLayout(properties_lyt)

        target_wgt = QLineEdit('Shit f**k X is continuous over Riemann')
        target_wgt.setFont(QFont('Fira Code'))

        friendly_wgt_lyt.addLayout(propobj_lyt)
        friendly_wgt_lyt.addWidget(QLabel('Target:'))
        friendly_wgt_lyt.addWidget(target_wgt)

        self.friendly_wgt.setLayout(friendly_wgt_lyt)

        # ─────────────────── Code widget ────────────────── #

        self.code_wgt = QTextEdit()
        self.code_wgt.setReadOnly(True)
        self.code_wgt.setFont(QFont('Menlo'))
        self.code_wgt.setText('Lean code goes here')

        # ──────────────────── Organize ──────────────────── #
        self.code_mode_checkbox.setChecked(False)
        self.friendly_wgt.show()
        self.code_wgt.hide()

        self.addWidget(self.friendly_wgt)
        self.addWidget(self.code_wgt)
        self.addLayout(checkbox_lyt)

    @Slot()
    def toggle_code_mode(self):

        if self.code_mode_checkbox.isChecked():
            self.friendly_wgt.hide()
            self.code_wgt.show()
        else:
            self.friendly_wgt.show()
            self.code_wgt.hide()
예제 #14
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}")
예제 #15
0
class Application(QApplication):
    def __init__(self):
        super().__init__()

        self.node_set = NodeSet()
        self.parent = QWidget()
        self.parent.hide()
        self.parent.setWindowFlags(self.parent.windowFlags() & ~QtCore.Qt.Tool)

        self.system_tray = SystemTray(self.parent, self.node_set)

        self.setQuitOnLastWindowClosed(False)

        self.aboutToQuit.connect(self.quit_app)

        self.system_tray.show()

        self.system_tray.showMessage('Nodes starting...',
                                     'Bitcoin and Lightning are syncing')

        self.node_set.bitcoin.file.file_watcher.fileChanged.connect(
            self.check_restart_required)
        self.node_set.lnd.file.file_watcher.fileChanged.connect(
            self.check_restart_required)

        self.timer = QTimer(self)
        self.timer.start(1000)
        self.timer.timeout.connect(self.check_restart_required)
        self.timer.singleShot(1000, self.check_version)

    def check_restart_required(self):
        if self.node_set.bitcoin.restart_required or self.node_set.lnd.restart_required:
            pass
        else:
            pass

    def check_version(self):
        latest_version = LauncherSoftware().get_latest_release_version()
        if latest_version is None:
            return
        latest_major, latest_minor, latest_bugfix = latest_version.split('.')
        major, minor, bugfix = NODE_LAUNCHER_RELEASE.split('.')

        major_upgrade = latest_major > major

        minor_upgrade = (latest_major == major and latest_minor > minor)

        bugfix_upgrade = (latest_major == major and latest_minor == minor
                          and latest_bugfix > bugfix)

        if major_upgrade or minor_upgrade or bugfix_upgrade:
            message_box = QMessageBox(self)
            message_box.setTextFormat(Qt.RichText)
            message_box.setText(UPGRADE)
            message_box.setInformativeText(
                f'Your version: {NODE_LAUNCHER_RELEASE}\n'
                f'New version: {latest_version}')
            message_box.exec_()

    @Slot()
    def quit_app(self):
        self.node_set.lnd.process.terminate()
        self.node_set.lnd.process.waitForFinished(2000)
        self.node_set.bitcoin.process.terminate()
        self.node_set.bitcoin.process.waitForFinished(20000)
        self.node_set.bitcoin.process.kill()

        QCoreApplication.exit(0)
예제 #16
0
class ScaleWidget(QWidget):
    stretches_list = ['powerdist', 'asinh', 'contrastbias', 'histogram', 'linear',
                      'log', 'power', 'sinh', 'sqrt', 'square']
    intervals_list = ['zscale', 'minmax', 'manual', 'percentile', 'asymetric']

    def __init__(self, parent, scales_model, cmap_model):
        QWidget.__init__(self, parent)
        self.parent = parent
        self.scalesModel = scales_model
        self.cmapModel = cmap_model
        self.ignore_signals = False
        layout = QVBoxLayout()
        layout.setSpacing(0)

        # pernament linear stretch
        self.perm_linear_widget = self.createLinearSliders()
        layout.addWidget(QLabel('Linear'))
        layout.addWidget(self.perm_linear_widget)

        # comboboxes
        self.combobox_widget = QWidget()
        self.combobox_layout = self.createComboboxes()
        self.combobox_widget.setLayout(self.combobox_layout)
        layout.addWidget(self.combobox_widget)

        # Stretch
        self.stretch_sliders_widget = QWidget()
        self.stretch_sliders_layout = self.createStretchStackedLayout()
        self.stretch_sliders_widget.setLayout(self.stretch_sliders_layout)
        layout.addWidget(self.stretch_sliders_widget)

        # Interval
        self.interval_sliders_widget = QWidget()
        self.interval_sliders_layout = self.createIntervalStackedLayout()
        self.interval_sliders_widget.setLayout(self.interval_sliders_layout)
        layout.addWidget(self.interval_sliders_widget)

        self.setLayout(layout)

        self.adjustCombos()
        self.adjustCmapCombo()
        self.adjustSliders()

        self.scalesModel.observe(lambda change: self.onScaleModelChange(change))
        self.cmapModel.observe(lambda change: self.onCmapModelChange(change))

    def createStretchStackedLayout(self):
        self.stretchStackedLayout = QStackedLayout()
        asinh = self.createAsinhParamsSliders()
        contrastbias = self.createContrastbiasParamsSliders()
        histogram = QLabel('')
        linear = QLabel('')
        log = self.createLogSliders()
        powerdist = self.createPowerdistSliders()
        power = self.createPowerSliders()
        sinh = self.createSinhSliders()
        sqrt = QLabel('')
        square = QLabel('')
        self.stretchStackedLayout.addWidget(powerdist)
        self.stretchStackedLayout.addWidget(asinh)
        self.stretchStackedLayout.addWidget(contrastbias)
        self.stretchStackedLayout.addWidget(histogram)
        self.stretchStackedLayout.addWidget(linear)
        self.stretchStackedLayout.addWidget(log)
        self.stretchStackedLayout.addWidget(power)
        self.stretchStackedLayout.addWidget(sinh)
        self.stretchStackedLayout.addWidget(sqrt)
        self.stretchStackedLayout.addWidget(square)

        return self.stretchStackedLayout

    def createIntervalStackedLayout(self):
        self.intervalStackedLayout = QStackedLayout()

        manual = self.createManualParamsSliders()
        percentile = self.createPercentileParamsSliders()
        asymetric = self.createAsymetricParamsSliders()
        zscale = self.createZscaleParamsSliders()
        self.intervalStackedLayout.addWidget(zscale)
        self.intervalStackedLayout.addWidget(QLabel(""))
        self.intervalStackedLayout.addWidget(manual)
        self.intervalStackedLayout.addWidget(percentile)
        self.intervalStackedLayout.addWidget(asymetric)

        return self.intervalStackedLayout

    def createManualParamsSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.manual_vmin = FloatSlider(min=0.0, max=30000.0)
        self.manual_vmax = IntSlider(min=0, max=30000)

        self.manual_vmin.valueChanged.connect(lambda val=vars: self.onSliderChange('interval_manual_vmin', val))
        self.manual_vmax.valueChanged.connect(lambda val=vars: self.onSliderChange('interval_manual_vmax', val))

        layout.addRow('vmin', self.manual_vmin)
        layout.addRow('vmax', self.manual_vmax)
        widget.setLayout(layout)

        return widget

    def createPercentileParamsSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.percentile_percentile = FloatSlider(min=0.1, max=2.0)
        self.percentile_nsamples = IntSlider(min=1, max=2000)

        self.percentile_percentile.valueChanged.connect(
            lambda val=vars: self.onSliderChange('interval_percentile_percentile', val))
        self.percentile_nsamples.valueChanged.connect(
            lambda val=vars: self.onSliderChange('interval_percentile_nsamples', val))

        layout.addRow('percentile', self.percentile_percentile)
        layout.addRow('samples', self.percentile_nsamples)
        widget.setLayout(layout)

        return widget

    def createAsymetricParamsSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.asymetric_lpercentile = FloatSlider(min=0.0, max=2.0)
        self.asymetric_upercentile = FloatSlider(min=0.0, max=2.0)
        self.asymetric_nsamples = IntSlider(min=0, max=2000)

        self.asymetric_lpercentile.valueChanged.connect(
            lambda val=vars: self.onSliderChange('interval_asymetric_lower_percentile', val))
        self.asymetric_upercentile.valueChanged.connect(
            lambda val=vars: self.onSliderChange('interval_asymetric_upper_percentile', val))
        self.asymetric_nsamples.valueChanged.connect(
            lambda val=vars: self.onSliderChange('interval_asymetric_nsamples', val))

        layout.addRow("l_percentile", self.asymetric_lpercentile)
        layout.addRow("u_percentile", self.asymetric_upercentile)
        layout.addRow("samples", self.asymetric_nsamples)

        widget.setLayout(layout)
        widget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)

        return widget

    def createZscaleParamsSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.zscale_nsamples = IntSlider(min=0, max=2000)
        self.zscale_contrast = FloatSlider(min=0.0, max=2.0)
        self.zscale_mreject = FloatSlider(min=0.0, max=2.0)
        self.zscale_minpixels = IntSlider(min=0.0, max=20)
        self.zscale_krej = FloatSlider(min=0.0, max=5.0)
        self.zscale_miterations = IntSlider(min=0, max=20)

        self.zscale_nsamples.valueChanged.connect(lambda val=vars: self.onSliderChange('interval_zscale_nsamples', val))
        self.zscale_contrast.valueChanged.connect(lambda val=vars: self.onSliderChange('interval_zscale_contrast', val))
        self.zscale_mreject.valueChanged.connect(lambda val=vars: self.onSliderChange('interval_zscale_maxreject', val))
        self.zscale_minpixels.valueChanged.connect(
            lambda val=vars: self.onSliderChange('interval_zscale_minpixels', val))
        self.zscale_krej.valueChanged.connect(lambda val=vars: self.onSliderChange('interval_zscale_krej', val))
        self.zscale_miterations.valueChanged.connect(
            lambda val=vars: self.onSliderChange('interval_zscale_maxiterations', val))

        layout.addRow("samples", self.zscale_nsamples)
        layout.addRow("contrast", self.zscale_contrast)
        layout.addRow("max reject", self.zscale_mreject)
        layout.addRow("pixels", self.zscale_minpixels)
        layout.addRow("krej", self.zscale_krej)
        layout.addRow("m_iterations", self.zscale_miterations)

        widget.setLayout(layout)

        return widget

    def createComboboxes(self):
        layout = QHBoxLayout()

        self.stretch_combobox = QComboBox()
        self.stretch_combobox.setFocusPolicy(Qt.NoFocus)
        self.stretch_combobox.addItems(self.stretches_list)

        self.interval_combobox = QComboBox()
        self.interval_combobox.setFocusPolicy(Qt.NoFocus)
        self.interval_combobox.addItems(self.intervals_list)

        self.color_combobox = QComboBox()
        self.color_combobox.setFocusPolicy(Qt.NoFocus)
        self.color_combobox.addItems(self.cmapModel.colormaps.keys())
        self.color_combobox.setCurrentText(self.cmapModel.cmap_idx)

        layout.addWidget(self.stretch_combobox)
        layout.addWidget(self.interval_combobox)
        layout.addWidget(self.color_combobox)

        self.stretch_combobox.activated.connect(lambda activated: self.on_select_stretch())
        self.interval_combobox.activated.connect(lambda activated: self.on_select_interval())
        self.color_combobox.activated.connect(lambda activated: self.on_select_cmap())
        return layout

    # For functionalty below, see FitsPlotterControlled.scale_from_model()
    # def fitsNormalization(self, stretch, interval):
    #     self.parent.fits_image.set_normalization(stretch=stretch,
    #                                       interval=interval,
    #                                       stretchkwargs=self.scalesModel.dictionary[stretch],
    #                                       intervalkwargs=self.scalesModel.dictionary[interval])
    #
    #     self.parent.fits_image.invalidate()
    #     self.parent.updateFitsInWidgets()

    def createAsinhParamsSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.asinh_a = FloatSlider(min=0.1, max=2.0)
        self.asinh_a.valueChanged.connect(
            lambda val=vars: self.onSliderChange('stretch_asinh_a', val))

        layout.addRow('a', self.asinh_a)
        widget.setLayout(layout)

        return widget

    def createContrastbiasParamsSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.contrast_contrast = FloatSlider(min=0.0, max=4.0)
        self.contrast_bias = FloatSlider(min=0.0, max=2.0)

        self.contrast_contrast.valueChanged.connect(
            lambda val=vars: self.onSliderChange('stretch_contrastbias_contrast', val))
        self.contrast_bias.valueChanged.connect(lambda val=vars: self.onSliderChange('stretch_contrastbias_bias', val))

        layout.addRow('contrast', self.contrast_contrast)
        layout.addRow('bias', self.contrast_bias)
        widget.setLayout(layout)

        return widget

    def createLinearSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.linear_slope = FloatSlider(min=0.1, max=3.0)
        self.linear_intercept = FloatSlider(min=-1.0, max=1.0)

        self.linear_slope.valueChanged.connect(lambda val=vars: self.onSliderChange('stretch_linear_slope', val))
        self.linear_intercept.valueChanged.connect(
            lambda val=vars: self.onSliderChange('stretch_linear_intercept', val))

        layout.addRow('slope', self.linear_slope)
        layout.addRow('intercept', self.linear_intercept)
        widget.setLayout(layout)

        return widget

    def createLogSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.log_a = FloatSlider(min=0.1, max=2000.0)
        self.log_a.valueChanged.connect(lambda val=vars: self.onSliderChange('stretch_log_a', val))

        layout.addRow('a', self.log_a)
        widget.setLayout(layout)

        return widget

    def createPowerdistSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.powerdist_a = FloatSlider(min=10.0, max=2000.0)
        self.powerdist_a.valueChanged.connect(lambda val=vars: self.onSliderChange('stretch_powerdist_a', val))

        layout.addRow('a', self.powerdist_a)
        widget.setLayout(layout)

        return widget

    def createPowerSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.power_a = FloatSlider(min=0.1, max=2.0)
        self.power_a.valueChanged.connect(lambda val=vars: self.onSliderChange('stretch_power_a', val))

        layout.addRow('a', self.power_a)
        widget.setLayout(layout)

        return widget

    def createSinhSliders(self):
        widget = QWidget()
        layout = QFormLayout()
        layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)

        self.sinh_a = FloatSlider(min=0.1, max=1.0)
        self.sinh_a.valueChanged.connect(lambda val=vars: self.onSliderChange('stretch_sinh_a', val))

        layout.addRow('a', self.sinh_a)
        widget.setLayout(layout)

        return widget

    def onSliderChange(self, param, value):
        if self.ignore_signals:
            return
        self.ignore_signals = True
        try:
            setattr(self.scalesModel, param, value)
        finally:
            self.ignore_signals = False

    def onScaleModelChange(self, change):
        if self.ignore_signals:  # avoid selfupdate on moving sliders
            return
        self.adjustCombos()
        self.adjustSliders()

    def onCmapModelChange(self, change):
        self.adjustCmapCombo()

    def on_select_stretch(self):
        self.scalesModel.selected_stretch = self.stretch_combobox.currentText()

    def on_select_interval(self):
        self.scalesModel.selected_interval = self.interval_combobox.currentText()

    def on_select_cmap(self):
        self.cmapModel.cmap_idx = self.color_combobox.currentText()

    def adjustCombos(self):
        """Select layout page from model and set scale combos value"""
        self.stretch_combobox.setCurrentText(self.scalesModel.selected_stretch)
        self.interval_combobox.setCurrentText(self.scalesModel.selected_interval)
        try:
            self.stretchStackedLayout.setCurrentIndex(
                self.stretches_list.index(self.scalesModel.selected_stretch))
        except ValueError:
            pass
        try:
            self.intervalStackedLayout.setCurrentIndex(
                self.intervals_list.index(self.scalesModel.selected_interval))
        except ValueError:
            pass
        try:
            self.adjustWidgetHeight(self.scalesModel.selected_stretch, self.scalesModel.selected_interval)
        except ValueError:
            pass

        # self.fitsNormalization(self.stretch_combobox.currentText(), self.interval_combobox.currentText())

    def adjustWidgetHeight(self, stretch, interval):
        self.perm_linear_widget.setMaximumHeight(100)
        self.combobox_widget.setMaximumHeight(40)

        widget_height = 160
        if stretch == 'linear' or stretch == 'histogram' or stretch == 'sqrt' or stretch == 'square':
            self.stretch_sliders_widget.hide()
        else:
            self.stretch_sliders_widget.show()
            if stretch == 'contrastbias' or stretch == 'linear':
                widget_height = widget_height + 100
                self.stretch_sliders_widget.setMaximumHeight(100)
            else:
                widget_height = widget_height + 50
                self.stretch_sliders_widget.setMaximumHeight(50)

        if interval == 'minmax':
            self.interval_sliders_widget.hide()
        else:
            self.interval_sliders_widget.show()
            if interval == 'asymetric':
                widget_height = widget_height + 120
                self.interval_sliders_widget.setMaximumHeight(120)
            elif interval == 'zscale':
                widget_height = widget_height + 210
                self.interval_sliders_widget.setMaximumHeight(210)
            else:
                widget_height = widget_height + 80
                self.interval_sliders_widget.setMaximumHeight(80)

        self.setMaximumHeight(widget_height)

    def adjustCmapCombo(self):
        """Set combo value"""
        self.color_combobox.setCurrentText(self.cmapModel.cmap_idx)

    def adjustSliders(self):
        ignore_signals = self.ignore_signals
        self.ignore_signals = True
        try:
            self.manual_vmin.set_value_from_settings(self.scalesModel.interval_manual_vmin)
            self.manual_vmax.set_value_from_settings(self.scalesModel.interval_manual_vmax)
            self.percentile_percentile.set_value_from_settings(self.scalesModel.interval_percentile_percentile)
            self.percentile_nsamples.set_value_from_settings(self.scalesModel.interval_percentile_nsamples)
            self.asymetric_lpercentile.set_value_from_settings(self.scalesModel.interval_asymetric_lower_percentile)
            self.asymetric_upercentile.set_value_from_settings(self.scalesModel.interval_asymetric_upper_percentile)
            self.asymetric_nsamples.set_value_from_settings(self.scalesModel.interval_asymetric_nsamples)
            self.zscale_nsamples.set_value_from_settings(self.scalesModel.interval_zscale_nsamples)
            self.zscale_contrast.set_value_from_settings(self.scalesModel.interval_zscale_contrast)
            self.zscale_mreject.set_value_from_settings(self.scalesModel.interval_zscale_maxreject)
            self.zscale_minpixels.set_value_from_settings(self.scalesModel.interval_zscale_minpixels)
            self.zscale_krej.set_value_from_settings(self.scalesModel.interval_zscale_krej)
            self.zscale_miterations.set_value_from_settings(self.scalesModel.interval_zscale_maxiterations)
            self.asinh_a.set_value_from_settings(self.scalesModel.stretch_asinh_a)
            self.contrast_contrast.set_value_from_settings(self.scalesModel.stretch_contrastbias_contrast)
            self.contrast_bias.set_value_from_settings(self.scalesModel.stretch_contrastbias_bias)
            self.linear_slope.set_value_from_settings(self.scalesModel.stretch_linear_slope)
            self.linear_intercept.set_value_from_settings(self.scalesModel.stretch_linear_intercept)
            self.log_a.set_value_from_settings(self.scalesModel.stretch_log_a)
            self.powerdist_a.set_value_from_settings(self.scalesModel.stretch_powerdist_a)
            self.power_a.set_value_from_settings(self.scalesModel.stretch_power_a)
            self.sinh_a.set_value_from_settings(self.scalesModel.stretch_sinh_a)
        finally:
            self.ignore_signals = ignore_signals

    def writeSlidersValues(self):
        # TODO: Move Save/Load settings to model object
        settings = QSettings()
        settings.beginGroup("Sliders")
        settings.setValue("asinh/a", self.scalesModel.stretch_asinh_a)
        settings.setValue("contrast/contrast", self.scalesModel.stretch_contrastbias_contrast)
        settings.setValue("contrast/bias", self.scalesModel.stretch_contrastbias_bias)
        settings.setValue("linear/slope", self.scalesModel.stretch_linear_slope)
        settings.setValue("linear/intercept", self.scalesModel.stretch_linear_intercept)
        settings.setValue("log/a", self.scalesModel.stretch_log_a)
        settings.setValue("powerdist/a", self.scalesModel.stretch_powerdist_a)
        settings.setValue("power/a", self.scalesModel.stretch_power_a)
        settings.setValue("sinh/a", self.scalesModel.stretch_sinh_a)

        settings.setValue("manual/vmin", self.scalesModel.interval_manual_vmin)
        settings.setValue("manual/vmax", self.scalesModel.interval_manual_vmax)
        settings.setValue("percentile/percentile", self.scalesModel.interval_percentile_percentile)
        settings.setValue("percentile/nsamples", self.scalesModel.interval_percentile_nsamples)
        settings.setValue("asymetric/lpercentile", self.scalesModel.interval_asymetric_lower_percentile)
        settings.setValue("asymetric/upercentile", self.scalesModel.interval_asymetric_upper_percentile)
        settings.setValue("asymetric/nsamples", self.scalesModel.interval_asymetric_nsamples)
        settings.setValue("zscale/contrast", self.scalesModel.interval_zscale_contrast)
        settings.setValue("zscale/nsamples", self.scalesModel.interval_zscale_nsamples)
        settings.setValue("zscale/maxreject", self.scalesModel.interval_zscale_maxreject)
        settings.setValue("zscale/minpixels", self.scalesModel.interval_zscale_minpixels)
        settings.setValue("zscale/krej", self.scalesModel.interval_zscale_krej)
        settings.setValue("zscale/maxiterations", self.scalesModel.interval_zscale_maxiterations)

        settings.setValue("cmap", self.cmapModel.cmap_idx)
        settings.setValue("stretch", self.scalesModel.selected_stretch)
        settings.setValue("interval", self.scalesModel.selected_interval)

        # settings.setValue("cmap", self.color_combobox.currentIndex())

        settings.endGroup()

    def readSlidersValues(self):
        settings = QSettings()
        settings.beginGroup("Sliders")

        self.setModelValue('cmap_idx', settings.value("cmap"), model=self.cmapModel)
        self.setModelValue('selected_stretch', settings.value("stretch"))
        self.setModelValue('selected_interval', settings.value("interval"))
        # self.color_combobox.setCurrentIndex(settings.value("cmap", 3))

        self.setModelValue('stretch_asinh_a', float(settings.value('asinh/a')))
        self.setModelValue('stretch_contrastbias_contrast', float(settings.value('contrast/contrast')))
        self.setModelValue('stretch_contrastbias_bias', float(settings.value('contrast/bias')))
        self.setModelValue('stretch_linear_slope', float(settings.value('linear/slope')))
        self.setModelValue('stretch_linear_intercept', float(settings.value('linear/intercept')))
        self.setModelValue('stretch_log_a', float(settings.value('log/a')))
        self.setModelValue('stretch_powerdist_a', float(settings.value('powerdist/a')))
        self.setModelValue('stretch_power_a', float(settings.value('power/a')))
        self.setModelValue('stretch_sinh_a', float(settings.value('sinh/a')))

        self.setModelValue('interval_manual_vmin', float(settings.value('manual/vmin')))
        self.setModelValue('interval_manual_vmax', int(settings.value('manual/vmax')))
        self.setModelValue('interval_percentile_percentile', float(settings.value('percentile/percentile')))
        self.setModelValue('interval_percentile_nsamples', int(settings.value('percentile/nsamples')))
        self.setModelValue('interval_asymetric_lower_percentile', float(settings.value('asymetric/lpercentile')))
        self.setModelValue('interval_asymetric_upper_percentile', float(settings.value('asymetric/upercentile')))
        self.setModelValue('interval_asymetric_nsamples', int(settings.value('asymetric/nsamples')))
        self.setModelValue('interval_zscale_nsamples', int(settings.value('zscale/nsamples')))
        self.setModelValue('interval_zscale_contrast', float(settings.value('zscale/contrast')))
        self.setModelValue('interval_zscale_maxreject', float(settings.value('zscale/maxreject')))
        self.setModelValue('interval_zscale_minpixels', int(settings.value('zscale/minpixels')))
        self.setModelValue('interval_zscale_krej', float(settings.value('zscale/krej')))
        self.setModelValue('interval_zscale_maxiterations', int(settings.value('zscale/maxiterations')))
        settings.endGroup()

    def setModelValue(self, model_key, value, model=None):
        if model is None:
            model = self.scalesModel
        try:
            setattr(model, model_key, value)
            pass
        except TraitError:
            if value is not None:
                print(f'Attempt to set {model_key} := {value} caused exception. Ignored')
예제 #17
0
class mainWindow(QObject):

    signalRun = Signal(SpiderThread)
    signalRunApi = Signal(apiTester)
    def __init__(self):
        QObject.__init__(self)  # must init parent QObject,if you want to use signal
        self.widget = QWidget()
        self.ipLabel = QLabel(self.widget)
        self.thread = QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.thread)
        self.man = SpiderThread()
        self.api = apiTester()
        # 1 2:loop 3: time
        self.testStatus = [False,1,0,"ip","mac"]

        # advance config
        self.findCoreStop = True
        self.cleanCacheSet = False
        self.useApiTest = False
        self.showTestProgress = True
        self.saveTestLog = False
        self.saveLogPath = ""
        self.chromePath =  ""
        self.showChrome = False
        self.coreDumpPath = ""

        # ui form
        self.passwordLabel = QLabel(self.widget)

        self.ipLineEdit = QLineEdit(self.widget)
        self.passwordLineEdit = QLineEdit(self.widget)

        self.startBtn = QPushButton(self.widget)
        self.stopBtn = QPushButton(self.widget)

        self.messageBox = QTextEdit(self.widget)
        self.messageBox.setReadOnly(True)

        self.userLabel = QLabel(self.widget)
        self.userLineEdit = QLineEdit(self.widget)

        self.intervalLabel = QLabel(self.widget)
        self.intervalSpinBox = QSpinBox(self.widget)

        self.loopLabel = QLabel(self.widget)
        self.loopSpinBox = QSpinBox(self.widget)

        self.intervalSpinBox.setRange(0,9999)
        self.loopSpinBox.setRange(1,9999)

        self.radioReboot = QRadioButton(self.widget)
        self.radioProvision = QRadioButton(self.widget)
        self.radioFactory = QRadioButton(self.widget)

        # self.apiCheckBox = QCheckBox(self.widget)

        self.menu = QMenu()
        self.gxpAction = QAction("Classic UI")
        self.grp2602Action = QAction("Ant Design UI")
        self.gxpAction.setCheckable(True)
        self.grp2602Action.setCheckable(True)
        self.menu.addAction(self.gxpAction)
        self.menu.addAction(self.grp2602Action)
        self.webLabel = QLabel(self.widget)
        self.webBtn = QPushButton(self.widget)
        self.webBtn.setMenu(self.menu)
        self.clearBtn = QPushButton(self.widget)
        self.messageList = deque()
        self.timer = QTimer()


        self.advanceBtn = QPushButton(self.widget)
        self.infoLabel = QLabel(self.widget)

        # provision widget
        self.provWidget = QWidget(self.widget)
        self.ver1Label = QLabel(self.provWidget)
        self.ver2Label = QLabel(self.provWidget)
        self.ver3Label = QLabel(self.provWidget)
        self.ver1LineEdit = QLineEdit(self.provWidget)
        self.ver2LineEdit = QLineEdit(self.provWidget)
        self.ver3LineEdit = QLineEdit(self.provWidget)

        self.dir1Label = QLabel(self.provWidget)
        self.dir2Label = QLabel(self.provWidget)
        self.dir3Label = QLabel(self.provWidget)
        self.dir1LineEdit = QLineEdit(self.provWidget)
        self.dir2LineEdit = QLineEdit(self.provWidget)
        self.dir3LineEdit = QLineEdit(self.provWidget)

        self.radioHttp = QRadioButton(self.provWidget)
        self.radioHttps = QRadioButton(self.provWidget)
        self.radioTftp = QRadioButton(self.provWidget)
        self.radioFtp = QRadioButton(self.provWidget)
        self.radioFtps = QRadioButton(self.provWidget)
        self.radioWindow = QRadioButton(self.provWidget)

        # advance widget
        self.advanceWidget = QWidget()
        self.checkCoreBox = QCheckBox(self.advanceWidget)
        self.cleanCache = QCheckBox(self.advanceWidget)
        self.checkSaveLogBox = QCheckBox(self.advanceWidget)
        self.selectDirBtn = QPushButton(self.advanceWidget)
        self.saveDirLabel = QLabel(self.advanceWidget)
        self.saveDirLabel.setStyleSheet("background:white")
        self.checkProgressBox = QCheckBox(self.advanceWidget)
        self.advanceOkBtn = QPushButton(self.advanceWidget)
        self.selectChromeBtn = QPushButton(self.advanceWidget)
        self.chromePathLabel = QLabel(self.advanceWidget)
        self.chromePathLabel.setStyleSheet("background:white")
        self.checkShowChromeBox = QCheckBox(self.advanceWidget)
        self.chromeLabel = QLabel(self.advanceWidget)
        self.pcapLabel = QLabel(self.advanceWidget)
        self.apiCheckBox = QCheckBox(self.advanceWidget)
        self.corePathLabel = QLabel(self.advanceWidget)
        self.corePathLabel.setStyleSheet("background:white")
        self.corePathBtn = QPushButton(self.advanceWidget)

        self.interfaceMenu = QComboBox(self.advanceWidget)
        self.interfaceMenu.addItem('Default')

        self.aiOptionBox= QCheckBox(self.advanceWidget)

        a = IFACES
        print(a)
        for i in a.keys():
            print(a[i].description)
            self.interfaceMenu.addItem(a[i].description)

        # connect singal and slot
        self.startBtn.clicked.connect(self.clickedStarBtn)
        self.radioProvision.clicked.connect(self.clickedProvision)
        self.radioReboot.clicked.connect(self.clickedOthers)
        self.radioFactory.clicked.connect(self.clickedOthers)
        self.stopBtn.clicked.connect(self.clickedStopBtn)
        self.grp2602Action.triggered.connect(self.clickedGrp2602)
        self.gxpAction.triggered.connect(self.clickedGxpType)
        self.timer.timeout.connect(self.updateMessage)
        self.apiCheckBox.stateChanged.connect(self.apiTestBoxCheck)
        self.clearBtn.clicked.connect(self.clickedClearBtn)

        self.advanceBtn.clicked.connect(self.clickedAdvanceBtn)
        self.advanceOkBtn.clicked.connect(self.clickedAdvanceOkBtn)
        self.checkSaveLogBox.stateChanged.connect(self.saveLogBoxCheck)
        self.selectChromeBtn.clicked.connect(self.clickedSelectChromeBtn)
        self.selectDirBtn.clicked.connect(self.clickedSelectDirBtn)
        self.corePathBtn.clicked.connect(self.clickedCorePathBtn)

        self.worker.signalJobEnd.connect(self.slotTestStoped)
        self.worker.apiTestFinished.connect(self.slotTestStoped)
        self.signalRun.connect(self.worker.dowork)
        self.signalRunApi.connect(self.worker.doworkApi)

        # self.man.signalUpdateMessage.connect(self.pushMessage)

    def setupUI(self):
        # set text content /value
        self.widget.setWindowTitle("自动重启升降级测试工具")
        self.ipLabel.setText("初始IP:")
        self.passwordLabel.setText("密码:")
        self.startBtn.setText("开始测试")
        self.stopBtn.setText("停止测试")
        self.userLabel.setText("用户名:")
        self.intervalLabel.setText("间隔时间:")
        self.loopLabel.setText("测试次数:")
        self.intervalSpinBox.setValue(130)
        self.radioFactory.setText("恢复出厂")
        self.radioProvision.setText("升降级")
        self.radioReboot.setText("重启")
        self.ver1Label.setText("版本 1:")
        self.ver2Label.setText("版本 2:")
        self.ver3Label.setText("版本 3:")
        self.dir1Label.setText("路径 1:")
        self.dir2Label.setText("路径 2:")
        self.dir3Label.setText("路径 3:")
        self.radioHttp.setText("Http")
        self.radioHttps.setText("Https")
        self.radioTftp.setText("Tftp")
        self.radioFtp.setText("Ftp")
        self.radioFtps.setText("Ftps")
        self.apiCheckBox.setText("使用API测试,配置CoreDump下载路径")
        self.webLabel.setText("网页类型:")
        self.webBtn.setText("请选择UI类型")
        self.clearBtn.setText("清空输入")

        self.advanceWidget.setWindowTitle("高级设置")
        self.advanceBtn.setText("高级设置")
        self.checkCoreBox.setText("发现Core Dump时停止测试")
        self.cleanCache.setText("清除页面cache")
        self.checkSaveLogBox.setText("保存测试日志")
        self.selectDirBtn.setText("浏览")
        self.checkProgressBox.setText("显示底部状态条")
        self.advanceOkBtn.setText("OK")
        self.checkShowChromeBox.setText("测试时显示Chrome浏览器")
        self.selectChromeBtn.setText("浏览")
        self.chromeLabel.setText("Chrome浏览器路径")
        self.infoLabel.setText("未开始测试")
        self.pcapLabel.setText("Net Interface")
        self.corePathBtn.setText("浏览")
        self.radioWindow.setText("网页拖拽文件")
        # self.aiOptionBox.setText("AI")

        #init value
        self.saveDirLabel.hide()
        self.selectDirBtn.hide()

        self.gxpAction.setChecked(True)
        self.radioReboot.click()
        self.radioHttp.click()
        self.stopBtn.setEnabled(False)
        self.passwordLineEdit.setEchoMode(QLineEdit.PasswordEchoOnEdit)

        # set position-------------------------------
        xPos = 20
        yPos = 30
        colum2 = xPos +200

        # line 1
        self.ipLabel.move(xPos,yPos)
        self.intervalLabel.move(colum2,yPos)
        self.intervalSpinBox.move(colum2+60,yPos-2)
        self.ipLineEdit.move(xPos+50,yPos-2)

        # line 2
        line2 = yPos +40
        self.passwordLabel.move(xPos,line2)
        self.passwordLineEdit.move(xPos+50,line2-2)
        self.loopLabel.move(colum2,line2)
        self.loopSpinBox.move(colum2+60,line2-2)

        # line3
        line3 = yPos +80
        self.userLabel.move(xPos,line3)
        self.userLineEdit.move(xPos+50,line3-2)

        self.radioReboot.move(colum2,line3)
        self.radioFactory.move(colum2+60,line3)
        self.radioProvision.move(colum2,line3+30)

        self.webLabel.move(xPos,line3+40)
        self.webBtn.move(xPos+60,line3+35)

        # provWidget
        self.provWidget.resize(400,130)
        self.provWidget.move(xPos,line3+70)
        spaceY = 30
        x = 0
        y = 0
        cl = 200
        self.ver1Label.move(x,y)
        self.ver1LineEdit.move(x+50,y)
        self.ver2Label.move(x,y+spaceY)
        self.ver2LineEdit.move(x+50,y+spaceY)
        self.ver3Label.move(x,y+spaceY*2)
        self.ver3LineEdit.move(x+50,y+spaceY*2)

        self.dir1Label.move(cl,y)
        self.dir1LineEdit.move(cl+50,y)
        self.dir2Label.move(cl,y+spaceY)
        self.dir2LineEdit.move(cl+50,y+spaceY)
        self.dir3Label.move(cl,y+spaceY*2)
        self.dir3LineEdit.move(cl+50,y+spaceY*2)

        self.radioHttp.move(x,y+spaceY*3)
        self.radioHttps.move(x+50,y+spaceY*3)
        self.radioTftp.move(x+110,y+spaceY*3)
        self.radioFtp.move(x+160,y+spaceY*3)
        self.radioFtps.move(x+210,y+spaceY*3)
        self.radioWindow.move(x+265,y+spaceY*3)

        # advance widget
        self.advanceWidget.resize(300,400)
        x = 20
        y = 20
        space = 30

        self.checkCoreBox.move(x,y)
        self.cleanCache.move(x,y+space)
        self.checkProgressBox.move(x,y+space*2)
        self.checkShowChromeBox.move(x,y+space*3)
        self.apiCheckBox.move(x,y+space*4)
        self.corePathBtn.move(x-2,y+space*5-8)
        self.corePathLabel.move(x+35,y+space*5-8)

        y += 40
        self.chromeLabel.move(x,y+space*4+10)
        self.selectChromeBtn.move(x-2,y+space*5)
        self.chromePathLabel.move(x+35,y+space*5+2)

        self.checkSaveLogBox.move(x,y+space*6+10)
        self.selectDirBtn.move(x-2,y+space*7)
        self.saveDirLabel.move(x+35,y+space*7+2)
        self.advanceOkBtn.move(x+170,y+space*10+10)
        self.interfaceMenu.move(x-5,y+space*8+10)
        self.pcapLabel.move(x,y+space*8-2)
        # self.aiOptionBox.move(x, y+space*9+8)
        # set size
        self.messageBox.resize(373,155)
        # self.widget.resize(410,400)
        self.loopSpinBox.resize(60,25)
        self.intervalSpinBox.resize(60,25)
        self.webBtn.resize(100,25)

        self.saveDirLabel.resize(185,22)
        self.selectDirBtn.resize(32,24)
        self.selectChromeBtn.resize(32,24)
        self.chromePathLabel.resize(185,22)
        self.infoLabel.resize(400, 25)
        self.corePathBtn.resize(32,24)
        self.corePathLabel.resize(185,22)
        #
        self.provWidget.hide()
        self.changePosition(True)
        self.widget.show()
        self.loadCache()
    # ----------------end of setupUI ---------------------


    def changePosition(self,hide):
        xPos = 20
        if hide:
            buttonLine = 200
            self.widget.resize(420,415)
        else:
            buttonLine = 310
            self.widget.resize(420,524)

        self.startBtn.move(xPos,buttonLine)
        self.stopBtn.move(xPos+90,buttonLine)
        self.clearBtn.move(xPos+180,buttonLine)
        self.advanceBtn.move(xPos+270,buttonLine)
        self.messageBox.move(xPos,buttonLine+30)
        boxH = self.messageBox.height()
        self.infoLabel.move(xPos,buttonLine+boxH+35)

    def setItemEnable(self,enable):
        self.provWidget.setEnabled(enable)
        self.ipLineEdit.setEnabled(enable)
        self.passwordLineEdit.setEnabled(enable)
        self.userLineEdit.setEnabled(enable)
        self.intervalSpinBox.setEnabled(enable)
        self.loopSpinBox.setEnabled(enable)
        self.radioFactory.setEnabled(enable)
        self.radioReboot.setEnabled(enable)
        self.radioProvision.setEnabled(enable)
        self.advanceBtn.setEnabled(enable)
        self.startBtn.setEnabled(enable)
        self.clearBtn.setEnabled(enable)
        if self.useApiTest:
            self.webBtn.setEnabled(False)
        else:
            self.webBtn.setEnabled(enable)
        self.stopBtn.setEnabled(not enable)

    def outputError(self,str):
        appstr = "<span style=\"color:red\">"
        appstr += str + "</span>"
        self.messageBox.append(appstr)

    def outputWarning(self,str):
        appstr = "<span style=\"color:orange\">"
        appstr += str + "</span>"
        self.messageBox.append(appstr)

    def loadCache(self):
        file = QFile("cache")
        if not file.open(QIODevice.ReadOnly | QIODevice.Text):
            return

        inStream = QTextStream(file)

        # ip
        self.ipLineEdit.setText(inStream.readLine())
        # passwordLabel
        self.passwordLineEdit.setText(inStream.readLine())
        # user
        self.userLineEdit.setText(inStream.readLine())
        # ver1
        self.ver1LineEdit.setText(inStream.readLine())
        self.dir1LineEdit.setText(inStream.readLine())
        # ver2
        self.ver2LineEdit.setText(inStream.readLine())
        self.dir2LineEdit.setText(inStream.readLine())
        # ver3
        self.ver3LineEdit.setText(inStream.readLine())
        self.dir3LineEdit.setText(inStream.readLine())

        self.intervalSpinBox.setValue(int(inStream.readLine()))
        self.loopSpinBox.setValue(int(inStream.readLine()))

        # web type button
        webType = inStream.readLine()
        if webType == "gxpAction":
            self.grp2602Action.setChecked(False)
            self.gxpAction.setChecked(True)
            self.webBtn.setText(self.gxpAction.text())
        else:
            self.grp2602Action.setChecked(True)
            self.gxpAction.setChecked(False)
            self.webBtn.setText(self.grp2602Action.text())

        testType = inStream.readLine()
        if testType == "reboot":
            self.radioReboot.setChecked(True)
        elif testType == "provision":
            self.radioProvision.setChecked(True)
            self.changePosition(False)
            self.provWidget.show()
        else:
            self.radioFactory.setChecked(True)

        serverType = inStream.readLine()
        if serverType == "Http":
            self.radioHttp.setChecked(True)
        elif serverType == "Https":
            self.radioHttps.setChecked(True)
        elif serverType == "Tftp":
            self.radioTftp.setChecked(True)
        elif serverType == "Ftp":
            self.radioFtp.setChecked(True)
        elif serverType == "Ftps":            
            self.radioFtps.setChecked(True)
        else:
            self.radioWindow.setChecked(True)

        if inStream.readLine() == "True":
            self.findCoreStop = True
        else:
            self.findCoreStop = False

        if inStream.readLine() == "True":
            self.cleanCacheSet = True
        else:
            self.cleanCacheSet = False

        if inStream.readLine() == "True":
            self.useApiTest = True
            self.webBtn.setEnabled(False)
        else:
            self.useApiTest = False
            self.corePathBtn.hide()
            self.corePathLabel.hide()

        if inStream.readLine() == "True":
            self.showTestProgress = True
        else:
            self.showTestProgress = False
            self.infoLabel.hide()

        if inStream.readLine() == "True":
            self.showChrome = True
        else:
            self.showChrome = False

        self.chromePath = inStream.readLine()

        if inStream.readLine() == "True":
            self.saveTestLog = True
        else:
            self.saveTestLog = False

        self.saveLogPath = inStream.readLine()

        self.coreDumpPath = inStream.readLine()

        file.close()

    def saveCache(self):
        file = QFile("cache")
        if not file.open(QIODevice.WriteOnly):
            return

        content = self.ipLineEdit.text() + "\n"
        content += self.passwordLineEdit.text() + "\n"
        content += self.userLineEdit.text() + "\n"
        content += self.ver1LineEdit.text() + "\n"
        content += self.dir1LineEdit.text() + "\n"
        content += self.ver2LineEdit.text() + "\n"
        content += self.dir2LineEdit.text() + "\n"
        content += self.ver3LineEdit.text() + "\n"
        content += self.dir3LineEdit.text() + "\n"

        content += str(self.intervalSpinBox.value()) + "\n"
        content += str(self.loopSpinBox.value()) + "\n"

        if self.gxpAction.isChecked():
            content += "gxpAction\n"
        else:
            content += "grp2602Action\n"

        if self.radioReboot.isChecked():
            content += "reboot\n"
        elif self.radioProvision.isChecked():
            content += "provision\n"
        else:
            content += "factory\n"

        if self.radioHttp.isChecked():
            content += "Http\n"
        elif self.radioHttps.isChecked():
            content += "Https\n"
        elif self.radioTftp.isChecked():
            content += "Tftp\n"
        elif self.radioFtp.isChecked():
            content += "Ftp\n"
        elif self.radioFtps.isChecked():
            content += "Ftps\n"
        else :
            content += "Window\n"

        content += str(self.findCoreStop) + "\n"
        content += str(self.cleanCacheSet) + "\n"
        content += str(self.useApiTest) + "\n"
        content += str(self.showTestProgress) + "\n"
        content += str(self.showChrome) + "\n"
        content += self.chromePath +"\n"
        content += str(self.saveTestLog) +"\n"
        content += self.saveLogPath +"\n"
        content += self.coreDumpPath +"\n"

        byteArr = bytes(content,"utf-8")
        file.write(QByteArray(byteArr))
        file.close()


    def checkBeforeRun(self):
        containError = False
        #---------check Ip address--------------
        if self.ipLineEdit.text() == "":
            containError = True
            self.outputError("IP地址不能为空!")
        else:
            pattern = re.compile("^((1[0-9][0-9]\.)|(2[0-4][0-9]\.)|(25[0-5]\.)|([1-9][0-9]\.)|([0-9]\.)){3}((1[0-9][0-9])|(2[0-4][0-9])|(25[0-5])|([1-9][0-9])|([0-9]))$")
            if not pattern.search(self.ipLineEdit.text()):
                containError = True
                self.outputError("IP地址格式错误,检查是否含有多余空格!(仅支持IPV4)")

        #------------------------
        if self.passwordLineEdit.text() == "":
            containError = True
            self.outputError("密码不能为空!")
        if self.userLineEdit.text() == "":
            containError = True
            self.outputError("用户名不能为空!")
        if self.intervalSpinBox.value() <= 40:
            self.outputWarning("间隔时间过短,可能对测试造成影响")
        if not self.radioProvision.isChecked() and not self.radioReboot.isChecked() and \
           not self.radioFactory.isChecked():
            containError = True
            self.outputError("必须选择测试方式(重启,升降级,恢复出厂)")

        # check provision ----------
        if self.radioProvision.isChecked():
            if self.ver1LineEdit.text() == "" or self.ver2LineEdit.text() == "" or \
               self.dir1LineEdit.text() == "" or self.dir2LineEdit.text() == "":
                containError = True
                self.outputError("升降级测试至少填上前两个版本及其路径")

            bin_name = ""
            if os.path.exists(os.path.abspath("config.ini") ):
                f = open(os.path.abspath("config.ini") , "r")
                line = f.readline()
                while line:
                    option = line.split("=")
                    if option[0] == "firmbinname":
                        if option[1].strip('"') != "":
                            filenamebin = option[1].strip()
                            bin_name = filenamebin.strip('"')
                        pass
                    line = f.readline()
                f.close()

            filename = os.path.join(self.dir1LineEdit.text(), bin_name)
            if not os.path.exists(filename) and self.radioWindow.isChecked():
                containError = True
                self.outputError("firmware1 文件不存在!" + filename)
            filename = os.path.join(self.dir2LineEdit.text(), bin_name)
            if not os.path.exists(filename) and self.radioWindow.isChecked():
                containError = True
                self.outputError("firmware2 文件不存在!" + filename)
           

            filename = os.path.join(self.dir3LineEdit.text(), bin_name)
            if self.ver3LineEdit.text() != "" and self.dir3LineEdit.text() == "":
                containError = True
                self.outputError("填写了版本3,但对应路径为空!")
            if self.dir3LineEdit.text() != "" and self.ver3LineEdit.text() == "":
                containError = True
                self.outputError("填写了路径3,但对应版本为空!")
            elif self.dir3LineEdit.text() != "" and self.radioWindow.isChecked() and not os.path.exists(filename):
                containError = True
                self.outputError("firmware3 文件不存在!" + filename)

            if not self.radioFtp.isChecked() and not self.radioFtps.isChecked() and \
               not self.radioHttp.isChecked() and not self.radioHttps.isChecked() and \
               not self.radioTftp.isChecked() and not self.radioWindow.isChecked():
                containError = True
                self.outputError("升降级测试必须选择服务器类型(Tftp,Ftp,Ftps,Http,Https)")
        return containError



    def startTest(self):
        ip = self.ipLineEdit.text()
        passwd = self.passwordLineEdit.text()
        username = self.userLineEdit.text()
        ptime = self.intervalSpinBox.value()
        loop = self.loopSpinBox.value()
        modelType = self.webBtn.text()
        if self.gxpAction.isChecked():
            device_type = "GXP21XX"
        elif self.grp2602Action.isChecked():
            device_type = "GRP260X"

        if self.radioReboot.isChecked():
            task_type = "reboot"
        elif self.radioProvision.isChecked():
            task_type = "provision"
            text_ver1 = self.ver1LineEdit.text()
            text_ver2 = self.ver2LineEdit.text()
            text_ver3 = self.ver3LineEdit.text()
            text_dir1 = self.dir1LineEdit.text()
            text_dir2 = self.dir2LineEdit.text()
            text_dir3 = self.dir3LineEdit.text()
            prov_dict = {"ver1": text_ver1.strip(), "dir1": text_dir1.strip(), "ver2": text_ver2.strip(),
                        "dir2": text_dir2.strip(), "ver3": text_ver3.strip(), "dir3": text_dir3.strip()}

            if self.radioHttp.isChecked():
                self.man.update_prov_setting("HTTP", prov_dict)
            elif self.radioHttps.isChecked():
                self.man.update_prov_setting("HTTPS", prov_dict)
            elif self.radioTftp.isChecked():
                self.man.update_prov_setting("TFTP", prov_dict)
            elif self.radioFtp.isChecked():
                self.man.update_prov_setting("FTP", prov_dict)
            elif self.radioFtps.isChecked():
                self.man.update_prov_setting("FTPS", prov_dict)
            elif self.radioWindow.isChecked():
                self.man.update_prov_setting("browser", prov_dict)

        else:
            task_type = "reset"

        coredump_stop = True
        headless_flag = False
        clean_cache = False
        if self.checkCoreBox.isChecked() == False:
            self.messageBox.append("Find core dump will not stop")
            coredump_stop = False
        if self.cleanCache.isChecked() == True:
            clean_cache = True
            
        if self.checkShowChromeBox.isChecked() == True or self.radioWindow.isChecked() == True:
            headless_flag = False
        else:
            headless_flag = True

        # ai_mode = False
        # if self.aiOptionBox.isChecked() == True:
        #     ai_mode = True

        browser_path = ""
        if self.chromePathLabel.text() != "":
            browser_path = self.chromePathLabel.text()

        self.testStatus = [True,1,0,"ip",""]
        print(self.interfaceMenu.currentText())
        self.man.setStatus(self.testStatus)
        self.man.setMessageList(self.messageList)
        self.man.update_setting(ip.strip(), username.strip(), passwd.strip(), device_type, \
                                task_type, loop, ptime, coredump_stop, headless_flag, browser_path, \
                                clean_cache, self.interfaceMenu.currentText(), False)
    
    def startApiTest(self):
        ip = self.ipLineEdit.text()
        passwd = self.passwordLineEdit.text()
        username = self.userLineEdit.text()
        ptime = self.intervalSpinBox.value()
        loop = self.loopSpinBox.value()
        
        testType = "Reboot"
        if self.radioProvision.isChecked():
            testType = "Provision"
        self.api.setValue(ip,username,passwd,ptime,loop,testType)

        v1 = self.ver1LineEdit.text()
        v2 = self.ver2LineEdit.text()
        v3 = self.ver3LineEdit.text()
        d1 = self.dir1LineEdit.text()
        d2 = self.dir2LineEdit.text()
        d3 = self.dir3LineEdit.text()
        self.api.setVersion(v1,v2,v3,d1,d2,d3)

        self.api.setTestStatus(self.testStatus,self.messageList,self.coreDumpPath)

        if self.radioHttp.isChecked():
            self.api.setServerType("http")
        elif self.radioHttps.isChecked():
            self.api.setServerType("https")
        elif self.radioTftp.isChecked():
            self.api.setServerType("tftp")
        elif self.radioFtp.isChecked():
            self.api.setServerType("ftp")
        else: #self.radioFtps.isChecked()
            self.api.setServerType("ftps")
        
        self.api.setFoundCoreStop(self.findCoreStop)


    # slot ---------------------------------
    def apiTestBoxCheck(self,state):
        if state == 2:
            self.corePathBtn.show()
            self.corePathLabel.show()
        else:
            self.corePathBtn.hide()
            self.corePathLabel.hide()

    def clickedCorePathBtn(self):
        dir = QFileDialog.getExistingDirectory(self.advanceWidget,"选择Core Dump存放路径","/home")
        if dir != "":
            self.corePathLabel.setText(dir)
            self.coreDumpPath = dir

    def clickedSelectDirBtn(self):
        dir = QFileDialog.getExistingDirectory(self.advanceWidget,"选择日志存放路径","/home")
        if dir != "":
            self.saveDirLabel.setText(dir)
            self.saveLogPath = dir

    def clickedSelectChromeBtn(self):
        fileName = QFileDialog.getOpenFileName(self.advanceWidget,"选择谷歌浏览器","/home","Chrome (*.exe)")
        if fileName != "":
            self.chromePathLabel.setText(fileName[0])
            self.chromePath = fileName[0]

    def saveLogBoxCheck(self,state):
        if state == 2: # checked
            self.selectDirBtn.show()
            self.saveDirLabel.show()
        else:
            self.selectDirBtn.hide()
            self.saveDirLabel.hide()

    def clickedAdvanceOkBtn(self):
        self.findCoreStop = self.checkCoreBox.isChecked()
        self.cleanCacheSet = self.cleanCache.isChecked()
        self.useApiTest = self.apiCheckBox.isChecked()
        self.showTestProgress = self.checkProgressBox.isChecked()
        self.saveTestLog = self.checkSaveLogBox.isChecked()
        self.saveLogPath = self.saveDirLabel.text()
        self.showChrome = self.checkShowChromeBox.isChecked()
        self.chromePath = self.chromePathLabel.text()
        self.coreDumpPath = self.corePathLabel.text()

        if self.useApiTest:
            self.webBtn.setEnabled(False)
            self.corePathBtn.show()
            self.corePathLabel.show()
        else:
            self.webBtn.setEnabled(True)
            self.corePathBtn.hide()
            self.corePathLabel.hide()

        if self.showTestProgress:
            self.infoLabel.show()
        else:
            self.infoLabel.hide()
                
        self.saveCache()
        self.advanceWidget.hide()


    def clickedAdvanceBtn(self):
        self.advanceWidget.hide()
        self.checkCoreBox.setChecked(self.findCoreStop)
        self.cleanCache.setChecked(self.cleanCacheSet)
        self.apiCheckBox.setChecked(self.useApiTest)
        self.checkProgressBox.setChecked(self.showTestProgress)
        self.checkSaveLogBox.setChecked(self.saveTestLog)
        self.saveDirLabel.setText(self.saveLogPath)
        self.checkShowChromeBox.setChecked(self.showChrome)
        self.chromePathLabel.setText(self.chromePath)
        self.corePathLabel.setText(self.coreDumpPath)
        self.advanceWidget.show()

    def slotTestStoped(self):
        self.testStatus[0] = False
        self.setItemEnable(True)
        self.timer.stop()
        self.updateMessage()
        self.thread.quit()

        # save Test log
        if self.saveTestLog:
            fileName = time.strftime("%Y_%m_%d.%H_%M_%S.",time.localtime())
            if self.radioReboot.isChecked():
                fileName += "reboot"
            elif self.radioProvision:
                fileName += "provision"
            else:
                fileName += "factoryReset"
            fileName += ".htm"
            if self.saveLogPath == "":
                self.outputWarning("日志地址没有设置,无法保存")
            else:
                fileName = self.saveLogPath + "\\" + fileName
                print(fileName)

                file = QFile(fileName)
                if not file.open(QIODevice.WriteOnly):
                    self.outputError("打开文件错误,保存日志失败")
                    return

                byteArr = bytes(self.messageBox.toHtml(),"utf-8")
                file.write(QByteArray(byteArr))
                file.close()

    def clickedClearBtn(self):
        self.ipLineEdit.setText("")
        self.passwordLineEdit.setText("")
        self.userLineEdit.setText("")
        self.ver1LineEdit.setText("")
        self.dir1LineEdit.setText("")
        self.ver2LineEdit.setText("")
        self.dir2LineEdit.setText("")
        self.ver3LineEdit.setText("")
        self.dir3LineEdit.setText("")

    def clickedStarBtn(self):
        if self.checkBeforeRun():
            return
        self.messageBox.clear()
        self.saveCache()
        self.messageBox.append("Init Setting...")
        self.setItemEnable(False)
        self.timer.start(500)
        self.thread.start()

        # deside use what to test
        if self.useApiTest:
            if self.radioFactory.isChecked():
                self.outputWarning("Api not support Factory Reset, will test as Gxp type web driver")	
                self.clickedGxpType()
                self.startTest()
                self.signalRun.emit(self.man)
            else:
                self.startApiTest()
                self.signalRunApi.emit(self.api)
        else:
            self.startTest()
            self.signalRun.emit(self.man)

    def clickedStopBtn(self):
        self.stopBtn.setEnabled(False)
        self.testStatus[0] = False
        self.man.quit()
        self.outputWarning("正在停止...")

    def clickedProvision(self):
        self.provWidget.show()
        self.changePosition(False)

    def clickedOthers(self):
        self.provWidget.hide()
        self.changePosition(True)

    def clickedGxpType(self):
        self.gxpAction.setChecked(True)
        self.grp2602Action.setChecked(False)
        self.webBtn.setText(self.gxpAction.text())

    def clickedGrp2602(self):
        self.gxpAction.setChecked(False)
        self.grp2602Action.setChecked(True)
        self.webBtn.setText(self.grp2602Action.text())

    def updateMessage(self):
        while len(self.messageList) >0:
            info = self.messageList.popleft()
            self.messageBox.append(info)
        if self.testStatus[0] == False:
            self.infoLabel.setText("未开始测试")
        else:
            info = "第" + str(self.testStatus[1]) + "次测试   "
            if self.testStatus[2] > 0:
                info += "等待:{} 秒".format( self.testStatus[2] )
            else:
                info += "运行中"
            
            info += "  " + self.testStatus[3]
            info += "  " + self.testStatus[4]
            self.infoLabel.setText(info)
예제 #18
0
class FlowStylusModesWidget(QWidget):
    def __init__(self, flow):
        super(FlowStylusModesWidget, self).__init__()

        # GENERAL ATTRIBUTES
        self.flow = flow
        self.pen_color = QColor(255, 255, 0)

        # stylus button
        self.stylus_button = QPushButton('stylus')  # show/hide
        self.stylus_button.clicked.connect(self.on_stylus_button_clicked)

        # mode
        self.set_stylus_mode_comment_button = QPushButton('comment')
        self.set_stylus_mode_comment_button.clicked.connect(
            self.on_comment_button_clicked)
        self.set_stylus_mode_edit_button = QPushButton('edit')
        self.set_stylus_mode_edit_button.clicked.connect(
            self.on_edit_button_clicked)

        # pen style
        self.pen_color_button = QPushButton('color')
        self.pen_color_button.clicked.connect(self.on_choose_color_clicked)
        self.pen_width_slider = QSlider(Qt.Horizontal)
        self.pen_width_slider.setRange(1, 100)  # -> /10
        self.pen_width_slider.setValue(20)

        self.pen_style_widget = QWidget()
        pen_style_layout = QHBoxLayout()
        pen_style_layout.addWidget(self.pen_color_button)
        pen_style_layout.addWidget(self.pen_width_slider)

        self.pen_style_widget.setLayout(pen_style_layout)

        # MERGE SETTINGS
        self.settings_widget = QWidget()
        settings_layout = QHBoxLayout()
        settings_layout.addWidget(self.pen_style_widget)
        settings_layout.addWidget(self.set_stylus_mode_comment_button)
        settings_layout.addWidget(self.set_stylus_mode_edit_button)

        self.settings_widget.setLayout(settings_layout)

        # MAIN LAYOUT
        main_horizontal_layout = QHBoxLayout()

        main_horizontal_layout.addWidget(self.settings_widget)
        main_horizontal_layout.addWidget(self.stylus_button)

        self.setLayout(main_horizontal_layout)

        self.setStyleSheet('''
            background: transparent;
            border: none;
        ''')

        self.push_button_style_sheet_start = '''
            QPushButton:hover:pressed {
                background-color: rgba(100, 100, 100, 150);
            }
            QPushButton {
                border: 1px solid #aaaaaa;
                border-radius: 8px;
                height: 25px;
                width: 50px;'''  # color may be dynamic

        self.std_push_button_style_sheet = self.push_button_style_sheet_start + '''
                background-color: rgba(10, 10, 10, 150);
                color: #aaaaaa;
            }
        '''

        self.set_stylus_mode_comment_button.setStyleSheet(
            self.std_push_button_style_sheet)
        self.set_stylus_mode_edit_button.setStyleSheet(
            self.std_push_button_style_sheet)
        self.stylus_button.setStyleSheet(self.std_push_button_style_sheet)
        self.update_color_button_SS()

        self.stylus_button.setFixedWidth(50)
        self.set_stylus_mode_edit_button.setFixedWidth(60)
        self.set_stylus_mode_comment_button.setFixedWidth(60)

        self.pen_style_widget.hide()
        self.settings_widget.hide()

    def on_stylus_button_clicked(self):
        if self.settings_widget.isHidden():
            self.settings_widget.show()
        elif not self.settings_widget.isHidden():
            self.settings_widget.hide()
        self.adjustSize()
        self.flow.set_stylus_proxy_pos()

    def on_edit_button_clicked(self):
        self.flow.stylus_mode = 'edit'
        self.pen_style_widget.hide()

        # if I don't hide and show the settings_widget manually here, the stylus mode buttons take up the additional
        # space when clicking on comment and then edit. self.adjustSize() does not seem to work properly here...
        self.settings_widget.hide()
        self.settings_widget.show()

        self.adjustSize()
        self.flow.set_stylus_proxy_pos()
        # self.flow.setDragMode(QGraphicsView.RubberBandDrag)

    def on_comment_button_clicked(self):
        self.flow.stylus_mode = 'comment'
        self.pen_style_widget.show()
        self.adjustSize()
        self.flow.set_stylus_proxy_pos()
        # self.flow.setDragMode(QGraphicsView.NoDrag)

    def on_choose_color_clicked(self):
        self.pen_color = QColorDialog.getColor(
            self.pen_color,
            options=QColorDialog.ShowAlphaChannel,
            title='Choose pen color')
        self.update_color_button_SS()

    def update_color_button_SS(self):
        self.pen_color_button.setStyleSheet(
            self.push_button_style_sheet_start + '''
                color: black;
                background-color: ''' + self.pen_color.name() + ''';
            }''')

    def get_pen_settings(self):
        return {
            'color': self.pen_color.name(),
            'base stroke weight': self.pen_width_slider.value() / 10
        }
예제 #19
0
class ImportDialog(QDialog):
    """
    A widget for importing data into a Spine db. Currently used by DataStoreForm.
    It embeds three widgets that alternate depending on user's actions:
    - `select_widget` is a `QWidget` for selecting the source data type (CSV, Excel, etc.)
    - `_import_preview` is an `ImportPreviewWidget` for defining Mappings to associate with the source data
    - `_error_widget` is an `ImportErrorWidget` to show errors from import operations
    """

    data_ready = Signal(dict)

    _SETTINGS_GROUP_NAME = "importDialog"

    def __init__(self, settings, parent):
        """
        Args:
            settings (QSettings): settings for storing/restoring window state
            parent (DataStoreForm): parent widget
        """
        from ...ui.import_source_selector import Ui_ImportSourceSelector  # pylint: disable=import-outside-toplevel

        super().__init__(parent)
        self.setWindowFlag(Qt.Window, True)
        self.setWindowFlag(Qt.WindowMinMaxButtonsHint, True)
        self.setWindowTitle("Import data")

        # state
        self._mapped_data = None
        self._mapping_errors = []
        connector_list = [
            CSVConnector, ExcelConnector, SqlAlchemyConnector, GdxConnector,
            JSONConnector
        ]
        self.connectors = {c.DISPLAY_NAME: c for c in connector_list}
        self._selected_connector = None
        self.active_connector = None
        self._current_view = "connector"
        self._settings = settings
        self._preview_window_state = {}

        # create widgets
        self._import_preview = None
        self._error_widget = ImportErrorsWidget()
        self._error_widget.hide()
        self._dialog_buttons = QDialogButtonBox(QDialogButtonBox.Ok
                                                | QDialogButtonBox.Abort
                                                | QDialogButtonBox.Cancel)
        self._dialog_buttons.button(QDialogButtonBox.Abort).setText("Back")

        self._layout = QVBoxLayout()

        # layout
        self.select_widget = QWidget(self)
        self._select_widget_ui = Ui_ImportSourceSelector()
        self._select_widget_ui.setupUi(self.select_widget)

        self.setLayout(QVBoxLayout())
        self.layout().addLayout(self._layout)
        self.layout().addWidget(self._dialog_buttons)
        self._layout.addWidget(self._error_widget)
        self._layout.addWidget(self.select_widget)

        # set list items
        self._select_widget_ui.source_list.blockSignals(True)
        self._select_widget_ui.source_list.addItems(
            list(self.connectors.keys()))
        self._select_widget_ui.source_list.clearSelection()
        self._select_widget_ui.source_list.blockSignals(False)

        # connect signals
        self._select_widget_ui.source_list.currentItemChanged.connect(
            self.connector_selected)
        self._select_widget_ui.source_list.activated.connect(
            self.launch_import_preview)
        self._dialog_buttons.button(QDialogButtonBox.Ok).clicked.connect(
            self._handle_ok_clicked)
        self._dialog_buttons.button(QDialogButtonBox.Cancel).clicked.connect(
            self._handle_cancel_clicked)
        self._dialog_buttons.button(QDialogButtonBox.Abort).clicked.connect(
            self._handle_back_clicked)
        self.data_ready.connect(self.parent().import_data)

        # init ok button
        self.set_ok_button_availability()

        self._dialog_buttons.button(QDialogButtonBox.Abort).hide()

    @property
    def mapped_data(self):
        return self._mapped_data

    @property
    def mapping_errors(self):
        return self._mapping_errors

    @Slot("QListWidgetItem", "QListWidgetItem")
    def connector_selected(self, current, _previous):
        connector = None
        if current:
            connector = self.connectors.get(current.text(), None)
        self._selected_connector = connector
        self.set_ok_button_availability()

    def set_ok_button_availability(self):
        if self._current_view == "connector":
            if self._selected_connector:
                self._dialog_buttons.button(
                    QDialogButtonBox.Ok).setEnabled(True)
            else:
                self._dialog_buttons.button(
                    QDialogButtonBox.Ok).setEnabled(False)
        elif self._current_view == "preview":
            if self._import_preview.checked_tables:
                self._dialog_buttons.button(
                    QDialogButtonBox.Ok).setEnabled(True)
            else:
                self._dialog_buttons.button(
                    QDialogButtonBox.Ok).setEnabled(False)
        else:
            self._dialog_buttons.button(QDialogButtonBox.Ok).setEnabled(True)

    @Slot(dict, list)
    def _handle_data_ready(self, data, errors):
        if errors:
            errors = [
                f"{table_name}: {error_message}"
                for table_name, error_message in errors
            ]
            self._error_widget.set_import_state(errors)
            self.set_error_widget_as_main_widget()
            return
        self.data_ready.emit(data)
        self.accept()

    @Slot()
    def _handle_ok_clicked(self):
        if self._current_view == "connector":
            self.launch_import_preview()
        elif self._current_view == "preview":
            self._import_preview.request_mapped_data()
        elif self._current_view == "error":
            self.accept()

    @Slot()
    def _handle_cancel_clicked(self):
        self.reject()

    @Slot()
    def _handle_back_clicked(self):
        self.set_preview_as_main_widget()

    @Slot()
    def launch_import_preview(self):
        if self._selected_connector:
            # create instance of connector
            connector_settings = {
                "gams_directory": self._gams_system_directory()
            }
            self.active_connector = ConnectionManager(self._selected_connector,
                                                      connector_settings)
            valid_source = self.active_connector.connection_ui()
            if valid_source:
                # Create instance of ImportPreviewWidget and configure
                self._import_preview = ImportEditor(self.active_connector,
                                                    self)
                self._import_preview.set_loading_status(True)
                self._import_preview.tableChecked.connect(
                    self.set_ok_button_availability)
                # Connect handle_data_ready method to the widget
                self._import_preview.mappedDataReady.connect(
                    self._handle_data_ready)
                self._layout.addWidget(self._import_preview)
                self.active_connector.connectionFailed.connect(
                    self._handle_failed_connection)
                self.active_connector.connectionReady.connect(
                    self.set_preview_as_main_widget)
                self.active_connector.init_connection()
            else:
                # remove connector object.
                self.active_connector.deleteLater()
                self.active_connector = None

    @Slot(str)
    def _handle_failed_connection(self, msg):
        """Handle failed connection, show error message and select widget

        Arguments:
            msg {str} -- str with message of reason for failed connection.
        """
        if self.active_connector:
            self.active_connector.close_connection()
            self.active_connector.deleteLater()
            self.active_connector = None
        if self._import_preview:
            self._import_preview.deleteLater()
            self._import_preview = None
        notification = Notification(self, msg)
        notification.show()

    @Slot()
    def set_preview_as_main_widget(self):
        self._current_view = "preview"
        self.select_widget.hide()
        self._error_widget.hide()
        self._import_preview.show()
        self._restore_preview_ui()
        self._dialog_buttons.button(QDialogButtonBox.Abort).hide()
        self.set_ok_button_availability()

    def set_error_widget_as_main_widget(self):
        self._current_view = "error"
        if self._import_preview is not None and not self._import_preview.isHidden(
        ):
            self._preview_window_state["maximized"] = self.windowState(
            ) == Qt.WindowMaximized
            self._preview_window_state["size"] = self.size()
            self._preview_window_state["position"] = self.pos()
            splitters = dict()
            self._preview_window_state["splitters"] = splitters
            for splitter in self._import_preview.findChildren(QSplitter):
                splitters[splitter.objectName()] = splitter.saveState()
        self.select_widget.hide()
        self._error_widget.show()
        self._import_preview.hide()
        self._dialog_buttons.button(QDialogButtonBox.Abort).show()
        self.set_ok_button_availability()

    def _restore_preview_ui(self):
        """Restore UI state from previous session."""
        if not self._preview_window_state:
            self._settings.beginGroup(self._SETTINGS_GROUP_NAME)
            window_size = self._settings.value("windowSize")
            window_pos = self._settings.value("windowPosition")
            n_screens = self._settings.value("n_screens", defaultValue=1)
            window_maximized = self._settings.value("windowMaximized",
                                                    defaultValue='false')
            splitter_state = {}
            for splitter in self._import_preview.findChildren(QSplitter):
                splitter_state[splitter] = self._settings.value(
                    splitter.objectName() + "_splitterState")
            self._settings.endGroup()
            original_size = self.size()
            if window_size:
                self.resize(window_size)
            else:
                self.setGeometry(
                    QStyle.alignedRect(
                        Qt.LeftToRight, Qt.AlignCenter, QSize(1000, 700),
                        QApplication.desktop().availableGeometry(self)))
            if window_pos:
                self.move(window_pos)
            if len(QGuiApplication.screens()) < int(n_screens):
                # There are less screens available now than on previous application startup
                self.move(
                    0, 0)  # Move this widget to primary screen position (0,0)
            ensure_window_is_on_screen(self, original_size)
            if window_maximized == 'true':
                self.setWindowState(Qt.WindowMaximized)
            for splitter, state in splitter_state.items():
                if state:
                    splitter.restoreState(state)
        else:
            self.resize(self._preview_window_state["size"])
            self.move(self._preview_window_state["position"])
            self.setWindowState(self._preview_window_state["maximized"])
            for splitter in self._import_preview.findChildren(QSplitter):
                name = splitter.objectName()
                splitter.restoreState(
                    self._preview_window_state["splitters"][name])

    def closeEvent(self, event):
        """Stores window's settings and accepts the event."""
        if self._import_preview is not None:
            self._settings.beginGroup(self._SETTINGS_GROUP_NAME)
            self._settings.setValue("n_screens",
                                    len(QGuiApplication.screens()))
            if not self._import_preview.isHidden():
                self._settings.setValue("windowSize", self.size())
                self._settings.setValue("windowPosition", self.pos())
                self._settings.setValue(
                    "windowMaximized",
                    self.windowState() == Qt.WindowMaximized)
                for splitter in self._import_preview.findChildren(QSplitter):
                    self._settings.setValue(
                        splitter.objectName() + "_splitterState",
                        splitter.saveState())
            elif self._preview_window_state:
                self._settings.setValue("windowSize",
                                        self._preview_window_state["size"])
                self._settings.setValue("windowPosition",
                                        self._preview_window_state["position"])
                self._settings.setValue("windowMaximized",
                                        self._preview_window_state.maximized)
                for name, state in self._preview_window_state["splitters"]:
                    self._settings.setValue(name + "_splitterState", state)
            self._settings.endGroup()
        event.accept()

    def _gams_system_directory(self):
        """Returns GAMS system path from Toolbox settings or None if GAMS default is to be used."""
        path = self._settings.value("appSettings/gamsPath", defaultValue=None)
        if not path:
            path = find_gams_directory()
        if path is not None and os.path.isfile(path):
            path = os.path.dirname(path)
        return path
예제 #20
0
class Application(QApplication):
    def __init__(self):
        super().__init__()

        self.parent = QWidget()
        self.parent.hide()
        self.parent.setWindowFlags(self.parent.windowFlags() & ~QtCore.Qt.Tool)
        self.setQuitOnLastWindowClosed(False)
        self.aboutToQuit.connect(self.quit_app)

        self.node_set = NodeSet()
        self.system_tray = SystemTray(self.parent, self.node_set)

    def start(self):
        threadpool = QThreadPool()
        worker = Worker(fn=self.check_version)
        threadpool.start(worker)

        self.system_tray.show()
        self.node_set.start()
        status = self.exec_()
        return status

    @staticmethod
    def check_version(progress_callback):
        latest_version = LauncherSoftware().get_latest_release_version()
        if latest_version is None:
            return
        latest_major, latest_minor, latest_bugfix = latest_version.split('.')
        major, minor, bugfix = NODE_LAUNCHER_RELEASE.split('.')

        major_upgrade = latest_major > major

        minor_upgrade = (latest_major == major and latest_minor > minor)

        bugfix_upgrade = (latest_major == major and latest_minor == minor
                          and latest_bugfix > bugfix)

        if major_upgrade or minor_upgrade or bugfix_upgrade:
            message_box = QMessageBox()
            message_box.setTextFormat(Qt.RichText)
            message_box.setText(UPGRADE)
            message_box.setInformativeText(
                f'Your version: {NODE_LAUNCHER_RELEASE}\n'
                f'New version: {latest_version}')
            message_box.exec_()

    @Slot()
    def quit_app(self):
        log.debug('quit_app')
        self.system_tray.show_message(title='Stopping LND...')
        self.node_set.lnd_node.stop()
        self.node_set.lnd_node.process.waitForFinished(-1)

        self.node_set.bitcoind_node.stop()
        self.system_tray.show_message(title='Stopping bitcoind...')
        self.node_set.bitcoind_node.process.waitForFinished(-1)

        self.node_set.tor_node.process.kill()
        self.node_set.tor_node.process.waitForFinished(-1)

        self.system_tray.show_message(title='Exiting Node Launcher', timeout=1)

        QCoreApplication.exit(0)
예제 #21
0
class ImportDialog(QDialog):
    """
    A widget for importing data into a Spine db. Currently used by DataStoreForm.
    It embeds three widgets that alternate depending on user's actions:
    - `select_widget` is a `QWidget` for selecting the source data type (CSV, Excel, etc.)
    - `_import_preview` is an `ImportPreviewWidget` for defining Mappings to associate with the source data
    - `_error_widget` is an `ImportErrorWidget` to show errors from import operations
    """

    _SETTINGS_GROUP_NAME = "importDialog"

    def __init__(self, settings, parent):
        """
        Args:
            settings (QSettings): settings for storing/restoring window state
            parent (QWidget): parent widget
        """
        from ..ui.import_source_selector import Ui_ImportSourceSelector

        super().__init__(parent)
        self.setWindowFlag(Qt.Window, True)
        self.setWindowFlag(Qt.WindowMinMaxButtonsHint, True)
        self.setWindowTitle("Import data")
        # DB mapping
        if parent is not None:
            self._db_map = parent.db_maps[0]

        # state
        self._mapped_data = None
        self._mapping_errors = []
        self.connector_list = [CSVConnector, ExcelConnector, SqlAlchemyConnector, GdxConnector]
        self.connector_list = {c.DISPLAY_NAME: c for c in self.connector_list}
        self._selected_connector = None
        self.active_connector = None
        self._current_view = "connector"
        self._settings = settings
        self._preview_window_state = {}

        # create widgets
        self._import_preview = None
        self._error_widget = ImportErrorWidget()
        self._error_widget.hide()
        self._dialog_buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Abort | QDialogButtonBox.Cancel)
        self._dialog_buttons.button(QDialogButtonBox.Abort).setText("Back")

        self._layout = QVBoxLayout()

        # layout
        self.select_widget = QWidget(self)
        self._select_widget_ui = Ui_ImportSourceSelector()
        self._select_widget_ui.setupUi(self.select_widget)

        self.setLayout(QVBoxLayout())
        self.layout().addLayout(self._layout)
        self.layout().addWidget(self._dialog_buttons)
        self._layout.addWidget(self._error_widget)
        self._layout.addWidget(self.select_widget)

        # set list items
        self._select_widget_ui.source_list.blockSignals(True)
        self._select_widget_ui.source_list.addItems(list(self.connector_list.keys()))
        self._select_widget_ui.source_list.clearSelection()
        self._select_widget_ui.source_list.blockSignals(False)

        # connect signals
        self._select_widget_ui.source_list.currentItemChanged.connect(self.connector_selected)
        self._select_widget_ui.source_list.activated.connect(self.launch_import_preview)
        self._dialog_buttons.button(QDialogButtonBox.Ok).clicked.connect(self.ok_clicked)
        self._dialog_buttons.button(QDialogButtonBox.Cancel).clicked.connect(self.cancel_clicked)
        self._dialog_buttons.button(QDialogButtonBox.Abort).clicked.connect(self.back_clicked)

        # init ok button
        self.set_ok_button_availability()

        self._dialog_buttons.button(QDialogButtonBox.Abort).hide()

    @property
    def mapped_data(self):
        return self._mapped_data

    @property
    def mapping_errors(self):
        return self._mapping_errors

    @Slot()
    def connector_selected(self, selection):
        connector = None
        if selection:
            connector = self.connector_list.get(selection.text(), None)
        self._selected_connector = connector
        self.set_ok_button_availability()

    def set_ok_button_availability(self):
        if self._current_view == "connector":
            if self._selected_connector:
                self._dialog_buttons.button(QDialogButtonBox.Ok).setEnabled(True)
            else:
                self._dialog_buttons.button(QDialogButtonBox.Ok).setEnabled(False)
        elif self._current_view == "preview":
            if self._import_preview.checked_tables:
                self._dialog_buttons.button(QDialogButtonBox.Ok).setEnabled(True)
            else:
                self._dialog_buttons.button(QDialogButtonBox.Ok).setEnabled(False)
        else:
            self._dialog_buttons.button(QDialogButtonBox.Ok).setEnabled(True)

    @busy_effect
    def import_data(self, data, errors):
        errors = [f"{table_name}: {error_message}" for table_name, error_message in errors]
        try:
            import_num, import_errors = spinedb_api.import_data(self._db_map, **data)
            import_errors = [f"{e.db_type}: {e.msg}" for e in import_errors]
            errors.extend(import_errors)
        except spinedb_api.SpineIntegrityError as err:
            self._db_map.rollback_session()
            self._error_widget.set_import_state(0, [err.msg])
            self.set_error_widget_as_main_widget()
        except spinedb_api.SpineDBAPIError as err:
            self._db_map.rollback_session()
            self._error_widget.set_import_state(0, ["Unable to import Data: %s", err.msg])
            self.set_error_widget_as_main_widget()
        if errors:
            self._error_widget.set_import_state(import_num, errors)
            self.set_error_widget_as_main_widget()
            return False
        return True

    @Slot()
    def data_ready(self, data, errors):
        if self.import_data(data, errors):
            self.accept()

    @Slot()
    def ok_clicked(self):
        if self._current_view == "connector":
            self.launch_import_preview()
        elif self._current_view == "preview":
            self._import_preview.request_mapped_data()
        elif self._current_view == "error":
            self.accept()

    @Slot()
    def cancel_clicked(self):
        if self._db_map.has_pending_changes():
            self._db_map.rollback_session()
        self.reject()

    @Slot()
    def back_clicked(self):
        if self._db_map.has_pending_changes():
            self._db_map.rollback_session()
        self.set_preview_as_main_widget()

    @Slot()
    def launch_import_preview(self):
        if self._selected_connector:
            # create instance of connector
            self.active_connector = ConnectionManager(self._selected_connector)
            valid_source = self.active_connector.connection_ui()
            if valid_source:
                # Create instance of ImportPreviewWidget and configure
                self._import_preview = ImportPreviewWidget(self.active_connector, self)
                self._import_preview.set_loading_status(True)
                self._import_preview.tableChecked.connect(self.set_ok_button_availability)
                # Connect data_ready method to the widget
                self._import_preview.mappedDataReady.connect(self.data_ready)
                self._layout.addWidget(self._import_preview)
                self.active_connector.connectionFailed.connect(self._handle_failed_connection)
                self.active_connector.init_connection()
                # show preview widget
                self.set_preview_as_main_widget()
            else:
                # remove connector object.
                self.active_connector.deleteLater()
                self.active_connector = None

    @Slot(str)
    def _handle_failed_connection(self, msg):
        """Handle failed connection, show error message and select widget

        Arguments:
            msg {str} -- str with message of reason for failed connection.
        """
        self.select_widget.hide()
        self._error_widget.hide()
        self._import_preview.hide()

        if self.active_connector:
            self.active_connector.close_connection()
            self.active_connector.deleteLater()
            self.active_connector = None
        if self._import_preview:
            self._import_preview.deleteLater()
            self._import_preview = None

        ok_button = QPushButton()
        ok_button.setText("Ok")

        temp_widget = QWidget()
        temp_widget.setLayout(QVBoxLayout())
        temp_widget.layout().addWidget(QLabel(msg))
        temp_widget.layout().addWidget(ok_button)

        ok_button.clicked.connect(self.select_widget.show)
        ok_button.clicked.connect(temp_widget.deleteLater)
        self.layout().addWidget(temp_widget)

    def set_preview_as_main_widget(self):
        self._current_view = "preview"
        self.select_widget.hide()
        self._error_widget.hide()
        self._import_preview.show()
        self._restore_preview_ui()
        self._dialog_buttons.button(QDialogButtonBox.Abort).hide()
        self.set_ok_button_availability()

    def set_error_widget_as_main_widget(self):
        self._current_view = "error"
        if self._import_preview is not None and not self._import_preview.isHidden():
            self._preview_window_state["maximized"] = self.windowState() == Qt.WindowMaximized
            self._preview_window_state["size"] = self.size()
            self._preview_window_state["position"] = self.pos()
            splitters = dict()
            self._preview_window_state["splitters"] = splitters
            for splitter in self._import_preview.findChildren(QSplitter):
                splitters[splitter.objectName()] = splitter.saveState()
        self.select_widget.hide()
        self._error_widget.show()
        self._import_preview.hide()
        self._dialog_buttons.button(QDialogButtonBox.Abort).show()
        self.set_ok_button_availability()

    def _restore_preview_ui(self):
        """Restore UI state from previous session."""
        if not self._preview_window_state:
            self._settings.beginGroup(self._SETTINGS_GROUP_NAME)
            window_size = self._settings.value("windowSize")
            window_pos = self._settings.value("windowPosition")
            n_screens = self._settings.value("n_screens", defaultValue=1)
            window_maximized = self._settings.value("windowMaximized", defaultValue='false')
            splitter_state = {}
            for splitter in self._import_preview.findChildren(QSplitter):
                splitter_state[splitter] = self._settings.value(splitter.objectName() + "_splitterState")
            self._settings.endGroup()
            if window_size:
                self.resize(window_size)
            else:
                self.setGeometry(
                    QStyle.alignedRect(
                        Qt.LeftToRight, Qt.AlignCenter, QSize(1000, 700), QApplication.desktop().availableGeometry(self)
                    )
                )
            if window_pos:
                self.move(window_pos)
            if window_maximized == 'true':
                self.setWindowState(Qt.WindowMaximized)
            for splitter, state in splitter_state.items():
                if state:
                    splitter.restoreState(state)
            if len(QGuiApplication.screens()) < int(n_screens):
                # There are less screens available now than on previous application startup
                self.move(0, 0)  # Move this widget to primary screen position (0,0)
        else:
            self.resize(self._preview_window_state["size"])
            self.move(self._preview_window_state["position"])
            self.setWindowState(self._preview_window_state["maximized"])
            for splitter in self._import_preview.findChildren(QSplitter):
                name = splitter.objectName()
                splitter.restoreState(self._preview_window_state["splitters"][name])

    def closeEvent(self, event):
        """Stores window's settings and accepts the event."""
        if self._import_preview is not None:
            self._settings.beginGroup(self._SETTINGS_GROUP_NAME)
            self._settings.setValue("n_screens", len(QGuiApplication.screens()))
            if not self._import_preview.isHidden():
                self._settings.setValue("windowSize", self.size())
                self._settings.setValue("windowPosition", self.pos())
                self._settings.setValue("windowMaximized", self.windowState() == Qt.WindowMaximized)
                for splitter in self._import_preview.findChildren(QSplitter):
                    self._settings.setValue(splitter.objectName() + "_splitterState", splitter.saveState())
            elif self._preview_window_state:
                self._settings.setValue("windowSize", self._preview_window_state["size"])
                self._settings.setValue("windowPosition", self._preview_window_state["position"])
                self._settings.setValue("windowMaximized", self._preview_window_state.maximized)
                for name, state in self._preview_window_state["splitters"]:
                    self._settings.setValue(name + "_splitterState", state)
            self._settings.endGroup()
        event.accept()
예제 #22
0
class QWizardProcessPage(QWizardPage):
    """A QWizards page with a log. Useful for pages that need to capture the output of a process."""
    class _ExecutionManager:
        """A descriptor that stores a QProcessExecutionManager.
        When ``execution_finished`` is emitted, it shows the button to copy the process log.
        """

        public_name = None
        private_name = None

        def __set_name__(self, owner, name):
            self.public_name = name
            self.private_name = '_' + name

        def __get__(self, obj, objtype=None):
            return getattr(obj, self.private_name)

        def __set__(self, obj, value):
            setattr(obj, self.private_name, value)
            try:
                value.execution_finished.connect(obj.widget_copy.show)
            except AttributeError:
                pass

    msg = Signal(str)
    msg_warning = Signal(str)
    msg_error = Signal(str)
    msg_success = Signal(str)
    msg_proc = Signal(str)
    msg_proc_error = Signal(str)

    _exec_mngr = _ExecutionManager()

    def __init__(self, parent):
        super().__init__(parent)
        self._log = MonoSpaceFontTextBrowser(self)
        self._exec_mngr = None
        self._successful = False
        layout = QVBoxLayout(self)
        layout.addWidget(self._log)
        self.widget_copy = QWidget()
        self.widget_copy.hide()
        self._button_copy = QPushButton("Copy log")
        self._label_copy = QLabel("Log copied to clipboard.")
        self._label_copy.hide()
        layout_copy = QHBoxLayout(self.widget_copy)
        layout_copy.addWidget(self._button_copy)
        layout_copy.addWidget(self._label_copy)
        layout_copy.addStretch()
        layout.addWidget(self.widget_copy)
        self._connect_signals()

    def _connect_signals(self):
        self.msg.connect(self._add_msg)
        self.msg_warning.connect(self._add_msg_warning)
        self.msg_error.connect(self._add_msg_error)
        self.msg_success.connect(self._add_msg_succes)
        self.msg_proc.connect(self._add_msg)
        self.msg_proc_error.connect(self._add_msg_error)
        self._button_copy.clicked.connect(self._handle_copy_clicked)

    @Slot(bool)
    def _handle_copy_clicked(self, _=False):
        self._label_copy.show()
        qApp.clipboard().setText(self._log.toPlainText())  # pylint: disable=undefined-variable

    def _add_msg(self, msg):
        self._log.append(format_log_message("msg", msg, show_datetime=False))

    def _add_msg_warning(self, msg):
        self._log.append(
            format_log_message("msg_warning", msg, show_datetime=False))

    def _add_msg_error(self, msg):
        self._log.append(
            format_log_message("msg_error", msg, show_datetime=False))

    def _add_msg_succes(self, msg):
        self._log.append(
            format_log_message("msg_success", msg, show_datetime=False))

    def isComplete(self):
        return self._exec_mngr is None

    def cleanupPage(self):
        super().cleanupPage()
        if self._exec_mngr is not None:
            self._exec_mngr.stop_execution()
        self.msg_error.emit("Aborted by the user")
예제 #23
0
class MonteCarloTab(NewAnalysisTab):
    def __init__(self, parent=None):
        super(MonteCarloTab, self).__init__(parent)
        self.parent: LCAResultsSubTab = parent

        self.layout.addLayout(get_header_layout('Monte Carlo Simulation'))
        self.scenario_label = QLabel("Scenario:")
        self.include_box = QGroupBox("Include uncertainty for:", self)
        grid = QGridLayout()
        self.include_tech = QCheckBox("Technosphere", self)
        self.include_tech.setChecked(True)
        self.include_bio = QCheckBox("Biosphere", self)
        self.include_bio.setChecked(True)
        self.include_cf = QCheckBox("Characterization Factors", self)
        self.include_cf.setChecked(True)
        self.include_parameters = QCheckBox("Parameters", self)
        self.include_parameters.setChecked(True)
        grid.addWidget(self.include_tech, 0, 0)
        grid.addWidget(self.include_bio, 0, 1)
        grid.addWidget(self.include_cf, 1, 0)
        grid.addWidget(self.include_parameters, 1, 1)
        self.include_box.setLayout(grid)

        self.add_MC_ui_elements()

        self.table = LCAResultsTable()
        self.table.table_name = 'MonteCarlo_' + self.parent.cs_name
        self.plot = MonteCarloPlot(self.parent)
        self.plot.hide()
        self.plot.plot_name = 'MonteCarlo_' + self.parent.cs_name
        self.layout.addWidget(self.plot)
        self.export_widget = self.build_export(has_plot=True, has_table=True)
        self.layout.addWidget(self.export_widget)
        self.layout.setAlignment(QtCore.Qt.AlignTop)
        self.connect_signals()

    def connect_signals(self):
        self.button_run.clicked.connect(self.calculate_mc_lca)
        # signals.monte_carlo_ready.connect(self.update_mc)
        # self.combobox_fu.currentIndexChanged.connect(self.update_plot)
        self.combobox_methods.currentIndexChanged.connect(
            # ignore the index and send the cs_name instead
            lambda x: self.update_mc(cs_name=self.parent.cs_name))

        # signals
        # self.radio_button_biosphere.clicked.connect(self.button_clicked)
        # self.radio_button_technosphere.clicked.connect(self.button_clicked)

        if self.using_presamples:
            self.scenario_box.currentIndexChanged.connect(
                self.parent.update_scenario_data)
            self.parent.update_scenario_box_index.connect(
                lambda index: self.set_combobox_index(self.scenario_box, index
                                                      ))

    def add_MC_ui_elements(self):
        layout_mc = QVBoxLayout()

        # H-LAYOUT start simulation
        self.button_run = QPushButton('Run Simulation')
        self.label_iterations = QLabel('Iterations:')
        self.iterations = QLineEdit('10')
        self.iterations.setFixedWidth(40)
        self.iterations.setValidator(QtGui.QIntValidator(1, 1000))
        self.label_seed = QLabel('Random seed:')
        self.label_seed.setToolTip(
            'Seed value (integer) for the random number generator. '
            'Use this for reproducible samples.')
        self.seed = QLineEdit('')
        self.seed.setFixedWidth(30)

        self.hlayout_run = QHBoxLayout()
        self.hlayout_run.addWidget(self.scenario_label)
        self.hlayout_run.addWidget(self.scenario_box)
        self.hlayout_run.addWidget(self.button_run)
        self.hlayout_run.addWidget(self.label_iterations)
        self.hlayout_run.addWidget(self.iterations)
        self.hlayout_run.addWidget(self.label_seed)
        self.hlayout_run.addWidget(self.seed)
        self.hlayout_run.addWidget(self.include_box)
        self.hlayout_run.addStretch(1)
        layout_mc.addLayout(self.hlayout_run)

        # self.label_running = QLabel('Running a Monte Carlo simulation. Please allow some time for this. '
        #                             'Please do not run another simulation at the same time.')
        # self.layout_mc.addWidget(self.label_running)
        # self.label_running.hide()

        # # buttons for all FUs or for all methods
        # self.radio_button_all_fu = QRadioButton("For all functional units")
        # self.radio_button_all_methods = QRadioButton("Technosphere flows")
        #
        # self.radio_button_biosphere.setChecked(True)
        # self.radio_button_technosphere.setChecked(False)
        #
        # self.label_for_all_fu = QLabel('For all functional units')
        # self.combobox_fu = QRadioButton()
        # self.hlayout_fu = QHBoxLayout()

        # FU selection
        # self.label_fu = QLabel('Choose functional unit')
        # self.combobox_fu = QComboBox()
        # self.hlayout_fu = QHBoxLayout()
        #
        # self.hlayout_fu.addWidget(self.label_fu)
        # self.hlayout_fu.addWidget(self.combobox_fu)
        # self.hlayout_fu.addStretch()
        # self.layout_mc.addLayout(self.hlayout_fu)

        # method selection
        self.method_selection_widget = QWidget()
        self.label_methods = QLabel('Choose LCIA method')
        self.combobox_methods = QComboBox()
        self.hlayout_methods = QHBoxLayout()

        self.hlayout_methods.addWidget(self.label_methods)
        self.hlayout_methods.addWidget(self.combobox_methods)
        self.hlayout_methods.addStretch()
        self.method_selection_widget.setLayout(self.hlayout_methods)

        layout_mc.addWidget(self.method_selection_widget)
        self.method_selection_widget.hide()

        self.layout.addLayout(layout_mc)

    def build_export(self,
                     has_table: bool = True,
                     has_plot: bool = True) -> QWidget:
        """Construct the export layout but set it into a widget because we
         want to hide it."""
        export_layout = super().build_export(has_table, has_plot)
        export_widget = QWidget()
        export_widget.setLayout(export_layout)
        # Hide widget until MC is calculated
        export_widget.hide()
        return export_widget

    @QtCore.Slot(name="calculateMcLca")
    def calculate_mc_lca(self):
        self.method_selection_widget.hide()
        self.plot.hide()
        self.export_widget.hide()

        iterations = int(self.iterations.text())
        seed = None
        if self.seed.text():
            print('SEED: ', self.seed.text())
            try:
                seed = int(self.seed.text())
            except ValueError:
                traceback.print_exc()
                QMessageBox.warning(
                    self, 'Warning',
                    'Seed value must be an integer number or left empty.')
                self.seed.setText('')
                return
        includes = {
            "technosphere": self.include_tech.isChecked(),
            "biosphere": self.include_bio.isChecked(),
            "cf": self.include_cf.isChecked(),
            "parameters": self.include_parameters.isChecked(),
        }

        try:
            self.parent.mc.calculate(iterations=iterations,
                                     seed=seed,
                                     **includes)
            self.update_mc()
        except InvalidParamsError as e:  # This can occur if uncertainty data is missing or otherwise broken
            # print(e)
            traceback.print_exc()
            QMessageBox.warning(self,
                                'Could not perform Monte Carlo simulation',
                                str(e))

        # a threaded way for this - unfortunatley this crashes as:
        # pypardsio_solver is used for the 'spsolve' and 'factorized' functions. Python crashes on windows if multiple
        # instances of PyPardisoSolver make calls to the Pardiso library
        # worker_thread = WorkerThread()
        # print('Created local worker_thread')
        # worker_thread.set_mc(self.parent.mc, iterations=iterations)
        # print('Passed object to thread.')
        # worker_thread.start()
        # self.label_running.show()

        #

        # thread = NewCSMCThread() #self.parent.mc
        # thread.calculation_finished.connect(
        #     lambda x: print('Calculation finished.'))
        # thread.start()

        # # give us a thread and start it
        # thread = QtCore.QThread()
        # thread.start()
        #
        # # create a worker and move it to our extra thread
        # worker = Worker()
        # worker.moveToThread(thread)

        # self.parent.mct.start()
        # self.parent.mct.run(iterations=iterations)
        # self.parent.mct.finished()

        # objThread = QtCore.QThread()
        # obj = QObjectMC()  # self.parent.cs_name
        # obj.moveToThread(objThread)
        # obj.finished.connect(objThread.quit)
        # objThread.started.connect(obj.long_running)
        # # objThread.finished.connect(app.exit)
        # objThread.finished.connect(
        #     lambda x: print('Finished Thread!')
        # )
        # objThread.start()

        # objThread = QtCore.QThread()
        # obj = SomeObject()
        # obj.moveToThread(objThread)
        # obj.finished.connect(objThread.quit)
        # objThread.started.connect(obj.long_running)
        # objThread.finished.connect(
        #     lambda x: print('Finished Thread!')
        # )
        # objThread.start()

        # self.method_selection_widget.show()
        # self.plot.show()
        # self.export_widget.show()

    def configure_scenario(self):
        super().configure_scenario()
        self.scenario_label.setVisible(self.using_presamples)

    def update_tab(self):
        self.update_combobox(self.combobox_methods,
                             [str(m) for m in self.parent.mc.methods])
        # self.update_combobox(self.combobox_methods, [str(m) for m in self.parent.mct.mc.methods])

    def update_mc(self, cs_name=None):
        # act = self.combobox_fu.currentText()
        # activity_index = self.combobox_fu.currentIndex()
        # act_key = self.parent.mc.activity_keys[activity_index]
        # if cs_name != self.parent.cs_name:  # relevant if several CS are open at the same time
        #     return

        # self.label_running.hide()
        self.method_selection_widget.show()
        self.export_widget.show()

        method_index = self.combobox_methods.currentIndex()
        method = self.parent.mc.methods[method_index]

        # data = self.parent.mc.get_results_by(act_key=act_key, method=method)
        self.df = self.parent.mc.get_results_dataframe(method=method)

        self.update_table()
        self.update_plot(method=method)
        filename = '_'.join([
            str(x)
            for x in [self.parent.cs_name, 'Monte Carlo results',
                      str(method)]
        ])
        self.plot.plot_name, self.table.table_name = filename, filename

    def update_plot(self, method):
        idx = self.layout.indexOf(self.plot)
        self.plot.figure.clf()
        self.plot.deleteLater()
        self.plot = MonteCarloPlot(self.parent)
        self.layout.insertWidget(idx, self.plot)
        self.plot.plot(self.df, method=method)
        self.plot.show()
        if self.layout.parentWidget():
            self.layout.parentWidget().updateGeometry()

    def update_table(self):
        self.table.sync(self.df)
예제 #24
0
class QVTKRenderWindowInteractor(QVTKRWIBaseClass):


    """ A QVTKRenderWindowInteractor for Python and Qt.  Uses a
    vtkGenericRenderWindowInteractor to handle the interactions.  Use
    GetRenderWindow() to get the vtkRenderWindow.  Create with the
    keyword stereo=1 in order to generate a stereo-capable window.

    The user interface is summarized in vtkInteractorStyle.h:

    - Keypress j / Keypress t: toggle between joystick (position
    sensitive) and trackball (motion sensitive) styles. In joystick
    style, motion occurs continuously as long as a mouse button is
    pressed. In trackball style, motion occurs when the mouse button
    is pressed and the mouse pointer moves.

    - Keypress c / Keypress o: toggle between camera and object
    (actor) modes. In camera mode, mouse events affect the camera
    position and focal point. In object mode, mouse events affect
    the actor that is under the mouse pointer.

    - Button 1: rotate the camera around its focal point (if camera
    mode) or rotate the actor around its origin (if actor mode). The
    rotation is in the direction defined from the center of the
    renderer's viewport towards the mouse position. In joystick mode,
    the magnitude of the rotation is determined by the distance the
    mouse is from the center of the render window.

    - Button 2: pan the camera (if camera mode) or translate the actor
    (if object mode). In joystick mode, the direction of pan or
    translation is from the center of the viewport towards the mouse
    position. In trackball mode, the direction of motion is the
    direction the mouse moves. (Note: with 2-button mice, pan is
    defined as <Shift>-Button 1.)

    - Button 3: zoom the camera (if camera mode) or scale the actor
    (if object mode). Zoom in/increase scale if the mouse position is
    in the top half of the viewport; zoom out/decrease scale if the
    mouse position is in the bottom half. In joystick mode, the amount
    of zoom is controlled by the distance of the mouse pointer from
    the horizontal centerline of the window.

    - Keypress 3: toggle the render window into and out of stereo
    mode.  By default, red-blue stereo pairs are created. Some systems
    support Crystal Eyes LCD stereo glasses; you have to invoke
    SetStereoTypeToCrystalEyes() on the rendering window.  Note: to
    use stereo you also need to pass a stereo=1 keyword argument to
    the constructor.

    - Keypress e: exit the application.

    - Keypress f: fly to the picked point

    - Keypress p: perform a pick operation. The render window interactor
    has an internal instance of vtkCellPicker that it uses to pick.

    - Keypress r: reset the camera view along the current view
    direction. Centers the actors and moves the camera so that all actors
    are visible.

    - Keypress s: modify the representation of all actors so that they
    are surfaces.

    - Keypress u: invoke the user-defined function. Typically, this
    keypress will bring up an interactor that you can type commands in.

    - Keypress w: modify the representation of all actors so that they
    are wireframe.
    """

    # Map between VTK and Qt cursors.
    _CURSOR_MAP = {
        0:  Qt.ArrowCursor,          # VTK_CURSOR_DEFAULT
        1:  Qt.ArrowCursor,          # VTK_CURSOR_ARROW
        2:  Qt.SizeBDiagCursor,      # VTK_CURSOR_SIZENE
        3:  Qt.SizeFDiagCursor,      # VTK_CURSOR_SIZENWSE
        4:  Qt.SizeBDiagCursor,      # VTK_CURSOR_SIZESW
        5:  Qt.SizeFDiagCursor,      # VTK_CURSOR_SIZESE
        6:  Qt.SizeVerCursor,        # VTK_CURSOR_SIZENS
        7:  Qt.SizeHorCursor,        # VTK_CURSOR_SIZEWE
        8:  Qt.SizeAllCursor,        # VTK_CURSOR_SIZEALL
        9:  Qt.PointingHandCursor,   # VTK_CURSOR_HAND
        10: Qt.CrossCursor,          # VTK_CURSOR_CROSSHAIR
    }

    def __init__(self, parent=None, **kw):
        # the current button
        self._ActiveButton = Qt.NoButton

        # private attributes
        self.__saveX = 0
        self.__saveY = 0
        self.__saveModifiers = Qt.NoModifier
        self.__saveButtons = Qt.NoButton
        self.__wheelDelta = 0

        # do special handling of some keywords:
        # stereo, rw

        try:
            stereo = bool(kw['stereo'])
        except KeyError:
            stereo = False

        try:
            rw = kw['rw']
        except KeyError:
            rw = None

        # create base qt-level widget
        if QVTKRWIBase == "QWidget":
            if "wflags" in kw:
                wflags = kw['wflags']
            else:
                wflags = Qt.WindowFlags()
            QWidget.__init__(self, parent, wflags | Qt.MSWindowsOwnDC)
        elif QVTKRWIBase == "QGLWidget":
            QGLWidget.__init__(self, parent)

        if rw: # user-supplied render window
            self._RenderWindow = rw
        else:
            self._RenderWindow = vtk.vtkRenderWindow()

        WId = self.winId()

        # Python2
        if type(WId).__name__ == 'PyCObject':
            from ctypes import pythonapi, c_void_p, py_object

            pythonapi.PyCObject_AsVoidPtr.restype  = c_void_p
            pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]

            WId = pythonapi.PyCObject_AsVoidPtr(WId)

        # Python3
        elif type(WId).__name__ == 'PyCapsule':
            from ctypes import pythonapi, c_void_p, py_object, c_char_p

            pythonapi.PyCapsule_GetName.restype = c_char_p
            pythonapi.PyCapsule_GetName.argtypes = [py_object]

            name = pythonapi.PyCapsule_GetName(WId)

            pythonapi.PyCapsule_GetPointer.restype  = c_void_p
            pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p]

            WId = pythonapi.PyCapsule_GetPointer(WId, name)

        self._RenderWindow.SetWindowInfo(str(int(WId)))

        if stereo: # stereo mode
            self._RenderWindow.StereoCapableWindowOn()
            self._RenderWindow.SetStereoTypeToCrystalEyes()

        try:
            self._Iren = kw['iren']
        except KeyError:
            self._Iren = vtk.vtkGenericRenderWindowInteractor()
            self._Iren.SetRenderWindow(self._RenderWindow)

        # do all the necessary qt setup
        self.setAttribute(Qt.WA_OpaquePaintEvent)
        self.setAttribute(Qt.WA_PaintOnScreen)
        self.setMouseTracking(True) # get all mouse events
        self.setFocusPolicy(Qt.WheelFocus)
        self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))

        self._Timer = QTimer(self)
        self._Timer.timeout.connect(self.TimerEvent)

        self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
        self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)
        self._Iren.GetRenderWindow().AddObserver('CursorChangedEvent',
                                                 self.CursorChangedEvent)

        #Create a hidden child widget and connect its destroyed signal to its
        #parent ``Finalize`` slot. The hidden children will be destroyed before
        #its parent thus allowing cleanup of VTK elements.
        self._hidden = QWidget(self)
        self._hidden.hide()
        self._hidden.destroyed.connect(self.Finalize)

    def __getattr__(self, attr):
        """Makes the object behave like a vtkGenericRenderWindowInteractor"""
        if attr == '__vtk__':
            return lambda t=self._Iren: t
        elif hasattr(self._Iren, attr):
            return getattr(self._Iren, attr)
        else:
            raise AttributeError(self.__class__.__name__ +
                  " has no attribute named " + attr)

    def Finalize(self):
        '''
        Call internal cleanup method on VTK objects
        '''
        self._RenderWindow.Finalize()

    def CreateTimer(self, obj, evt):
        self._Timer.start(10)

    def DestroyTimer(self, obj, evt):
        self._Timer.stop()
        return 1

    def TimerEvent(self):
        self._Iren.TimerEvent()

    def CursorChangedEvent(self, obj, evt):
        """Called when the CursorChangedEvent fires on the render window."""
        # This indirection is needed since when the event fires, the current
        # cursor is not yet set so we defer this by which time the current
        # cursor should have been set.
        QTimer.singleShot(0, self.ShowCursor)

    def HideCursor(self):
        """Hides the cursor."""
        self.setCursor(Qt.BlankCursor)

    def ShowCursor(self):
        """Shows the cursor."""
        vtk_cursor = self._Iren.GetRenderWindow().GetCurrentCursor()
        qt_cursor = self._CURSOR_MAP.get(vtk_cursor, Qt.ArrowCursor)
        self.setCursor(qt_cursor)

    def closeEvent(self, evt):
        self.Finalize()

    def sizeHint(self):
        return QSize(400, 400)

    def paintEngine(self):
        return None

    def paintEvent(self, ev):
        self._Iren.Render()

    def resizeEvent(self, ev):
        w = self.width()
        h = self.height()
        vtk.vtkRenderWindow.SetSize(self._RenderWindow, w, h)
        self._Iren.SetSize(w, h)
        self._Iren.ConfigureEvent()
        self.update()

    def _GetCtrlShift(self, ev):
        ctrl = shift = False

        if hasattr(ev, 'modifiers'):
            if ev.modifiers() & Qt.ShiftModifier:
                shift = True
            if ev.modifiers() & Qt.ControlModifier:
                ctrl = True
        else:
            if self.__saveModifiers & Qt.ShiftModifier:
                shift = True
            if self.__saveModifiers & Qt.ControlModifier:
                ctrl = True

        return ctrl, shift

    def enterEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, chr(0), 0, None)
        self._Iren.EnterEvent()

    def leaveEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, chr(0), 0, None)
        self._Iren.LeaveEvent()

    def mousePressEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        repeat = 0
        if ev.type() == QEvent.MouseButtonDblClick:
            repeat = 1
        self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
                                            ctrl, shift, chr(0), repeat, None)

        self._ActiveButton = ev.button()

        if self._ActiveButton == Qt.LeftButton:
            self._Iren.LeftButtonPressEvent()
        elif self._ActiveButton == Qt.RightButton:
            self._Iren.RightButtonPressEvent()
        elif self._ActiveButton == Qt.MidButton:
            self._Iren.MiddleButtonPressEvent()

    def mouseReleaseEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
                                            ctrl, shift, chr(0), 0, None)

        if self._ActiveButton == Qt.LeftButton:
            self._Iren.LeftButtonReleaseEvent()
        elif self._ActiveButton == Qt.RightButton:
            self._Iren.RightButtonReleaseEvent()
        elif self._ActiveButton == Qt.MidButton:
            self._Iren.MiddleButtonReleaseEvent()

    def mouseMoveEvent(self, ev):
        self.__saveModifiers = ev.modifiers()
        self.__saveButtons = ev.buttons()
        self.__saveX = ev.x()
        self.__saveY = ev.y()

        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
                                            ctrl, shift, chr(0), 0, None)
        self._Iren.MouseMoveEvent()

    def keyPressEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        if ev.key() < 256:
            key = str(ev.text())
        else:
            key = chr(0)

        keySym = _qt_key_to_key_sym(ev.key())
        if shift and len(keySym) == 1 and keySym.isalpha():
            keySym = keySym.upper()

        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, key, 0, keySym)
        self._Iren.KeyPressEvent()
        self._Iren.CharEvent()

    def keyReleaseEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        if ev.key() < 256:
            key = chr(ev.key())
        else:
            key = chr(0)

        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, key, 0, None)
        self._Iren.KeyReleaseEvent()

    def wheelEvent(self, ev):
        if hasattr(ev, 'delta'):
            self.__wheelDelta += ev.delta()
        else:
            self.__wheelDelta += ev.angleDelta().y()

        if self.__wheelDelta >= 120:
            self._Iren.MouseWheelForwardEvent()
            self.__wheelDelta = 0
        elif self.__wheelDelta <= -120:
            self._Iren.MouseWheelBackwardEvent()
            self.__wheelDelta = 0

    def GetRenderWindow(self):
        return self._RenderWindow

    def Render(self):
        self.update()