コード例 #1
0
class MainWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.initMe()

    def initMe(self):
        self.v = QVBoxLayout()
        self.h = QHBoxLayout()
        # mod_window = QGridLayout()

        scroll = QScrollArea()
        self.h.addWidget(scroll)
        scroll.setWidgetResizable(True)
        self.scrollcontent = QListWidget(scroll)

        for mod in mods:
            a = QListWidgetItem(mod.name)
            a.mod = mod
            self.scrollcontent.addItem(a)

        scroll.setWidget(self.scrollcontent)

        self.scrollcontent.itemSelectionChanged.connect(self.update_RPanel)

        self.mod_info = RPanel(mods[0])
        self.h.addWidget(self.mod_info)
        self.mod_info.show()

        searchbox = SearchBox(self)
        self.v.addWidget(searchbox)
        searchbox.show()

        self.v.addLayout(self.h)
        self.setLayout(self.v)
        # self.show()

    def update_RPanel(self):
        self.mod_info.setParent(None)
        self.mod_info.pixmap = None
        items = self.scrollcontent.selectedItems()
        if not items:
            return
        item = items[0]
        self.mod_info = RPanel(item.mod)
        self.h.addWidget(self.mod_info)
        self.mod_info.show()

    def update_RPanel_With_Search(self, keyword):
        items = self.scrollcontent.findItems(keyword, Qt.MatchContains)
        if not items:
            self.error = ErrorBox("No Mod found with matching name")
        else:
            item = items[0]
            item.setSelected(True)
            self.scrollcontent.scrollToItem(item,
                                            QAbstractItemView.PositionAtTop)
コード例 #2
0
 def remove_faulty(node_string,
                   list_name: QtWidgets.QListWidget,
                   all_elements=True):
     """Removes duplicates and nodes that shouldn't be on the list"""
     qlistitem = list_name.findItems(node_string, QtCore.Qt.MatchExactly)
     if qlistitem:
         if all_elements:
             for node in qlistitem:
                 list_name.takeItem(list_name.row(node))
         elif len(qlistitem) > 1:
             qlistitem.reverse()
             list_name.takeItem(list_name.row(qlistitem[0]))
コード例 #3
0
class CustomFunctionsPanel(QWidget):
    save_script = QtCore.pyqtSignal()

    def __init__(self, settings):
        super().__init__()
        self.settings = settings

        # Initializing GUI elements
        self.lib_name = QLineEdit()
        self.functions_list = QListWidget()
        self.save_script_button = QPushButton('Save build script')

        # Preparing elements by giving initial values and etc
        self.lib_name.setPlaceholderText('Custom library name...')

        # Setting all widgets in their places
        layout = QVBoxLayout()
        layout.addWidget(self.lib_name)
        layout.addWidget(self.functions_list)
        layout.addWidget(self.save_script_button)
        self.setLayout(layout)

        self.save_script_button.clicked.connect(self.on_save_script)
        self.settings.package_changed.connect(self.reset)

    def on_save_script(self):
        self.save_script.emit()

    def reset(self):
        self.functions_list.clear()

    def add_function(self, function):
        """
        Adds new function to required list

        :param function: name if function
        """
        self.functions_list.addItem(QListWidgetItem(function))

    def remove_function(self, function):
        """
        Removes function from left list
        """
        item = self.functions_list.findItems(function, QtCore.Qt.MatchExactly)
        if item:
            self.functions_list.takeItem(self.functions_list.row(item[0]))
コード例 #4
0
    def find_list_widget_item(self, list_widget: QListWidget):
        """
        Находит и окрашивает выбраный в списке несовпадений элемент
        в заданном списке.
        :param list_widget: Виджет списка.
        :return:
        """
        if self.sender().currentItem().text() != '':
            for index in range(list_widget.count()):
                list_widget.item(index).setBackground(QColor(255, 255, 255))

            items = list_widget.findItems(
                self.sender().currentItem().text(),
                QtCore.Qt.MatchExactly)

            if len(items) > 0:
                for item in items:
                    item.setBackground(QColor("red"))
                    list_widget.scrollToItem(item,
                                             QAbstractItemView.PositionAtCenter)
コード例 #5
0
    def fill_list_widget(self, dictionary: dict,
                         list_widget: QListWidget, counter=None):
        """
        Заполняет list widget словарем.
        :param dictionary: Словарь.
        :param list_widget: Объект list widget
        :param counter: Счетчик (только для поиска несовпадений).
        :return:
        """
        for key in sorted(dictionary):
            list_widget.addItem(key)
            items = list_widget.findItems(key, QtCore.Qt.MatchExactly)

            if len(items) > 0:
                for item in items:
                    item.setFont(QFont("Serif", 10, QFont.Bold))

            for i in sorted(dictionary[key]):
                list_widget.addItem(i)
                if counter is not None:
                    counter[0] += 1

            list_widget.addItem('')
コード例 #6
0
ファイル: recycleApp.py プロジェクト: stich9208/SWP2_AD
class RecycleApp(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.dataController = DataController()

        # load data
        try:
            self.dataController.loadData()
        except (EOFError):
            self.dataController.parsingData()
            self.dataController.saveData(self.dataController.recycles)
        finally:
            self.recycleData = self.dataController.loadData()

        # listWindow
        self.listWindow = QListWidget(self)
        self.listWindow.setMinimumSize(700, 200)
        for data in self.recycleData:
            item = QListWidgetItem()
            item.setText(data["name"])
            self.listWindow.addItem(item)
        self.listWindow.sortItems()

        # list layout
        listLayout = QGridLayout()

        listLayout.addWidget(self.listWindow, 0, 0)

        # status layout
        statusLayout = QGridLayout()

        # dump label
        self.dumpTitle = QLabel("How to dump")
        self.dumpTitle.setAlignment(Qt.AlignLeft)
        font = self.dumpTitle.font()
        font.setPointSize(font.pointSize() + 8)
        font.setWeight(75)
        self.dumpTitle.setFont(font)
        statusLayout.addWidget(self.dumpTitle, 0, 0, 1, 3)
        # dump result
        self.dumpResult = QLineEdit()
        self.dumpResult.setReadOnly(True)
        statusLayout.addWidget(self.dumpResult, 1, 0, 1, 2)
        # dump check button
        self.checkButton = QToolButton()
        self.checkButton.setText("check")
        self.checkButton.setMinimumWidth(60)
        self.checkButton.clicked.connect(self.checkBtnClicked)
        statusLayout.addWidget(self.checkButton, 1, 2)

        # add name label
        self.addNameLabel = QLabel("Name: ")
        statusLayout.addWidget(self.addNameLabel, 2, 0)
        # add name
        self.addName = QLineEdit()
        statusLayout.addWidget(self.addName, 2, 1)
        # add dump label
        self.addDumpLabel = QLabel("Dump: ")
        statusLayout.addWidget(self.addDumpLabel, 3, 0)
        # add dump
        self.addDump = QLineEdit()
        statusLayout.addWidget(self.addDump, 3, 1)
        # add button
        self.addButton = QToolButton()
        self.addButton.setText("Add")
        self.addButton.setMinimumHeight(50)
        self.addButton.setMinimumWidth(60)
        self.addButton.clicked.connect(self.addBtnClicked)
        statusLayout.addWidget(self.addButton, 2, 2, 2, 1)

        # search
        self.searchName = QLineEdit()
        self.searchName.setPlaceholderText("search for name")
        statusLayout.addWidget(self.searchName, 4, 0, 1, 2)
        # search button
        self.searchButton = QToolButton()
        self.searchButton.setText("search")
        self.searchButton.setMinimumWidth(60)
        self.searchButton.clicked.connect(self.searchBtnClicked)
        statusLayout.addWidget(self.searchButton, 4, 2)

        # delete
        self.deleteName = QLineEdit()
        self.deleteName.setPlaceholderText("delete for name")
        statusLayout.addWidget(self.deleteName, 5, 0, 1, 2)
        #delete button
        self.deleteButton = QToolButton()
        self.deleteButton.setText("delete")
        self.deleteButton.setMinimumWidth(60)
        self.deleteButton.clicked.connect(self.delBtnClicked)
        statusLayout.addWidget(self.deleteButton, 5, 2)

        # mainlayout
        mainLayout = QGridLayout()
        mainLayout.addLayout(listLayout, 0, 0)
        mainLayout.addLayout(statusLayout, 1, 0)

        # alert
        self.alert = QMessageBox()

        self.setWindowTitle('분리배출')
        self.setLayout(mainLayout)

    def checkBtnClicked(self):
        for item in self.recycleData:
            if item["name"] == self.listWindow.currentItem().text():
                self.dumpResult.setText(item["dump"])

    def addBtnClicked(self):
        if self.addName.text() == "" or self.addDump.text() == "":
            self.alert.information(self, "alert", "can`t add blank")
        else:
            same = self.listWindow.findItems(self.addName.text(),
                                             Qt.MatchExactly)
            if len(same) > 0:
                self.alert.information(self, "alert", "already exist")
            else:
                item = {}
                item["name"] = self.addName.text()
                item["dump"] = self.addDump.text()
                self.recycleData.append(item)
                # save data to recycle.dat
                self.dataController.saveData(self.recycleData)

                self.listWindow.addItem(self.addName.text())
                self.listWindow.repaint()
                self.listWindow.sortItems()
                self.addName.clear()
                self.addDump.clear()

    def searchBtnClicked(self):
        sameList = self.listWindow.findItems(self.searchName.text(),
                                             Qt.MatchExactly)
        if len(sameList) > 0:
            sameList[0].setSelected(True)
            self.listWindow.scrollToItem(sameList[0],
                                         QAbstractItemView.PositionAtTop)
            self.searchName.clear()
        else:
            self.alert.information(self, "alert", "no such item")

    def delBtnClicked(self):
        deleteList = self.listWindow.findItems(self.deleteName.text(),
                                               Qt.MatchExactly)
        if len(deleteList) > 0:
            for item in self.recycleData:
                if item["name"] == self.deleteName.text():
                    self.recycleData.remove(item)
            # save data to recycle.dat
            self.dataController.saveData(self.recycleData)

            self.listWindow.takeItem(self.listWindow.row(deleteList[0]))
            self.deleteName.clear()
        else:
            self.alert.information(self, "alert", "no such item")
コード例 #7
0
class MainWindow(CenterWindow):
    """
    Displays list with tasks assigned to current user in JIRA
    """
    def __init__(self, controller):
        super().__init__()
        self.setStyleSheet(QSS)
        self.controller = controller
        self.resize(1000, 600)
        self.setWindowTitle('JIRA Quick Reporter')
        self.setWindowIcon(QIcon(LOGO_PATH))
        self.center()
        self.current_item = None

        self.vbox = QVBoxLayout()

        self.save_btn_box = QHBoxLayout()
        self.filter_name_label = QLabel()
        self.filter_name_label.setObjectName('filter_name_label')
        self.filter_name_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.filter_name_label.setAlignment(Qt.AlignLeft)
        self.filter_edited_label = QLabel('-> edited')
        self.filter_edited_label.setObjectName('filter_edited_label')
        self.filter_edited_label.hide()
        self.filter_edited_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.save_filter_btn = QPushButton('Save as')
        self.save_filter_btn.setObjectName('save_filter_btn')
        self.save_filter_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.save_filter_btn.clicked.connect(self.controller.save_filter)

        self.overwrite_filter_button = QPushButton('Save')
        self.overwrite_filter_button.setToolTip('You need to edit filter query first')
        self.overwrite_filter_button.setObjectName('save_filter_btn')
        self.overwrite_filter_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.overwrite_filter_button.clicked.connect(lambda: self.controller.save_filter(True))

        self.delete_filter_btn = QPushButton()
        self.delete_filter_btn.setObjectName('delete_filter_btn')
        self.delete_filter_btn.clicked.connect(self.delete_filter)
        self.delete_filter_btn.setIcon(QIcon(DELETE_FILTER_ICON))
        self.delete_filter_btn.setIconSize(
            QSize(
                self.delete_filter_btn.sizeHint().height(),
                self.delete_filter_btn.sizeHint().height()
            )
        )
        self.delete_filter_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.delete_filter_btn.setToolTip('Delete filter')

        self.save_btn_box.addWidget(self.filter_name_label, Qt.AlignLeft)
        self.save_btn_box.addWidget(self.filter_edited_label, Qt.AlignLeft)
        self.save_btn_box.addWidget(self.save_filter_btn, Qt.AlignLeft)
        self.save_btn_box.addWidget(self.overwrite_filter_button, Qt.AlignLeft)
        self.save_btn_box.addStretch()
        self.save_btn_box.addWidget(self.delete_filter_btn, Qt.AlignRight)

        self.create_filter_box = QHBoxLayout()
        self.query_field = QLineEdit()
        self.query_field.setObjectName('query_field')
        self.query_field.setPlaceholderText('You need to write a query here')
        self.action_help = QAction()
        self.action_help.setIcon(self.style().standardIcon(QStyle.SP_MessageBoxQuestion))
        self.help_filter_url = QUrl(FILTER_FIELD_HELP_URL)
        self.action_help.triggered.connect(self.filter_field_help)
        self.query_field.addAction(self.action_help, QLineEdit.TrailingPosition)
        self.query_field.installEventFilter(self)
        self.search_issues_button = QPushButton('Search')
        self.search_issues_button.setObjectName('search_issues_button')
        self.search_issues_button.clicked.connect(self.controller.search_issues_by_query)
        self.create_filter_box.addWidget(self.query_field)
        self.create_filter_box.addWidget(self.search_issues_button)

        self.list_box = QVBoxLayout()
        self.issue_list_widget = QListWidget()
        self.issue_list_widget.setObjectName('issue_list')
        self.label_info = QLabel('You have no issues.')
        self.label_info.setAlignment(Qt.AlignCenter)
        self.list_box.addWidget(self.issue_list_widget)
        self.list_box.addWidget(self.label_info)
        self.label_info.hide()

        self.vbox.addLayout(self.save_btn_box)
        self.vbox.addLayout(self.create_filter_box)
        self.vbox.addLayout(self.list_box)

        self.filters_frame = QFrame()
        self.filters_frame.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
        self.filters_frame.setFrameShape(QFrame.StyledPanel)
        self.filters_frame.setObjectName('filters_frame')
        self.filters_box = QVBoxLayout(self.filters_frame)
        self.filters_box_label = QLabel('Issues and filters')
        self.filters_box_label.setObjectName('filters_box_label')
        self.filters_box.addWidget(self.filters_box_label)
        self.filters_list = QListWidget()
        self.filters_list.installEventFilter(self)
        self.filters_list.itemClicked.connect(self.on_filter_selected)
        self.filters_list.setObjectName('filters_list')
        self.filters_list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.filters_box.addWidget(self.filters_list)
        self.add_filter_button = QPushButton('+')
        self.add_filter_button.clicked.connect(self.add_filter_btn_click)
        self.add_filter_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.filters_box.addWidget(self.add_filter_button, alignment=Qt.AlignRight)

        self.btn_box = QHBoxLayout()
        self.refresh_btn = QPushButton('Refresh')
        self.refresh_btn.clicked.connect(self.controller.refresh_issue_list)
        self.btn_box.addWidget(self.refresh_btn, alignment=Qt.AlignRight)
        self.vbox.addLayout(self.btn_box)

        self.toggle_frame_filters_btn = QPushButton('<')
        self.toggle_frame_filters_btn.clicked.connect(self.toggle_frame_filters)
        self.toggle_frame_filters_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.toggle_frame_filters_btn.setObjectName('toggle_filters_btn')

        self.main_box = QHBoxLayout()
        self.main_box.addWidget(self.filters_frame)
        self.main_box.addWidget(self.toggle_frame_filters_btn, alignment=Qt.AlignTop)
        self.main_box.addLayout(self.vbox)
        self.setLayout(self.main_box)

        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(QIcon(LOGO_PATH))

        self.tray_menu = QMenu()
        self.action_open = QAction('Open JQR', self)
        self.action_quit = QAction('Quit JQR', self)
        self.tray_menu.addAction(self.action_open)
        self.action_open.triggered.connect(self.show_jqr_from_tray)
        self.tray_menu.addAction(self.action_quit)
        self.action_quit.triggered.connect(self.controller.quit_app)
        self.tray_icon.setContextMenu(self.tray_menu)
        self.tray_icon.show()
        self.timer_log_work = QTimer()
        self.timer_log_work.timeout.connect(self.notification_to_log_work)
        self.timer_log_work.start(LOG_TIME)

        self.timer_refresh = QTimer()
        self.timer_refresh.timeout.connect(self.controller.auto_refresh_issue_list)

    def show_jqr_from_tray(self):
        self.hide()
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
        self.activateWindow()
        self.show()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Return:
            self.controller.search_issues_by_query()

    def notification_to_log_work(self):
        QSound.play(RING_SOUND_PATH)
        self.tray_icon.showMessage(
            '1 hour had passed',
            'Don\'t forget to log your work!',
            msecs=2000
        )
        self.timer_log_work.start(LOG_TIME)

    def update_issues(self, update_list):
        for issue in update_list:
            item = self.issue_list_widget.findItems(
                issue['key'], Qt.MatchExactly
            )[0]
            item.setText(issue['key'])
            issue_widget = self.issue_list_widget.itemWidget(item)
            issue_widget.set_issue_key(issue['key'], issue['link'])
            issue_widget.set_issue_title(issue['title'])
            issue_widget.set_time(
                issue['estimated'],
                issue['logged'],
                issue['remaining']
            )
            issue_widget.set_workflow.clear()
            issue_widget.set_workflow.addItems(self.controller.get_possible_workflows(issue))
            issue_widget.set_workflow.setCurrentIndex(0)

            issue_widget.set_workflow.activated[str].disconnect()
            issue_widget.set_workflow.activated[str].connect(
                partial(
                    self.controller.change_workflow,
                    issue['workflow'],
                    issue['issue_obj'],
                )
            )

    def delete_issues(self, delete_list):
        for issue in delete_list:
            item = self.issue_list_widget.findItems(
                issue['key'], Qt.MatchExactly
            )[0]
            self.issue_list_widget.takeItem(
                self.issue_list_widget.row(item)
            )

    def insert_issues(self, new_issues_list):
        for issue in new_issues_list:
            issue_widget = QCustomWidget()
            issue_widget.set_issue_key(issue['key'], issue['link'])
            issue_widget.set_issue_title(issue['title'])
            issue_widget.set_time(
                issue['estimated'],
                issue['logged'],
                issue['remaining']
            )

            issue_widget.quick_log_btn.clicked.connect(
                partial(
                    self.controller.log_work_from_list,
                    issue['key']
                )
            )

            issue_widget.log_work_btn.clicked.connect(
                partial(
                    self.controller.open_time_log,
                    issue['key']
                )
            )

            issue_widget.open_pomodoro_btn.clicked.connect(
                partial(
                    self.controller.open_pomodoro_window,
                    issue['key'], issue['title']
                )
            )

            # add workflow statuses to dropdown
            possible_workflows = self.controller.get_possible_workflows(issue)

            issue_widget.set_workflow.addItems(possible_workflows)
            issue_widget.set_workflow.setCurrentIndex(0)
            issue_widget.set_workflow.activated[str].connect(
                partial(
                    self.controller.change_workflow,
                    issue['workflow'],
                    issue['issue_obj'],
                )
            )

            # add issue item to list
            issue_list_widget_item = QListWidgetItem()
            issue_list_widget_item.setText(issue['key'])
            issue_list_widget_item.setSizeHint(issue_widget.sizeHint())
            self.issue_list_widget.insertItem(issue['index'], issue_list_widget_item)
            self.issue_list_widget.setItemWidget(
                issue_list_widget_item, issue_widget
            )
        self.set_size_hint()

    def set_size_hint(self):
        self.issue_list_widget.setMinimumWidth(
            self.issue_list_widget.sizeHintForColumn(0) + 50
        )
        self.issue_list_widget.setMinimumHeight(
            self.issue_list_widget.sizeHintForRow(0) * 2
        )

    def show_filters(self, filters_dict):
        for index, key in enumerate(filters_dict):
            if key == SEARCH_ITEM_NAME:
                self.filters_list.insertItem(0, key)
            else:
                self.filters_list.addItem(key)
                self.filters_list.item(index).setToolTip(key)
        self.filters_list.item(0).setText(self.filters_list.item(0).text().capitalize())

        # add separator after first item
        separator = QFrame()
        separator.setFrameShape(QFrame.HLine)
        separator.setObjectName('separator')
        item_separator = QListWidgetItem()
        item_separator.setFlags(Qt.NoItemFlags)
        self.filters_list.insertItem(1, item_separator)
        self.filters_list.setItemWidget(item_separator, separator)

        self.filters_list.setCurrentItem(
            self.filters_list.findItems(
                MY_ISSUES_ITEM_NAME, Qt.MatchExactly
            )[0])

        self.filters_list.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
        self.on_filter_selected(self.filters_list.currentItem())

    def filter_field_help(self):
        QDesktopServices.openUrl(self.help_filter_url)

    def on_filter_selected(self, item):
        if not item.text():
            return
        self.current_item = item
        if len(self.current_item.text()) > 50:
            set_text = '{}...'.format(self.current_item.text()[:50])
        else:
            set_text = self.current_item.text()
        self.issue_list_widget.scrollToTop()
        self.controller.search_issues_by_filter_name(item.text())
        self.filter_name_label.setText(set_text)
        self.filter_edited_label.hide()

        if self.filters_list.currentItem().text() == MY_ISSUES_ITEM_NAME:
            self.save_filter_btn.hide()
            self.overwrite_filter_button.hide()
            self.filter_edited_label.hide()
            self.delete_filter_btn.hide()

        elif self.filters_list.currentItem().text() == SEARCH_ITEM_NAME.capitalize():
            # activate save button
            self.overwrite_filter_button.hide()
            self.save_filter_btn.show()
            self.delete_filter_btn.hide()
        else:
            # activate overwrite button
            self.overwrite_filter_button.show()
            self.overwrite_filter_button.setEnabled(False)
            self.save_filter_btn.hide()
            self.delete_filter_btn.show()

    def toggle_frame_filters(self):
        if self.toggle_frame_filters_btn.text() == '<':
            self.toggle_frame_filters_btn.setText('>')
            self.filters_frame.hide()
        else:
            self.toggle_frame_filters_btn.setText('<')
            self.filters_frame.show()

    def add_filter_btn_click(self):
        self.overwrite_filter_button.hide()
        self.save_filter_btn.show()
        self.delete_filter_btn.hide()
        self.filter_edited_label.hide()
        self.filters_list.setCurrentItem(None)
        self.query_field.setText('')
        self.filter_name_label.setText('Add new filter')
        self.controller.current_issues.clear()
        self.show_no_issues()

    def eventFilter(self, obj, event):
        # if user started typing in filter field
        if obj is self.query_field and event.type() == QEvent.KeyRelease:
            if not self.filters_list.currentItem():
                return super().eventFilter(obj, event)
            current_filter_name = self.filters_list.currentItem().text().lower()
            # if current filter is not 'Search issues' or 'my open issues'
            if current_filter_name not in (SEARCH_ITEM_NAME, MY_ISSUES_ITEM_NAME):
                # if query of current filter has not changed
                if self.controller.filters_handler.get_filter_by_name(
                        current_filter_name
                ) != self.query_field.text():
                    # show that filter has been edited
                    self.filter_edited_label.show()
                    self.overwrite_filter_button.setEnabled(True)
                else:
                    self.filter_edited_label.hide()
                    self.overwrite_filter_button.setEnabled(False)
        return super().eventFilter(obj, event)

    def set_current_filter(self, filter_name):
        items = self.filters_list.findItems(
            filter_name, Qt.MatchExactly
        )
        self.filters_list.setCurrentItem(items[0])
        self.on_filter_selected(items[0])

    def add_filter(self, filter_name):
        self.filters_list.addItem(filter_name)
        self.set_current_filter(filter_name)

    def delete_filter(self):
        filter_name = self.filters_list.currentItem().text()
        reply = QMessageBox.question(
            self,
            'Delete filter',
            "Are you sure you want to delete "
            "'{}' filter?".format(filter_name),
            QMessageBox.Yes | QMessageBox.Cancel
        )
        if reply == QMessageBox.Yes:
            self.controller.filters_handler.delete_filter(filter_name)
            self.filters_list.takeItem(
                self.filters_list.currentRow()
            )
            self.filters_list.setCurrentItem(
                self.filters_list.findItems(
                    MY_ISSUES_ITEM_NAME, Qt.MatchExactly
                )[0])
            self.on_filter_selected(self.filters_list.currentItem())

    def show_no_issues(self, error_text=None):
        self.issue_list_widget.clear()
        self.issue_list_widget.hide()
        if error_text:
            self.label_info.setText(error_text)
        self.label_info.show()

    def set_workflow_current_state(self, issue_key):
        item = self.issue_list_widget.findItems(
            issue_key, Qt.MatchExactly
        )[0]
        custom_item = self.issue_list_widget.itemWidget(item)
        custom_item.set_workflow.setCurrentIndex(0)

    def wheelEvent(self, event):
        # top left corner coordinates of the issue list
        list_pos = self.issue_list_widget.pos()
        # check if cursor position is on the issue list
        if event.pos().x() >= list_pos.x() and event.pos().y() >= list_pos.y():
            if event.angleDelta().y() < 0:
                self.controller.refresh_issue_list(True)
                event.accept()

    def closeEvent(self, event):
        event.ignore()
        self.hide()
コード例 #8
0
class SimulationGui(QMainWindow):
    """
    class for the graphical user interface
    """
    # TODO enable closing plot docks by right-clicking their name

    runSimulation = pyqtSignal()
    stopSimulation = pyqtSignal()
    playbackTimeChanged = pyqtSignal()
    regimeFinished = pyqtSignal()
    finishedRegimeBatch = pyqtSignal(bool)

    def __init__(self):
        # constructor of the base class
        QMainWindow.__init__(self)

        QCoreApplication.setOrganizationName("RST")
        QCoreApplication.setOrganizationDomain("https://tu-dresden.de/rst")
        QCoreApplication.setApplicationVersion(
            pkg_resources.require("PyMoskito")[0].version)
        QCoreApplication.setApplicationName(globals()["__package__"])

        # load settings
        self._settings = QSettings()
        self._read_settings()

        # initialize logger
        self._logger = logging.getLogger(self.__class__.__name__)

        # Create Simulation Backend
        self.guiProgress = None
        self.cmdProgress = None
        self.sim = SimulatorInteractor(self)
        self.runSimulation.connect(self.sim.run_simulation)
        self.stopSimulation.connect(self.sim.stop_simulation)
        self.sim.simulation_finalized.connect(self.new_simulation_data)
        self.currentDataset = None
        self.interpolator = None

        # sim setup viewer
        self.targetView = SimulatorView(self)
        self.targetView.setModel(self.sim.target_model)
        self.targetView.expanded.connect(self.target_view_changed)
        self.targetView.collapsed.connect(self.target_view_changed)

        # sim results viewer
        self.result_view = QTreeView()

        # the docking area allows to rearrange the user interface at runtime
        self.area = pg.dockarea.DockArea()

        # Window properties
        icon_size = QSize(25, 25)
        self.setCentralWidget(self.area)
        self.resize(1000, 700)
        self.setWindowTitle("PyMoskito")
        res_path = get_resource("mosquito.png")
        icon = QIcon(res_path)
        self.setWindowIcon(icon)

        # create docks
        self.propertyDock = pg.dockarea.Dock("Properties")
        self.animationDock = pg.dockarea.Dock("Animation")
        self.regimeDock = pg.dockarea.Dock("Regimes")
        self.dataDock = pg.dockarea.Dock("Data")
        self.logDock = pg.dockarea.Dock("Log")
        self.plotDockPlaceholder = pg.dockarea.Dock("Placeholder")

        # arrange docks
        self.area.addDock(self.animationDock, "right")
        self.area.addDock(self.regimeDock, "left", self.animationDock)
        self.area.addDock(self.propertyDock, "bottom", self.regimeDock)
        self.area.addDock(self.dataDock, "bottom", self.propertyDock)
        self.area.addDock(self.plotDockPlaceholder, "bottom", self.animationDock)
        self.area.addDock(self.logDock, "bottom", self.dataDock)
        self.non_plotting_docks = list(self.area.findAll()[1].keys())

        # add widgets to the docks
        self.propertyDock.addWidget(self.targetView)

        if not vtk_available:
            self._logger.error("loading vtk failed with:{}".format(vtk_error_msg))

        # check if there is a registered visualizer
        available_vis = get_registered_visualizers()
        self._logger.info("found visualizers: {}".format(
            [name for cls, name in available_vis]))
        if available_vis:
            # instantiate the first visualizer
            self._logger.info("loading visualizer '{}'".format(available_vis[0][1]))
            self.animationLayout = QVBoxLayout()

            if issubclass(available_vis[0][0], MplVisualizer):
                self.animationWidget = QWidget()
                self.visualizer = available_vis[0][0](self.animationWidget,
                                                      self.animationLayout)
                self.animationDock.addWidget(self.animationWidget)
            elif issubclass(available_vis[0][0], VtkVisualizer):
                if vtk_available:
                    # vtk window
                    self.animationFrame = QFrame()
                    self.vtkWidget = QVTKRenderWindowInteractor(
                        self.animationFrame)
                    self.animationLayout.addWidget(self.vtkWidget)
                    self.animationFrame.setLayout(self.animationLayout)
                    self.animationDock.addWidget(self.animationFrame)
                    self.vtk_renderer = vtkRenderer()
                    self.vtkWidget.GetRenderWindow().AddRenderer(
                        self.vtk_renderer)
                    self.visualizer = available_vis[0][0](self.vtk_renderer)
                    self.vtkWidget.Initialize()
                else:
                    self._logger.warning("visualizer depends on vtk which is "
                                         "not available on this system!")
            elif available_vis:
                raise NotImplementedError
        else:
            self.visualizer = None

        # regime window
        self.regime_list = QListWidget(self)
        self.regime_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.regimeDock.addWidget(self.regime_list)
        self.regime_list.itemDoubleClicked.connect(self.regime_dclicked)
        self._regimes = []
        self.regime_file_name = ""

        self.actDeleteRegimes = QAction(self.regime_list)
        self.actDeleteRegimes.setText("&Delete Selected Regimes")
        # TODO shortcut works always, not only with focus on the regime list
        # self.actDeleteRegimes.setShortcutContext(Qt.WindowShortcut)
        self.actDeleteRegimes.setShortcut(QKeySequence(Qt.Key_Delete))
        self.actDeleteRegimes.triggered.connect(self.remove_regime_items)

        self.actSave = QAction(self)
        self.actSave.setText('Save Results As')
        self.actSave.setIcon(QIcon(get_resource("save.png")))
        self.actSave.setDisabled(True)
        self.actSave.setShortcut(QKeySequence.Save)
        self.actSave.triggered.connect(self.export_simulation_data)

        self.actLoadRegimes = QAction(self)
        self.actLoadRegimes.setText("Load Regimes from File")
        self.actLoadRegimes.setIcon(QIcon(get_resource("load.png")))
        self.actLoadRegimes.setDisabled(False)
        self.actLoadRegimes.setShortcut(QKeySequence.Open)
        self.actLoadRegimes.triggered.connect(self.load_regime_dialog)

        self.actExitOnBatchCompletion = QAction(self)
        self.actExitOnBatchCompletion.setText("&Exit On Batch Completion")
        self.actExitOnBatchCompletion.setCheckable(True)
        self.actExitOnBatchCompletion.setChecked(
            self._settings.value("control/exit_on_batch_completion") == "True"
        )
        self.actExitOnBatchCompletion.changed.connect(
            self.update_exit_on_batch_completion_setting)

        # regime management
        self.runningBatch = False
        self._current_regime_index = None
        self._current_regime_name = None
        self._regimes = []

        self.regimeFinished.connect(self.run_next_regime)
        self.finishedRegimeBatch.connect(self.regime_batch_finished)

        # data window
        self.dataList = QListWidget(self)
        self.dataDock.addWidget(self.dataList)
        self.dataList.itemDoubleClicked.connect(self.create_plot)

        # actions for simulation control
        self.actSimulateCurrent = QAction(self)
        self.actSimulateCurrent.setText("&Simulate Current Regime")
        self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png")))
        self.actSimulateCurrent.setShortcut(QKeySequence("F5"))
        self.actSimulateCurrent.triggered.connect(self.start_simulation)

        self.actSimulateAll = QAction(self)
        self.actSimulateAll.setText("Simulate &All Regimes")
        self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png")))
        self.actSimulateAll.setShortcut(QKeySequence("F6"))
        self.actSimulateAll.setDisabled(True)
        self.actSimulateAll.triggered.connect(self.start_regime_execution)

        # actions for animation control
        self.actAutoPlay = QAction(self)
        self.actAutoPlay.setText("&Autoplay Simulation")
        self.actAutoPlay.setCheckable(True)
        self.actAutoPlay.setChecked(
            self._settings.value("control/autoplay_animation") == "True"
        )
        self.actAutoPlay.changed.connect(self.update_autoplay_setting)

        self.actPlayPause = QAction(self)
        self.actPlayPause.setText("Play Animation")
        self.actPlayPause.setIcon(QIcon(get_resource("play.png")))
        self.actPlayPause.setDisabled(True)
        self.actPlayPause.setShortcut(QKeySequence(Qt.Key_Space))
        self.actPlayPause.triggered.connect(self.play_animation)

        self.actStop = QAction(self)
        self.actStop.setText("Stop")
        self.actStop.setIcon(QIcon(get_resource("stop.png")))
        self.actStop.setDisabled(True)
        self.actStop.triggered.connect(self.stop_animation)

        self.actSlow = QAction(self)
        self.actSlow.setText("Slowest")
        self.actSlow.setIcon(QIcon(get_resource("slow.png")))
        self.actSlow.setDisabled(False)
        self.actSlow.triggered.connect(self.set_slowest_playback_speed)

        self.actFast = QAction(self)
        self.actFast.setText("Fastest")
        self.actFast.setIcon(QIcon(get_resource("fast.png")))
        self.actFast.setDisabled(False)
        self.actFast.triggered.connect(self.set_fastest_playback_speed)

        self.speedControl = QSlider(Qt.Horizontal, self)
        self.speedControl.setMaximumSize(200, 25)
        self.speedControl.setTickPosition(QSlider.TicksBothSides)
        self.speedControl.setDisabled(False)
        self.speedControl.setMinimum(0)
        self.speedControl.setMaximum(12)
        self.speedControl.setValue(6)
        self.speedControl.setTickInterval(6)
        self.speedControl.setSingleStep(2)
        self.speedControl.setPageStep(3)
        self.speedControl.valueChanged.connect(self.update_playback_speed)

        self.timeSlider = QSlider(Qt.Horizontal, self)
        self.timeSlider.setMinimum(0)
        self.timeSliderRange = 1000
        self.timeSlider.setMaximum(self.timeSliderRange)
        self.timeSlider.setTickInterval(1)
        self.timeSlider.setTracking(True)
        self.timeSlider.setDisabled(True)
        self.timeSlider.valueChanged.connect(self.update_playback_time)

        self.playbackTime = .0
        self.playbackGain = 1
        self.currentStepSize = .0
        self.currentEndTime = .0
        self.playbackTimer = QTimer()
        self.playbackTimer.timeout.connect(self.increment_playback_time)
        self.playbackTimeChanged.connect(self.update_gui)
        self.playbackTimeout = 33  # in [ms] -> 30 fps

        self.actResetCamera = QAction(self)
        self.actResetCamera.setText("Reset Camera")
        self.actResetCamera.setIcon(QIcon(get_resource("reset_camera.png")))
        self.actResetCamera.setDisabled(True)
        if available_vis:
            self.actResetCamera.setEnabled(self.visualizer.can_reset_view)
        self.actResetCamera.triggered.connect(self.reset_camera_clicked)

        # postprocessing
        self.actPostprocessing = QAction(self)
        self.actPostprocessing.setText("Launch Postprocessor")
        self.actPostprocessing.setIcon(QIcon(get_resource("processing.png")))
        self.actPostprocessing.setDisabled(False)
        self.actPostprocessing.triggered.connect(self.postprocessing_clicked)
        self.actPostprocessing.setShortcut(QKeySequence("F7"))

        self.postprocessor = None

        # toolbar
        self.toolbarSim = QToolBar("Simulation")
        self.toolbarSim.setContextMenuPolicy(Qt.PreventContextMenu)
        self.toolbarSim.setMovable(False)
        self.toolbarSim.setIconSize(icon_size)
        self.addToolBar(self.toolbarSim)
        self.toolbarSim.addAction(self.actLoadRegimes)
        self.toolbarSim.addAction(self.actSave)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actSimulateCurrent)
        self.toolbarSim.addAction(self.actSimulateAll)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actPlayPause)
        self.toolbarSim.addAction(self.actStop)
        self.toolbarSim.addWidget(self.timeSlider)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actSlow)
        self.toolbarSim.addWidget(self.speedControl)
        self.toolbarSim.addAction(self.actFast)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actPostprocessing)
        self.toolbarSim.addAction(self.actResetCamera)
        self.postprocessor = None

        # log dock
        self.logBox = QPlainTextEdit(self)
        self.logBox.setReadOnly(True)
        self.logDock.addWidget(self.logBox)

        # init logger for logging box
        self.textLogger = PlainTextLogger(logging.INFO)
        self.textLogger.set_target_cb(self.logBox.appendPlainText)
        logging.getLogger().addHandler(self.textLogger)

        # menu bar
        fileMenu = self.menuBar().addMenu("&File")
        fileMenu.addAction(self.actLoadRegimes)
        fileMenu.addAction(self.actSave)
        fileMenu.addAction("&Quit", self.close)

        editMenu = self.menuBar().addMenu("&Edit")
        editMenu.addAction(self.actDeleteRegimes)

        simMenu = self.menuBar().addMenu("&Simulation")
        simMenu.addAction(self.actSimulateCurrent)
        simMenu.addAction(self.actSimulateAll)
        simMenu.addAction(self.actExitOnBatchCompletion)
        simMenu.addAction(self.actPostprocessing)

        animMenu = self.menuBar().addMenu("&Animation")
        animMenu.addAction(self.actPlayPause)
        animMenu.addAction("&Increase Playback Speed",
                           self.increment_playback_speed,
                           QKeySequence(Qt.CTRL + Qt.Key_Plus))
        animMenu.addAction("&Decrease Playback Speed",
                           self.decrement_playback_speed,
                           QKeySequence(Qt.CTRL + Qt.Key_Minus))
        animMenu.addAction("&Reset Playback Speed",
                           self.reset_playback_speed,
                           QKeySequence(Qt.CTRL + Qt.Key_0))
        animMenu.addAction(self.actAutoPlay)
        animMenu.addAction(self.actResetCamera)

        helpMenu = self.menuBar().addMenu("&Help")
        helpMenu.addAction("&Online Documentation", self.show_online_docs)
        helpMenu.addAction("&About", self.show_info)

        # status bar
        self.status = QStatusBar(self)
        self.setStatusBar(self.status)
        self.statusLabel = QLabel("Ready.")
        self.statusBar().addPermanentWidget(self.statusLabel)
        self.timeLabel = QLabel("current time: 0.0")
        self.statusBar().addPermanentWidget(self.timeLabel)

        self._logger.info("Simulation GUI is up and running.")

    def _read_settings(self):

        # add default settings if none are present
        if not self._settings.contains("path/simulation_results"):
            self._settings.setValue("path/simulation_results",
                                    os.path.join(os.path.curdir,
                                                 "results",
                                                 "simulation"))
        if not self._settings.contains("path/postprocessing_results"):
            self._settings.setValue("path/postprocessing_results",
                                    os.path.join(os.path.curdir,
                                                 "results",
                                                 "postprocessing"))
        if not self._settings.contains("path/metaprocessing_results"):
            self._settings.setValue("path/metaprocessing_results",
                                    os.path.join(os.path.curdir,
                                                 "results",
                                                 "metaprocessing"))

        if not self._settings.contains("control/autoplay_animation"):
            self._settings.setValue("control/autoplay_animation", "False")

        if not self._settings.contains("control/exit_on_batch_completion"):
            self._settings.setValue("control/exit_on_batch_completion", "False")

    def _write_settings(self):
        """ Store the application state. """
        pass

    @pyqtSlot()
    def update_autoplay_setting(self):
        self._settings.setValue("control/autoplay_animation",
                                str(self.actAutoPlay.isChecked()))

    @pyqtSlot()
    def update_exit_on_batch_completion_setting(self, state=None):
        if state is None:
            state = self.actExitOnBatchCompletion.isChecked()
        self._settings.setValue("control/exit_on_batch_completion", str(state))

    def set_visualizer(self, vis):
        self.visualizer = vis
        self.vtkWidget.Initialize()

    @pyqtSlot()
    def play_animation(self):
        """
        play the animation
        """
        self._logger.debug("Starting Playback")

        # if we are at the end, start from the beginning
        if self.playbackTime == self.currentEndTime:
            self.timeSlider.setValue(0)

        self.actPlayPause.setText("Pause Animation")
        self.actPlayPause.setIcon(QIcon(get_resource("pause.png")))
        self.actPlayPause.triggered.disconnect(self.play_animation)
        self.actPlayPause.triggered.connect(self.pause_animation)
        self.playbackTimer.start(self.playbackTimeout)

    @pyqtSlot()
    def pause_animation(self):
        """
        pause the animation
        """
        self._logger.debug("Pausing Playback")
        self.playbackTimer.stop()
        self.actPlayPause.setText("Play Animation")
        self.actPlayPause.setIcon(QIcon(get_resource("play.png")))
        self.actPlayPause.triggered.disconnect(self.pause_animation)
        self.actPlayPause.triggered.connect(self.play_animation)

    def stop_animation(self):
        """
        Stop the animation if it is running and reset the playback time.
        """
        self._logger.debug("Stopping Playback")
        if self.actPlayPause.text() == "Pause Animation":
            # animation is playing -> stop it
            self.playbackTimer.stop()
            self.actPlayPause.setText("Play Animation")
            self.actPlayPause.setIcon(QIcon(get_resource("play.png")))
            self.actPlayPause.triggered.disconnect(self.pause_animation)
            self.actPlayPause.triggered.connect(self.play_animation)

        self.timeSlider.setValue(0)

    @pyqtSlot()
    def start_simulation(self):
        """
        start the simulation and disable start button
        """
        if self._current_regime_index is None:
            regime_name = ""
        else:
            regime_name = str(self.regime_list.item(
                self._current_regime_index).text())

        self.statusLabel.setText("simulating {}".format(regime_name))
        self._logger.info("Simulating: {}".format(regime_name))

        self.actSimulateCurrent.setIcon(QIcon(
            get_resource("stop_simulation.png")))
        self.actSimulateCurrent.setText("Abort &Simulation")
        self.actSimulateCurrent.triggered.disconnect(self.start_simulation)
        self.actSimulateCurrent.triggered.connect(self.stop_simulation)

        if not self.runningBatch:
            self.actSimulateAll.setDisabled(True)

        self.guiProgress = QProgressBar(self)
        self.sim.simulationProgressChanged.connect(self.guiProgress.setValue)
        self.statusBar().addWidget(self.guiProgress)
        self.runSimulation.emit()

    @pyqtSlot()
    def stop_simulation(self):
        self.stopSimulation.emit()

    def export_simulation_data(self, ok):
        """
        Query the user for a custom name and export the current simulation
        results.

        :param ok: unused parameter from QAction.triggered() Signal
        """
        self._save_data()

    def _save_data(self, file_path=None):
        """
        Save the current simulation results.

        If *fie_name* is given, the result will be saved to the specified
        location, making automated exporting easier.

        Args:
            file_path(str): Absolute path of the target file. If `None` the
                use will be asked for a storage location.
        """
        regime_name = self._regimes[self._current_regime_index]["Name"]

        if file_path is None:
            # get default path
            path = self._settings.value("path/simulation_results")

            # create canonic file name
            suggestion = self._simfile_name(regime_name)
        else:
            path = os.path.dirname(file_path)
            suggestion = os.path.basename(file_path)

        # check if path exists otherwise create it
        if not os.path.isdir(path):
            box = QMessageBox()
            box.setText("Export Folder does not exist yet.")
            box.setInformativeText("Do you want to create it? \n"
                                   "{}".format(os.path.abspath(path)))
            box.setStandardButtons(QMessageBox.Ok | QMessageBox.No)
            box.setDefaultButton(QMessageBox.Ok)
            ret = box.exec_()
            if ret == QMessageBox.Ok:
                os.makedirs(path)
            else:
                path = os.path.abspath(os.path.curdir)
                file_path = None

        # If no path was given, present the default and let the user choose
        if file_path is None:
            dialog = QFileDialog(self)
            dialog.setAcceptMode(QFileDialog.AcceptSave)
            dialog.setFileMode(QFileDialog.AnyFile)
            dialog.setDirectory(path)
            dialog.setNameFilter("PyMoskito Results (*.pmr)")
            dialog.selectFile(suggestion)

            if dialog.exec_():
                file_path = dialog.selectedFiles()[0]
            else:
                self._logger.warning("Export Aborted")
                return -1

        # ask whether this should act as new default
        path = os.path.abspath(os.path.dirname(file_path))
        if path != self._settings.value("path/simulation_results"):
            box = QMessageBox()
            box.setText("Use this path as new default?")
            box.setInformativeText("{}".format(path))
            box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            box.setDefaultButton(QMessageBox.Yes)
            ret = box.exec_()
            if ret == QMessageBox.Yes:
                self._settings.setValue("path/simulation_results", path)

        self.currentDataset.update({"regime name": regime_name})
        with open(file_path, "wb") as f:
            pickle.dump(self.currentDataset, f, protocol=4)

        self.statusLabel.setText("results saved to {}".format(file_path))
        self._logger.info("results saved to {}".format(file_path))

    def _simfile_name(self, regime_name):
        """ Create a canonical name for a simulation result file
        """
        suggestion = (time.strftime("%Y%m%d-%H%M%S")
                      + "_" + regime_name + ".pmr")
        return suggestion

    def load_regime_dialog(self):
        regime_path = os.path.join(os.curdir)

        dialog = QFileDialog(self)
        dialog.setFileMode(QFileDialog.ExistingFile)
        dialog.setDirectory(regime_path)
        dialog.setNameFilter("Simulation Regime files (*.sreg)")

        if dialog.exec_():
            file = dialog.selectedFiles()[0]
            self.load_regimes_from_file(file)

    def load_regimes_from_file(self, file_name):
        """
        load simulation regime from file
        :param file_name:
        """
        self.regime_file_name = os.path.split(file_name)[-1][:-5]
        self._logger.info("loading regime file: {0}".format(self.regime_file_name))
        with open(file_name.encode(), "r") as f:
            self._regimes += yaml.load(f)

        self._update_regime_list()

        if self._regimes:
            self.actSimulateAll.setDisabled(False)

        self._logger.info("loaded {} regimes".format(len(self._regimes)))
        self.statusBar().showMessage("loaded {} regimes.".format(len(self._regimes)), 1000)
        return

    def _update_regime_list(self):
        self.regime_list.clear()
        for reg in self._regimes:
            self._logger.debug("adding '{}' to regime list".format(reg["Name"]))
            self.regime_list.addItem(reg["Name"])

    def remove_regime_items(self):
        if self.regime_list.currentRow() >= 0:
            # flag all selected files as invalid
            items = self.regime_list.selectedItems()
            for item in items:
                del self._regimes[self.regime_list.row(item)]
                self.regime_list.takeItem(self.regime_list.row(item))

    @pyqtSlot(QListWidgetItem)
    def regime_dclicked(self, item):
        """
        Apply the selected regime to the current target.
        """
        self.apply_regime_by_name(str(item.text()))

    def apply_regime_by_name(self, regime_name):
        """
        Apply the regime given by `regime_name` und update the regime index.

        Returns:
            bool: `True` if successful, `False` if errors occurred.
        """
        # get regime idx
        try:
            idx = list(map(itemgetter("Name"), self._regimes)).index(regime_name)
        except ValueError as e:
            self._logger.error("apply_regime_by_name(): Error no regime called "
                               "'{0}'".format(regime_name))
            return False

        # apply
        return self._apply_regime_by_idx(idx)

    def _apply_regime_by_idx(self, index=0):
        """
        Apply the given regime.

        Args:
            index(int): Index of the regime in the `RegimeList` .

        Returns:
            bool: `True` if successful, `False` if errors occurred.
        """
        if index >= len(self._regimes):
            self._logger.error("applyRegime: index error! ({})".format(index))
            return False

        reg_name = self._regimes[index]["Name"]
        self.statusBar().showMessage("regime {} applied.".format(reg_name),
                                     1000)
        self._logger.info("applying regime '{}'".format(reg_name))

        self._current_regime_index = index
        self._current_regime_name = reg_name

        return self.sim.set_regime(self._regimes[index])

    @pyqtSlot()
    def start_regime_execution(self):
        """
        Simulate all regimes in the regime list.
        """
        self.actSimulateAll.setText("Stop Simulating &All Regimes")
        self.actSimulateAll.setIcon(QIcon(get_resource("stop_batch.png")))
        self.actSimulateAll.triggered.disconnect(self.start_regime_execution)
        self.actSimulateAll.triggered.connect(self.stop_regime_excecution)

        self.runningBatch = True
        self._current_regime_index = -1
        self.regimeFinished.emit()

    def run_next_regime(self):
        """
        Execute the next regime in the regime batch.
        """
        # are we finished?
        if self._current_regime_index == len(self._regimes) - 1:
            self.finishedRegimeBatch.emit(True)
            return

        suc = self._apply_regime_by_idx(self._current_regime_index + 1)
        if not suc:
            self.finishedRegimeBatch.emit(False)
            return

        self.start_simulation()

    @pyqtSlot()
    def stop_regime_excecution(self):
        """ Stop the batch process.
        """
        self.stopSimulation.emit()
        self.finishedRegimeBatch.emit(False)

    def regime_batch_finished(self, status):
        self.runningBatch = False
        self.actSimulateAll.setDisabled(False)
        self.actSave.setDisabled(True)

        self.actSimulateAll.setText("Simulate &All Regimes")
        self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png")))
        self.actSimulateAll.triggered.disconnect(self.stop_regime_excecution)
        self.actSimulateAll.triggered.connect(self.start_regime_execution)

        if status:
            self.statusLabel.setText("All regimes have been simulated")
            self._logger.info("All Regimes have been simulated")
        else:
            self._logger.error("Batch simulation has been aborted")

        if self._settings.value("control/exit_on_batch_completion") == "True":
            self._logger.info("Shutting down SimulationGUI")
            self.close()

    @pyqtSlot(str, dict, name="new_simulation_data")
    def new_simulation_data(self, status, data):
        """
        Slot to be called when the simulation interface has completed the
        current job and new data is available.

        Args:
            status (str): Status of the simulation, either
                - `finished` : Simulation has been finished successfully or
                - `failed` : Simulation has failed.
            data (dict): Dictionary, holding the simulation data.
        """
        self._logger.info("Simulation {}".format(status))
        self.statusLabel.setText("Simulation {}".format(status))

        self.actSimulateCurrent.setText("&Simulate Current Regime")
        self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png")))
        self.actSimulateCurrent.triggered.disconnect(self.stop_simulation)
        self.actSimulateCurrent.triggered.connect(self.start_simulation)

        self.actPlayPause.setDisabled(False)
        self.actStop.setDisabled(False)
        self.actSave.setDisabled(False)
        self.speedControl.setDisabled(False)
        self.timeSlider.setDisabled(False)

        self.sim.simulationProgressChanged.disconnect(self.guiProgress.setValue)
        self.statusBar().removeWidget(self.guiProgress)

        self.stop_animation()

        self.currentDataset = data
        if data:
            self._read_results()
            self._update_data_list()
            self._update_plots()

        if self._settings.value("control/autoplay_animation") == "True":
            self.actPlayPause.trigger()

        if self.runningBatch:
            regime_name = self._regimes[self._current_regime_index]["Name"]
            file_name = self._simfile_name(regime_name)
            self._save_data(os.path.join(
                self._settings.value("path/simulation_results"),
                file_name))
            self.regimeFinished.emit()
        else:
            self.actSimulateAll.setDisabled(False)

    def _read_results(self):
        state = self.currentDataset["results"]["Solver"]
        self.interpolator = interp1d(self.currentDataset["results"]["time"],
                                     state,
                                     axis=0,
                                     bounds_error=False,
                                     fill_value=(state[0], state[-1]))
        self.currentStepSize = 1.0/self.currentDataset["simulation"][
            "measure rate"]
        self.currentEndTime = self.currentDataset["simulation"]["end time"]
        self.validData = True

    def increment_playback_speed(self):
        self.speedControl.setValue(self.speedControl.value()
                                   + self.speedControl.singleStep())

    def decrement_playback_speed(self):
        self.speedControl.setValue(self.speedControl.value()
                                   - self.speedControl.singleStep())

    def reset_playback_speed(self):
        self.speedControl.setValue((self.speedControl.maximum()
                                    - self.speedControl.minimum())/2)

    def set_slowest_playback_speed(self):
        self.speedControl.setValue(self.speedControl.minimum())

    def set_fastest_playback_speed(self):
        self.speedControl.setValue(self.speedControl.maximum())

    def update_playback_speed(self, val):
        """
        adjust playback time to slider value

        :param val:
        """
        maximum = self.speedControl.maximum()
        self.playbackGain = 10**(3.0 * (val - maximum / 2) / maximum)

    @pyqtSlot()
    def increment_playback_time(self):
        """
        go one time step forward in playback
        """
        if self.playbackTime == self.currentEndTime:
            self.pause_animation()
            return

        increment = self.playbackGain * self.playbackTimeout / 1000
        self.playbackTime = min(self.currentEndTime,
                                self.playbackTime + increment)
        pos = int(self.playbackTime / self.currentEndTime
                  * self.timeSliderRange)
        self.timeSlider.blockSignals(True)
        self.timeSlider.setValue(pos)
        self.timeSlider.blockSignals(False)
        self.playbackTimeChanged.emit()

    def update_playback_time(self):
        """
        adjust playback time to slider value
        """
        self.playbackTime = self.timeSlider.value()/self.timeSliderRange*self.currentEndTime
        self.playbackTimeChanged.emit()
        return

    def update_gui(self):
        """
        updates the graphical user interface, including:
            - timestamp
            - visualisation
            - time cursor in diagrams
        """
        if not self.validData:
            return

        self.timeLabel.setText("current time: %4f" % self.playbackTime)

        # update time cursor in plots
        self._update_time_cursors()

        # update state of rendering
        if self.visualizer:
            state = self.interpolator(self.playbackTime)
            self.visualizer.update_scene(state)
            if isinstance(self.visualizer, MplVisualizer):
                pass
            elif isinstance(self.visualizer, VtkVisualizer):
                self.vtkWidget.GetRenderWindow().Render()

    def _update_data_list(self):
        self.dataList.clear()
        for module_name, results in self.currentDataset["results"].items():
            if not isinstance(results, np.ndarray):
                continue
            if len(results.shape) == 1:
                self.dataList.insertItem(0, module_name)
            elif len(results.shape) == 2:
                for col in range(results.shape[1]):
                    self.dataList.insertItem(
                        0,
                        self._build_entry_name(module_name, (col, ))
                    )
            elif len(results.shape) == 3:
                for col in range(results.shape[1]):
                    for der in range(results.shape[2]):
                        self.dataList.insertItem(
                            0,
                            self._build_entry_name(module_name, (col, der))
                        )

    def _build_entry_name(self, module_name, idx):
        """
        Construct an identifier for a given entry of a module.
        Args:
            module_name (str): name of the module the entry belongs to.
            idx (tuple): Index of the entry.

        Returns:
            str: Identifier to use for display.
        """
        # save the user from defining 1d entries via tuples
        if len(idx) == 1:
            m_idx = idx[0]
        else:
            m_idx = idx

        mod_settings = self.currentDataset["modules"]
        info = mod_settings.get(module_name, {}).get("output_info", None)
        if info:
            if m_idx in info:
                return ".".join([module_name, info[m_idx]["Name"]])

        return ".".join([module_name] + [str(i) for i in idx])

    def _get_index_from_suffix(self, module_name, suffix):
        info = self.currentDataset["modules"].get(module_name, {}).get(
            "output_info", None)
        idx = next((i for i in info if info[i]["Name"] == suffix), None)
        return idx

    def _get_units(self, entry):
        """
        Return the unit that corresponds to a given entry.

        If no information is available, None is returned.

        Args:
            entry (str): Name of the entry. This can either be "Model.a.b" where
                a and b are numbers or if information is available "Model.Signal"
                where signal is the name of that part.

        Returns:

        """
        args = entry.split(".")
        module_name = args.pop(0)
        info = self.currentDataset["modules"].get(module_name, {}).get(
            "output_info", None)
        if info is None:
            return None

        if len(args) == 1:
            try:
                idx = int(args[0])
            except ValueError:
                idx = next((i for i in info if info[i]["Name"] == args[0]),
                           None)
        else:
            idx = (int(a) for a in args)

        return info[idx]["Unit"]

    def create_plot(self, item):
        """
        Creates a plot widget based on the given item.

        If a plot for this item is already open no new plot is created but the
        existing one is raised up again.

        Args:
            item(Qt.ListItem): Item to plot.
        """
        title = str(item.text())
        if title in self.non_plotting_docks:
            self._logger.error("Title '{}' not allowed for a plot window since"
                               "it would shadow on of the reserved "
                               "names".format(title))

        # check if plot has already been opened
        if title in self.area.findAll()[1]:
            self.area.docks[title].raiseDock()
            return

        # collect data
        data = self._get_data_by_name(title)
        t = self.currentDataset["results"]["time"]
        unit = self._get_units(title)
        if "." in title:
            name = title.split(".")[1]
        else:
            name = title

        # create plot widget
        widget = pg.PlotWidget(title=title)
        widget.showGrid(True, True)
        widget.plot(x=t, y=data)
        widget.getPlotItem().getAxis("bottom").setLabel(text="Time", units="s")
        widget.getPlotItem().getAxis("left").setLabel(text=name, units=unit)

        # add a time line
        time_line = pg.InfiniteLine(self.playbackTime,
                                    angle=90,
                                    movable=False,
                                    pen=pg.mkPen("#FF0000", width=2.0))
        widget.getPlotItem().addItem(time_line)

        # create dock container and add it to dock area
        dock = pg.dockarea.Dock(title, closable=True)
        dock.addWidget(widget)
        self.area.addDock(dock, "above", self.plotDockPlaceholder)

    def _get_data_by_name(self, name):
        tmp = name.split(".")
        module_name = tmp[0]
        if len(tmp) == 1:
            data = np.array(self.currentDataset["results"][module_name])
        elif len(tmp) == 2:
            try:
                idx = int(tmp[1])
            except ValueError:
                idx = self._get_index_from_suffix(module_name, tmp[1])
            finally:
                data = self.currentDataset["results"][module_name][..., idx]
        elif len(tmp) == 3:
            idx = int(tmp[1])
            der = int(tmp[2])
            data = self.currentDataset["results"][module_name][..., idx, der]
        else:
            raise ValueError("Format not supported")

        return data

    def _update_time_cursors(self):
        """
        Update the time lines of all plot windows
        """
        for title, dock in self.area.findAll()[1].items():
            if title in self.non_plotting_docks:
                continue
            for widget in dock.widgets:
                for item in widget.getPlotItem().items:
                    if isinstance(item, pg.InfiniteLine):
                        item.setValue(self.playbackTime)

    def _update_plots(self):
        """
        Update the data in all plot windows
        """
        for title, dock in self.area.findAll()[1].items():
            if title in self.non_plotting_docks:
                continue

            if not self.dataList.findItems(dock.name(), Qt.MatchExactly):
                # no data for this plot -> remove it
                dock.close()
                continue

            for widget in dock.widgets:
                for item in widget.getPlotItem().items:
                    if isinstance(item, pg.PlotDataItem):
                        x_data = self.currentDataset["results"]["time"]
                        y_data = self._get_data_by_name(dock.name())
                        item.setData(x=x_data, y=y_data)

    @pyqtSlot(QModelIndex)
    def target_view_changed(self, index):
        self.targetView.resizeColumnToContents(0)

    def postprocessing_clicked(self):
        """
        starts the post- and metaprocessing application
        """
        self._logger.info("launching postprocessor")
        self.statusBar().showMessage("launching postprocessor", 1000)
        if self.postprocessor is None:
            self.postprocessor = PostProcessor()

        self.postprocessor.show()

    def reset_camera_clicked(self):
        """
        reset camera in vtk window
        """
        self.visualizer.reset_camera()
        self.vtkWidget.GetRenderWindow().Render()

    def show_info(self):
        icon_lic = open(get_resource("license.txt"), "r").read()
        text = "This application was build using PyMoskito ver. {} .<br />" \
               "PyMoskito is free software distributed under GPLv3. <br />" \
               "It is developed by members of the " \
               "<a href=\'https://tu-dresden.de/ing/elektrotechnik/rst'>" \
               "Institute of Control Theory</a>" \
               " at the <a href=\'https://tu-dresden.de'>" \
               "Dresden University of Technology</a>. <br />" \
               "".format(pkg_resources.require("PyMoskito")[0].version) \
               + "<br />" + icon_lic
        box = QMessageBox.about(self, "PyMoskito", text)

    def show_online_docs(self):
        webbrowser.open("https://pymoskito.readthedocs.org")

    def closeEvent(self, QCloseEvent):
        self._logger.info("Close Event received, shutting down.")
        logging.getLogger().removeHandler(self.textLogger)
        super().closeEvent(QCloseEvent)
コード例 #9
0
ファイル: outlineBasics.py プロジェクト: zieglerm/manuskript
class outlineBasics(QAbstractItemView):
    def __init__(self, parent=None):
        self._indexesToOpen = None
        self.menuCustomIcons = None

    def getSelection(self):
        sel = []
        for i in self.selectedIndexes():
            if i.column() != 0:
                continue
            if not i in sel:
                sel.append(i)
        return sel

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.RightButton:
            self.menu = self.makePopupMenu()
            self.menu.popup(event.globalPos())
        # We don't call QAbstractItemView.mouseReleaseEvent because
        # outlineBasics is never subclassed alone. So the others views
        # (outlineView, corkView, treeView) that subclass outlineBasics
        # call their respective mother class.

    def makePopupMenu(self):
        index = self.currentIndex()
        sel = self.getSelection()
        clipboard = qApp.clipboard()

        menu = QMenu(self)

        # Get index under cursor
        pos = self.viewport().mapFromGlobal(QCursor.pos())
        mouseIndex = self.indexAt(pos)

        # Get index's title
        if mouseIndex.isValid():
            title = mouseIndex.internalPointer().title()

        elif self.rootIndex().parent().isValid():
            # mouseIndex is the background of an item, so we check the parent
            mouseIndex = self.rootIndex().parent()
            title = mouseIndex.internalPointer().title()

        else:
            title = qApp.translate("outlineBasics", "Root")

        if len(title) > 25:
            title = title[:25] + "…"

        # Open Item action
        self.actOpen = QAction(
            QIcon.fromTheme("go-right"),
            qApp.translate("outlineBasics", "Open {}".format(title)), menu)
        self.actOpen.triggered.connect(self.openItem)
        menu.addAction(self.actOpen)

        # Open item(s) in new tab
        if mouseIndex in sel and len(sel) > 1:
            actionTitle = qApp.translate("outlineBasics",
                                         "Open {} items in new tabs").format(
                                             len(sel))
            self._indexesToOpen = sel
        else:
            actionTitle = qApp.translate("outlineBasics",
                                         "Open {} in a new tab").format(title)
            self._indexesToOpen = [mouseIndex]

        self.actNewTab = QAction(QIcon.fromTheme("go-right"), actionTitle,
                                 menu)
        self.actNewTab.triggered.connect(self.openItemsInNewTabs)
        menu.addAction(self.actNewTab)

        menu.addSeparator()

        # Add text / folder
        self.actAddFolder = QAction(
            QIcon.fromTheme("folder-new"),
            qApp.translate("outlineBasics", "New &Folder"), menu)
        self.actAddFolder.triggered.connect(self.addFolder)
        menu.addAction(self.actAddFolder)

        self.actAddText = QAction(QIcon.fromTheme("document-new"),
                                  qApp.translate("outlineBasics", "New &Text"),
                                  menu)
        self.actAddText.triggered.connect(self.addText)
        menu.addAction(self.actAddText)

        menu.addSeparator()

        # Copy, cut, paste, duplicate
        self.actCut = QAction(QIcon.fromTheme("edit-cut"),
                              qApp.translate("outlineBasics", "C&ut"), menu)
        self.actCut.triggered.connect(self.cut)
        menu.addAction(self.actCut)

        self.actCopy = QAction(QIcon.fromTheme("edit-copy"),
                               qApp.translate("outlineBasics", "&Copy"), menu)
        self.actCopy.triggered.connect(self.copy)
        menu.addAction(self.actCopy)

        self.actPaste = QAction(QIcon.fromTheme("edit-paste"),
                                qApp.translate("outlineBasics", "&Paste"),
                                menu)
        self.actPaste.triggered.connect(self.paste)
        menu.addAction(self.actPaste)

        # Rename / duplicate / remove items
        self.actDelete = QAction(QIcon.fromTheme("edit-delete"),
                                 qApp.translate("outlineBasics", "&Delete"),
                                 menu)
        self.actDelete.triggered.connect(self.delete)
        menu.addAction(self.actDelete)

        self.actRename = QAction(QIcon.fromTheme("edit-rename"),
                                 qApp.translate("outlineBasics", "&Rename"),
                                 menu)
        self.actRename.triggered.connect(self.rename)
        menu.addAction(self.actRename)

        menu.addSeparator()

        # POV
        self.menuPOV = QMenu(qApp.translate("outlineBasics", "Set POV"), menu)
        mw = mainWindow()
        a = QAction(QIcon.fromTheme("dialog-no"),
                    qApp.translate("outlineBasics", "None"), self.menuPOV)
        a.triggered.connect(lambda: self.setPOV(""))
        self.menuPOV.addAction(a)
        self.menuPOV.addSeparator()

        menus = []
        for i in [
                qApp.translate("outlineBasics", "Main"),
                qApp.translate("outlineBasics", "Secondary"),
                qApp.translate("outlineBasics", "Minor")
        ]:
            m = QMenu(i, self.menuPOV)
            menus.append(m)
            self.menuPOV.addMenu(m)

        mpr = QSignalMapper(self.menuPOV)
        for i in range(mw.mdlCharacter.rowCount()):
            a = QAction(mw.mdlCharacter.icon(i), mw.mdlCharacter.name(i),
                        self.menuPOV)
            a.triggered.connect(mpr.map)
            mpr.setMapping(a, int(mw.mdlCharacter.ID(i)))

            imp = toInt(mw.mdlCharacter.importance(i))

            menus[2 - imp].addAction(a)

        mpr.mapped.connect(self.setPOV)
        menu.addMenu(self.menuPOV)

        # Status
        self.menuStatus = QMenu(qApp.translate("outlineBasics", "Set Status"),
                                menu)
        # a = QAction(QIcon.fromTheme("dialog-no"), qApp.translate("outlineBasics", "None"), self.menuStatus)
        # a.triggered.connect(lambda: self.setStatus(""))
        # self.menuStatus.addAction(a)
        # self.menuStatus.addSeparator()

        mpr = QSignalMapper(self.menuStatus)
        for i in range(mw.mdlStatus.rowCount()):
            a = QAction(mw.mdlStatus.item(i, 0).text(), self.menuStatus)
            a.triggered.connect(mpr.map)
            mpr.setMapping(a, i)
            self.menuStatus.addAction(a)
        mpr.mapped.connect(self.setStatus)
        menu.addMenu(self.menuStatus)

        # Labels
        self.menuLabel = QMenu(qApp.translate("outlineBasics", "Set Label"),
                               menu)
        mpr = QSignalMapper(self.menuLabel)
        for i in range(mw.mdlLabels.rowCount()):
            a = QAction(
                mw.mdlLabels.item(i, 0).icon(),
                mw.mdlLabels.item(i, 0).text(), self.menuLabel)
            a.triggered.connect(mpr.map)
            mpr.setMapping(a, i)
            self.menuLabel.addAction(a)
        mpr.mapped.connect(self.setLabel)
        menu.addMenu(self.menuLabel)

        menu.addSeparator()

        # Custom icons
        if self.menuCustomIcons:
            menu.addMenu(self.menuCustomIcons)
        else:
            self.menuCustomIcons = QMenu(
                qApp.translate("outlineBasics", "Set Custom Icon"), menu)
            a = QAction(qApp.translate("outlineBasics", "Restore to default"),
                        self.menuCustomIcons)
            a.triggered.connect(lambda: self.setCustomIcon(""))
            self.menuCustomIcons.addAction(a)
            self.menuCustomIcons.addSeparator()

            txt = QLineEdit()
            txt.textChanged.connect(self.filterLstIcons)
            txt.setPlaceholderText("Filter icons")
            txt.setStyleSheet("background: transparent; border: none;")
            act = QWidgetAction(self.menuCustomIcons)
            act.setDefaultWidget(txt)
            self.menuCustomIcons.addAction(act)

            self.lstIcons = QListWidget()
            for i in customIcons():
                item = QListWidgetItem()
                item.setIcon(QIcon.fromTheme(i))
                item.setData(Qt.UserRole, i)
                item.setToolTip(i)
                self.lstIcons.addItem(item)
            self.lstIcons.itemClicked.connect(self.setCustomIconFromItem)
            self.lstIcons.setViewMode(self.lstIcons.IconMode)
            self.lstIcons.setUniformItemSizes(True)
            self.lstIcons.setResizeMode(self.lstIcons.Adjust)
            self.lstIcons.setMovement(self.lstIcons.Static)
            self.lstIcons.setStyleSheet(
                "background: transparent; background: none;")
            self.filterLstIcons("")
            act = QWidgetAction(self.menuCustomIcons)
            act.setDefaultWidget(self.lstIcons)
            self.menuCustomIcons.addAction(act)

            menu.addMenu(self.menuCustomIcons)

        # Disabling stuff
        if not clipboard.mimeData().hasFormat("application/xml"):
            self.actPaste.setEnabled(False)

        if len(sel) == 0:
            self.actCopy.setEnabled(False)
            self.actCut.setEnabled(False)
            self.actRename.setEnabled(False)
            self.actDelete.setEnabled(False)
            self.menuPOV.setEnabled(False)
            self.menuStatus.setEnabled(False)
            self.menuLabel.setEnabled(False)
            self.menuCustomIcons.setEnabled(False)

        if len(sel) > 1:
            self.actRename.setEnabled(False)

        return menu

    def openItem(self):
        #idx = self.currentIndex()
        idx = self._indexesToOpen[0]
        from manuskript.functions import MW
        MW.openIndex(idx)

    def openItemsInNewTabs(self):
        from manuskript.functions import MW
        MW.openIndexes(self._indexesToOpen)

    def rename(self):
        if len(self.getSelection()) == 1:
            index = self.currentIndex()
            self.edit(index)
        elif len(self.getSelection()) > 1:
            # FIXME: add smart rename
            pass

    def addFolder(self):
        self.addItem("folder")

    def addText(self):
        self.addItem("text")

    def addItem(self, _type="folder"):
        if len(self.selectedIndexes()) == 0:
            parent = self.rootIndex()
        else:
            parent = self.currentIndex()

        if _type == "text":
            _type = settings.defaultTextType

        item = outlineItem(title=qApp.translate("outlineBasics", "New"),
                           _type=_type)
        self.model().appendItem(item, parent)

    def copy(self):
        mimeData = self.model().mimeData(
            self.selectionModel().selectedIndexes())
        qApp.clipboard().setMimeData(mimeData)

    def paste(self, mimeData=None):
        """
        Paste item from mimeData to selected item. If mimeData is not given,
        it is taken from clipboard. If not item selected, paste into root.
        """
        index = self.currentIndex()
        if len(self.getSelection()) == 0:
            index = self.rootIndex()

        if not mimeData:
            mimeData = qApp.clipboard().mimeData()

        self.model().dropMimeData(mimeData, Qt.CopyAction, -1, 0, index)

    def cut(self):
        self.copy()
        self.delete()

    def delete(self):
        """
        Shows a warning, and then deletes currently selected indexes.
        """
        if not settings.dontShowDeleteWarning:
            msg = QMessageBox(
                QMessageBox.Warning,
                qApp.translate("outlineBasics", "About to remove"),
                qApp.translate(
                    "outlineBasics",
                    "<p><b>You're about to delete {} item(s).</b></p><p>Are you sure?</p>"
                ).format(len(self.getSelection())),
                QMessageBox.Yes | QMessageBox.Cancel)

            chk = QCheckBox("&Don't show this warning in the future.")
            msg.setCheckBox(chk)
            ret = msg.exec()

            if ret == QMessageBox.Cancel:
                return

            if chk.isChecked():
                settings.dontShowDeleteWarning = True

        self.model().removeIndexes(self.getSelection())

    def duplicate(self):
        """
        Duplicates item(s), while preserving clipboard content.
        """
        mimeData = self.model().mimeData(
            self.selectionModel().selectedIndexes())
        self.paste(mimeData)

    def move(self, delta=1):
        """
        Move selected items up or down.
        """

        # we store selected indexes
        currentID = self.model().ID(self.currentIndex())
        selIDs = [self.model().ID(i) for i in self.selectedIndexes()]

        # Block signals
        self.blockSignals(True)
        self.selectionModel().blockSignals(True)

        # Move each index individually
        for idx in self.selectedIndexes():
            self.moveIndex(idx, delta)

        # Done the hardcore way, so inform views
        self.model().layoutChanged.emit()

        # restore selection
        selIdx = [self.model().getIndexByID(ID) for ID in selIDs]
        sm = self.selectionModel()
        sm.clear()
        [sm.select(idx, sm.Select) for idx in selIdx]
        sm.setCurrentIndex(self.model().getIndexByID(currentID), sm.Select)
        #self.setSmsgBoxelectionModel(sm)

        # Unblock signals
        self.blockSignals(False)
        self.selectionModel().blockSignals(False)

    def moveIndex(self, index, delta=1):
        """
        Move the item represented by index. +1 means down, -1 means up.
        """

        if not index.isValid():
            return

        if index.parent().isValid():
            parentItem = index.parent().internalPointer()
        else:
            parentItem = index.model().rootItem

        parentItem.childItems.insert(index.row() + delta,
                                     parentItem.childItems.pop(index.row()))
        parentItem.updateWordCount(emit=False)

    def moveUp(self):
        self.move(-1)

    def moveDown(self):
        self.move(+1)

    def splitDialog(self):
        """
        Opens a dialog to split selected items.

        Call context: if at least one index is selected. Folder or text.
        """

        indexes = self.getSelection()
        if len(indexes) == 0:
            # No selection, we use parent
            indexes = [self.rootIndex()]

        splitDialog(self, indexes)

    def merge(self):
        """
        Merges selected items together.

        Call context: Multiple selection, same parent.
        """

        # Get selection
        indexes = self.getSelection()
        # Get items
        items = [i.internalPointer() for i in indexes if i.isValid()]
        # Remove folders
        items = [i for i in items if not i.isFolder()]

        # Check that we have at least 2 items
        if len(items) < 2:
            statusMessage(qApp.translate(
                "outlineBasics",
                "Select at least two items. Folders are ignored."),
                          importance=2)
            return

        # Check that all share the same parent
        p = items[0].parent()
        for i in items:
            if i.parent() != p:
                statusMessage(qApp.translate(
                    "outlineBasics",
                    "All items must be on the same level (share the same parent)."
                ),
                              importance=2)
                return

        # Sort items by row
        items = sorted(items, key=lambda i: i.row())

        items[0].mergeWith(items[1:])

    def setPOV(self, POV):
        for i in self.getSelection():
            self.model().setData(i.sibling(i.row(), Outline.POV), str(POV))

    def setStatus(self, status):
        for i in self.getSelection():
            self.model().setData(i.sibling(i.row(), Outline.status),
                                 str(status))

    def setLabel(self, label):
        for i in self.getSelection():
            self.model().setData(i.sibling(i.row(), Outline.label), str(label))

    def setCustomIcon(self, customIcon):
        for i in self.getSelection():
            item = i.internalPointer()
            item.setCustomIcon(customIcon)

    def setCustomIconFromItem(self, item):
        icon = item.data(Qt.UserRole)
        self.setCustomIcon(icon)
        self.menu.close()

    def filterLstIcons(self, text):
        for l in self.lstIcons.findItems("", Qt.MatchContains):
            l.setHidden(not text in l.data(Qt.UserRole))
コード例 #10
0
ファイル: wordlist.py プロジェクト: triptych/novelWriter
class GuiWordList(QDialog):
    def __init__(self, theParent, theProject):
        QDialog.__init__(self, theParent)

        logger.debug("Initialising GuiWordList ...")
        self.setObjectName("GuiWordList")

        self.mainConf = nw.CONFIG
        self.theParent = theParent
        self.theTheme = theParent.theTheme
        self.theProject = theProject
        self.optState = theProject.optState

        self.setWindowTitle(self.tr("Project Word List"))

        mS = self.mainConf.pxInt(250)
        wW = self.mainConf.pxInt(320)
        wH = self.mainConf.pxInt(340)

        self.setMinimumWidth(mS)
        self.setMinimumHeight(mS)
        self.resize(
            self.mainConf.pxInt(
                self.optState.getInt("GuiWordList", "winWidth", wW)),
            self.mainConf.pxInt(
                self.optState.getInt("GuiWordList", "winHeight", wH)))

        # Main Widgets
        # ============

        self.headLabel = QLabel("<b>%s</b>" % self.tr("Project Word List"))

        self.listBox = QListWidget()
        self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
        self.listBox.setSortingEnabled(True)

        self.newEntry = QLineEdit()

        self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
        self.addButton.setToolTip(self.tr("Add new entry"))
        self.addButton.clicked.connect(self._doAdd)

        self.delButton = QPushButton(self.theTheme.getIcon("remove"), "")
        self.delButton.setToolTip(self.tr("Delete selected entry"))
        self.delButton.clicked.connect(self._doDelete)

        self.editBox = QHBoxLayout()
        self.editBox.addWidget(self.newEntry, 1)
        self.editBox.addWidget(self.addButton, 0)
        self.editBox.addWidget(self.delButton, 0)

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save
                                          | QDialogButtonBox.Close)
        self.buttonBox.accepted.connect(self._doSave)
        self.buttonBox.rejected.connect(self._doClose)

        # Assemble
        # ========

        self.outerBox = QVBoxLayout()
        self.outerBox.addWidget(self.headLabel)
        self.outerBox.addSpacing(self.mainConf.pxInt(8))
        self.outerBox.addWidget(self.listBox, 1)
        self.outerBox.addLayout(self.editBox, 0)
        self.outerBox.addSpacing(self.mainConf.pxInt(12))
        self.outerBox.addWidget(self.buttonBox, 0)

        self.setLayout(self.outerBox)

        self._loadWordList()

        logger.debug("GuiWordList initialisation complete")

        return

    ##
    #  Slots
    ##

    def _doAdd(self):
        """Add a new word to the word list.
        """
        newWord = self.newEntry.text().strip()
        if newWord == "":
            self.theParent.makeAlert(self.tr("Cannot add a blank word."),
                                     nwAlert.ERROR)
            return False

        if self.listBox.findItems(newWord, Qt.MatchExactly):
            self.theParent.makeAlert(
                self.tr("The word '{0}' is already in the word list.").format(
                    newWord), nwAlert.ERROR)
            return False

        self.listBox.addItem(newWord)
        self.newEntry.setText("")

        return True

    def _doDelete(self):
        """Delete the selected item.
        """
        selItem = self.listBox.selectedItems()
        if selItem:
            self.listBox.takeItem(self.listBox.row(selItem[0]))
        return

    def _doSave(self):
        """Save the new word list and close.
        """
        self._saveGuiSettings()

        dctFile = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
        tmpFile = dctFile + "~"

        try:
            with open(tmpFile, mode="w", encoding="utf8") as outFile:
                for i in range(self.listBox.count()):
                    outFile.write(self.listBox.item(i).text() + "\n")

        except Exception:
            logger.error("Could not save new word list")
            nw.logException()
            self.reject()
            return False

        if os.path.isfile(dctFile):
            os.unlink(dctFile)
        os.rename(tmpFile, dctFile)
        self.accept()

        return True

    def _doClose(self):
        """Close without saving the word list.
        """
        self._saveGuiSettings()
        self.reject()
        return

    ##
    #  Internal Functions
    ##

    def _loadWordList(self):
        """Load the project's word list, if it exists.
        """
        self.listBox.clear()

        wordList = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
        if not os.path.isfile(wordList):
            logger.debug("No project dictionary file found")
            return False

        with open(wordList, mode="r", encoding="utf8") as inFile:
            for inLine in inFile:
                theWord = inLine.strip()
                if len(theWord) == 0:
                    continue
                self.listBox.addItem(theWord)

        return True

    def _saveGuiSettings(self):
        """Save GUI settings.
        """
        winWidth = self.mainConf.rpxInt(self.width())
        winHeight = self.mainConf.rpxInt(self.height())

        self.optState.setValue("GuiWordList", "winWidth", winWidth)
        self.optState.setValue("GuiWordList", "winHeight", winHeight)

        return
コード例 #11
0
ファイル: runoapps.py プロジェクト: lliurex/runomatic
class desktopChooser(QDialog):
    dblClicked = pyqtSignal("PyQt_PyObject")

    def __init__(self, parent):
        super(desktopChooser, self).__init__(parent)
        self.parent = parent
        self.menu = App2Menu.app2menu()
        self.setWindowTitle(_("Launcher select"))
        self.setModal(False)
        self.desktopList = QListWidget()
        self.desktopList.setSortingEnabled(True)
        self.desktopList.setDragEnabled(True)
        self.desktopList.setAcceptDrops(True)
        self.desktopList.setSpacing(3)
        self.desktopList.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.desktopList.itemDoubleClicked.connect(self._dblClick)
        self.desktopList.itemPressed.connect(self._loadMime)
        self.data = {}
        self._renderGui()

    def _renderGui(self):
        def searchList():
            items = self.desktopList.findItems(inp_search.text(),
                                               Qt.MatchFlag.MatchContains)
            if items:
                self.desktopList.scrollToItem(items[0])
                self.desktopList.setCurrentItem(items[0])

        box = QVBoxLayout()
        inp_search = QLineEdit()
        inp_search.setPlaceholderText(_("Search"))
        inp_search.textChanged.connect(searchList)
        completer = QCompleter()
        completer.setCaseSensitivity(Qt.CaseInsensitive)
        model = QtGui.QStandardItemModel()
        #Load available desktops
        categories = self.menu.get_categories()
        for category in categories:
            desktops = self.menu.get_apps_from_category(category)
            for desktop in desktops.keys():
                desktopInfo = self.menu.get_desktop_info(
                    "%s/%s" % (self.menu.desktoppath, desktop))
                if desktopInfo.get("NoDisplay", False):
                    continue
                listWidget = QListWidgetItem()
                desktopLayout = QGridLayout()
                ficon = desktopInfo.get("Icon", "shell")
                icon = QtGui.QIcon.fromTheme(ficon)
                name = desktopInfo.get("Name", "shell")
                model.appendRow(QtGui.QStandardItem(name))
                comment = desktopInfo.get("Comment", "shell")
                listWidget.setIcon(icon)
                listWidget.setText(name)
                self.desktopList.addItem(listWidget)
                self.data[self.desktopList.count() - 1] = {
                    'path': "%s/%s" % (self.menu.desktoppath, desktop),
                    'icon': icon
                }
        completer.setModel(model)
        inp_search.setCompleter(completer)
        box.addWidget(inp_search)
        box.addWidget(self.desktopList)
        self.setLayout(box)

    def _dblClick(self):
        listWidget = self.desktopList.currentRow()
        path = self.data[listWidget]
        self.dblClicked.emit(path)

    def _loadMime(self):
        listWidget = self.desktopList.currentRow()
        mimedata = QMimeData()
        mimedata.setText(self.data[listWidget]['path'])
        drag = QtGui.QDrag(self)
        drag.setMimeData(mimedata)
        pixmap = self.data[listWidget]['icon'].pixmap(QSize(
            BTN_SIZE, BTN_SIZE))
        drag.setPixmap(pixmap)
        dropAction = drag.exec_(Qt.MoveAction)

    #def mousePressEvent

    def dragMoveEvent(self, e):
        e.accept()

    #def dragEnterEvent

    def dragEnterEvent(self, e):
        e.accept()
コード例 #12
0
class outlineBasics(QAbstractItemView):
    def __init__(self, parent=None):
        self._indexesToOpen = None
        self.menuCustomIcons = None

    def getSelection(self):
        sel = []
        for i in self.selectedIndexes():
            if i.column() != 0:
                continue
            if not i in sel:
                sel.append(i)
        return sel

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.RightButton:
            self.menu = self.makePopupMenu()
            self.menu.popup(event.globalPos())
        # We don't call QAbstractItemView.mouseReleaseEvent because
        # outlineBasics is never subclassed alone. So the others views
        # (outlineView, corkView, treeView) that subclass outlineBasics
        # call their respective mother class.

    def makePopupMenu(self):
        index = self.currentIndex()
        sel = self.getSelection()
        clipboard = qApp.clipboard()

        menu = QMenu(self)

        # Get index under cursor
        pos = self.viewport().mapFromGlobal(QCursor.pos())
        mouseIndex = self.indexAt(pos)

        # Get index's title
        if mouseIndex.isValid():
            title = mouseIndex.internalPointer().title()

        elif self.rootIndex().parent().isValid():
            # mouseIndex is the background of an item, so we check the parent
            mouseIndex = self.rootIndex().parent()
            title = mouseIndex.internalPointer().title()

        else:
            title = qApp.translate("outlineBasics", "Root")

        if len(title) > 25:
            title = title[:25] + "…"

        # Open Item action
        self.actOpen = QAction(QIcon.fromTheme("go-right"),
                               qApp.translate("outlineBasics", "Open {}".format(title)),
                               menu)
        self.actOpen.triggered.connect(self.openItem)
        menu.addAction(self.actOpen)

        # Open item(s) in new tab
        if mouseIndex in sel and len(sel) > 1:
            actionTitle = qApp.translate("outlineBasics", "Open {} items in new tabs").format(len(sel))
            self._indexesToOpen = sel
        else:
            actionTitle = qApp.translate("outlineBasics", "Open {} in a new tab").format(title)
            self._indexesToOpen = [mouseIndex]

        self.actNewTab = QAction(QIcon.fromTheme("go-right"), actionTitle, menu)
        self.actNewTab.triggered.connect(self.openItemsInNewTabs)
        menu.addAction(self.actNewTab)

        menu.addSeparator()

        # Add text / folder
        self.actAddFolder = QAction(QIcon.fromTheme("folder-new"),
                                    qApp.translate("outlineBasics", "New &Folder"),
                                    menu)
        self.actAddFolder.triggered.connect(self.addFolder)
        menu.addAction(self.actAddFolder)

        self.actAddText = QAction(QIcon.fromTheme("document-new"),
                                  qApp.translate("outlineBasics", "New &Text"),
                                  menu)
        self.actAddText.triggered.connect(self.addText)
        menu.addAction(self.actAddText)

        menu.addSeparator()

        # Copy, cut, paste, duplicate
        self.actCut = QAction(QIcon.fromTheme("edit-cut"),
                              qApp.translate("outlineBasics", "C&ut"), menu)
        self.actCut.triggered.connect(self.cut)
        menu.addAction(self.actCut)

        self.actCopy = QAction(QIcon.fromTheme("edit-copy"),
                               qApp.translate("outlineBasics", "&Copy"), menu)
        self.actCopy.triggered.connect(self.copy)
        menu.addAction(self.actCopy)

        self.actPaste = QAction(QIcon.fromTheme("edit-paste"),
                                qApp.translate("outlineBasics", "&Paste"), menu)
        self.actPaste.triggered.connect(self.paste)
        menu.addAction(self.actPaste)

        # Rename / duplicate / remove items
        self.actDelete = QAction(QIcon.fromTheme("edit-delete"),
                                 qApp.translate("outlineBasics", "&Delete"),
                                 menu)
        self.actDelete.triggered.connect(self.delete)
        menu.addAction(self.actDelete)

        self.actRename = QAction(QIcon.fromTheme("edit-rename"),
                                 qApp.translate("outlineBasics", "&Rename"),
                                 menu)
        self.actRename.triggered.connect(self.rename)
        menu.addAction(self.actRename)

        menu.addSeparator()

        # POV
        self.menuPOV = QMenu(qApp.translate("outlineBasics", "Set POV"), menu)
        mw = mainWindow()
        a = QAction(QIcon.fromTheme("dialog-no"), qApp.translate("outlineBasics", "None"), self.menuPOV)
        a.triggered.connect(lambda: self.setPOV(""))
        self.menuPOV.addAction(a)
        self.menuPOV.addSeparator()

        menus = []
        for i in [qApp.translate("outlineBasics", "Main"),
                  qApp.translate("outlineBasics", "Secondary"),
                  qApp.translate("outlineBasics", "Minor")]:
            m = QMenu(i, self.menuPOV)
            menus.append(m)
            self.menuPOV.addMenu(m)

        mpr = QSignalMapper(self.menuPOV)
        for i in range(mw.mdlCharacter.rowCount()):
            a = QAction(mw.mdlCharacter.icon(i), mw.mdlCharacter.name(i), self.menuPOV)
            a.triggered.connect(mpr.map)
            mpr.setMapping(a, int(mw.mdlCharacter.ID(i)))

            imp = toInt(mw.mdlCharacter.importance(i))

            menus[2 - imp].addAction(a)

        mpr.mapped.connect(self.setPOV)
        menu.addMenu(self.menuPOV)

        # Status
        self.menuStatus = QMenu(qApp.translate("outlineBasics", "Set Status"), menu)
        # a = QAction(QIcon.fromTheme("dialog-no"), qApp.translate("outlineBasics", "None"), self.menuStatus)
        # a.triggered.connect(lambda: self.setStatus(""))
        # self.menuStatus.addAction(a)
        # self.menuStatus.addSeparator()

        mpr = QSignalMapper(self.menuStatus)
        for i in range(mw.mdlStatus.rowCount()):
            a = QAction(mw.mdlStatus.item(i, 0).text(), self.menuStatus)
            a.triggered.connect(mpr.map)
            mpr.setMapping(a, i)
            self.menuStatus.addAction(a)
        mpr.mapped.connect(self.setStatus)
        menu.addMenu(self.menuStatus)

        # Labels
        self.menuLabel = QMenu(qApp.translate("outlineBasics", "Set Label"), menu)
        mpr = QSignalMapper(self.menuLabel)
        for i in range(mw.mdlLabels.rowCount()):
            a = QAction(mw.mdlLabels.item(i, 0).icon(),
                        mw.mdlLabels.item(i, 0).text(),
                        self.menuLabel)
            a.triggered.connect(mpr.map)
            mpr.setMapping(a, i)
            self.menuLabel.addAction(a)
        mpr.mapped.connect(self.setLabel)
        menu.addMenu(self.menuLabel)

        menu.addSeparator()

        # Custom icons
        if self.menuCustomIcons:
            menu.addMenu(self.menuCustomIcons)
        else:
            self.menuCustomIcons = QMenu(qApp.translate("outlineBasics", "Set Custom Icon"), menu)
            a = QAction(qApp.translate("outlineBasics", "Restore to default"), self.menuCustomIcons)
            a.triggered.connect(lambda: self.setCustomIcon(""))
            self.menuCustomIcons.addAction(a)
            self.menuCustomIcons.addSeparator()

            txt = QLineEdit()
            txt.textChanged.connect(self.filterLstIcons)
            txt.setPlaceholderText("Filter icons")
            txt.setStyleSheet("QLineEdit { background: transparent; border: none; }")
            act = QWidgetAction(self.menuCustomIcons)
            act.setDefaultWidget(txt)
            self.menuCustomIcons.addAction(act)

            self.lstIcons = QListWidget()
            for i in customIcons():
                item = QListWidgetItem()
                item.setIcon(QIcon.fromTheme(i))
                item.setData(Qt.UserRole, i)
                item.setToolTip(i)
                self.lstIcons.addItem(item)
            self.lstIcons.itemClicked.connect(self.setCustomIconFromItem)
            self.lstIcons.setViewMode(self.lstIcons.IconMode)
            self.lstIcons.setUniformItemSizes(True)
            self.lstIcons.setResizeMode(self.lstIcons.Adjust)
            self.lstIcons.setMovement(self.lstIcons.Static)
            self.lstIcons.setStyleSheet("background: transparent; background: none;")
            self.filterLstIcons("")
            act = QWidgetAction(self.menuCustomIcons)
            act.setDefaultWidget(self.lstIcons)
            self.menuCustomIcons.addAction(act)

            menu.addMenu(self.menuCustomIcons)

        # Disabling stuff
        if not clipboard.mimeData().hasFormat("application/xml"):
            self.actPaste.setEnabled(False)

        if len(sel) == 0:
            self.actCopy.setEnabled(False)
            self.actCut.setEnabled(False)
            self.actRename.setEnabled(False)
            self.actDelete.setEnabled(False)
            self.menuPOV.setEnabled(False)
            self.menuStatus.setEnabled(False)
            self.menuLabel.setEnabled(False)
            self.menuCustomIcons.setEnabled(False)

        if len(sel) > 1:
            self.actRename.setEnabled(False)

        return menu

    def openItem(self):
        #idx = self.currentIndex()
        idx = self._indexesToOpen[0]
        from manuskript.functions import MW
        MW.openIndex(idx)

    def openItemsInNewTabs(self):
        from manuskript.functions import MW
        MW.openIndexes(self._indexesToOpen)

    def rename(self):
        if len(self.getSelection()) == 1:
            index = self.currentIndex()
            self.edit(index)
        elif len(self.getSelection()) > 1:
            # FIXME: add smart rename
            pass

    def addFolder(self):
        self.addItem("folder")

    def addText(self):
        self.addItem("text")

    def addItem(self, _type="folder"):
        if len(self.selectedIndexes()) == 0:
            parent = self.rootIndex()
        else:
            parent = self.currentIndex()

        if _type == "text":
            _type = settings.defaultTextType

        item = outlineItem(title=qApp.translate("outlineBasics", "New"), _type=_type)
        self.model().appendItem(item, parent)

    def copy(self):
        mimeData = self.model().mimeData(self.selectionModel().selectedIndexes())
        qApp.clipboard().setMimeData(mimeData)

    def paste(self, mimeData=None):
        """
        Paste item from mimeData to selected item. If mimeData is not given,
        it is taken from clipboard. If not item selected, paste into root.
        """
        index = self.currentIndex()
        if len(self.getSelection()) == 0:
            index = self.rootIndex()

        if not mimeData:
            mimeData = qApp.clipboard().mimeData()

        self.model().dropMimeData(mimeData, Qt.CopyAction, -1, 0, index)

    def cut(self):
        self.copy()
        self.delete()

    def delete(self):
        """
        Shows a warning, and then deletes currently selected indexes.
        """
        if not settings.dontShowDeleteWarning:
            msg = QMessageBox(QMessageBox.Warning,
                qApp.translate("outlineBasics", "About to remove"),
                qApp.translate("outlineBasics",
                    "<p><b>You're about to delete {} item(s).</b></p><p>Are you sure?</p>"
                    ).format(len(self.getSelection())),
                QMessageBox.Yes | QMessageBox.Cancel)

            chk = QCheckBox("&Don't show this warning in the future.")
            msg.setCheckBox(chk)
            ret = msg.exec()

            if ret == QMessageBox.Cancel:
                return

            if chk.isChecked():
                settings.dontShowDeleteWarning = True

        self.model().removeIndexes(self.getSelection())

    def duplicate(self):
        """
        Duplicates item(s), while preserving clipboard content.
        """
        mimeData = self.model().mimeData(self.selectionModel().selectedIndexes())
        self.paste(mimeData)

    def move(self, delta=1):
        """
        Move selected items up or down.
        """

        # we store selected indexes
        currentID = self.model().ID(self.currentIndex())
        selIDs = [self.model().ID(i) for i in self.selectedIndexes()]

        # Block signals
        self.blockSignals(True)
        self.selectionModel().blockSignals(True)

        # Move each index individually
        for idx in self.selectedIndexes():
            self.moveIndex(idx, delta)

        # Done the hardcore way, so inform views
        self.model().layoutChanged.emit()

        # restore selection
        selIdx = [self.model().getIndexByID(ID) for ID in selIDs]
        sm = self.selectionModel()
        sm.clear()
        [sm.select(idx, sm.Select) for idx in selIdx]
        sm.setCurrentIndex(self.model().getIndexByID(currentID), sm.Select)
        #self.setSmsgBoxelectionModel(sm)

        # Unblock signals
        self.blockSignals(False)
        self.selectionModel().blockSignals(False)

    def moveIndex(self, index, delta=1):
        """
        Move the item represented by index. +1 means down, -1 means up.
        """

        if not index.isValid():
            return

        if index.parent().isValid():
            parentItem = index.parent().internalPointer()
        else:
            parentItem = index.model().rootItem

        parentItem.childItems.insert(index.row() + delta,
                                     parentItem.childItems.pop(index.row()))
        parentItem.updateWordCount(emit=False)

    def moveUp(self): self.move(-1)
    def moveDown(self): self.move(+1)

    def splitDialog(self):
        """
        Opens a dialog to split selected items.

        Call context: if at least one index is selected. Folder or text.
        """

        indexes = self.getSelection()
        if len(indexes) == 0:
            # No selection, we use parent
            indexes = [self.rootIndex()]

        splitDialog(self, indexes)

    def merge(self):
        """
        Merges selected items together.

        Call context: Multiple selection, same parent.
        """

        # Get selection
        indexes = self.getSelection()
        # Get items
        items = [i.internalPointer() for i in indexes if i.isValid()]
        # Remove folders
        items = [i for i in items if not i.isFolder()]

        # Check that we have at least 2 items
        if len(items) < 2:
            statusMessage(qApp.translate("outlineBasics",
                          "Select at least two items. Folders are ignored."),
                          importance=2)
            return

        # Check that all share the same parent
        p = items[0].parent()
        for i in items:
            if i.parent() != p:
                statusMessage(qApp.translate("outlineBasics",
                          "All items must be on the same level (share the same parent)."),
                          importance=2)
                return

        # Sort items by row
        items = sorted(items, key=lambda i: i.row())

        items[0].mergeWith(items[1:])

    def setPOV(self, POV):
        for i in self.getSelection():
            self.model().setData(i.sibling(i.row(), Outline.POV), str(POV))

    def setStatus(self, status):
        for i in self.getSelection():
            self.model().setData(i.sibling(i.row(), Outline.status), str(status))

    def setLabel(self, label):
        for i in self.getSelection():
            self.model().setData(i.sibling(i.row(), Outline.label), str(label))

    def setCustomIcon(self, customIcon):
        for i in self.getSelection():
            item = i.internalPointer()
            item.setCustomIcon(customIcon)

    def setCustomIconFromItem(self, item):
        icon = item.data(Qt.UserRole)
        self.setCustomIcon(icon)
        self.menu.close()

    def filterLstIcons(self, text):
        for l in self.lstIcons.findItems("", Qt.MatchContains):
            l.setHidden(not text in l.data(Qt.UserRole))
コード例 #13
0
class ToolBar(QWidget):
    mouseEntered = pyqtSignal()
    mouseLeft = pyqtSignal()

    toolChanged = pyqtSignal(str)
    primaryInkChanged = pyqtSignal(str)
    secondaryInkChanged = pyqtSignal(str)

    def __init__(self):

        super(ToolBar, self).__init__()

        self.setAttribute(Qt.WA_StaticContents)
        self.setAttribute(Qt.WA_NoSystemBackground)

        self.setFont(ResourcesCache.get("BigFont"))

        self._registeredTools = {}
        self._registeredInks = {}

        self._toolSlots = []
        self._inkSlots = []

        self._currentActiveToolSlot = None
        self._previousActiveToolSlot = None

        self._currentEditedInkSlot = None
        self._previousEditedInkSlot = None

        self._editMode = False

        self._backgroundColor = QColor(40, 40, 40)
        self._toolLabelColor = QColor(112, 231, 255)

        self._layout = QVBoxLayout()
        self._layout.setAlignment(Qt.AlignTop)
        self._layout.setContentsMargins(4, 4, 4, 4)

        top_layout = QHBoxLayout()

        self._layout.addLayout(top_layout)

        self._toolsLayout = QHBoxLayout()
        self._inksLayout = QHBoxLayout()

        self._toolsLayout.setContentsMargins(0, 0, 0, 0)
        self._toolsLayout.setAlignment(Qt.AlignLeft)

        self._inksLayout.setContentsMargins(0, 0, 0, 0)
        self._inksLayout.setAlignment(Qt.AlignRight)

        top_layout.addLayout(self._toolsLayout)
        top_layout.addLayout(self._inksLayout)

        self._toolsButtonGroup = QButtonGroup()
        self._toolsButtonGroup.buttonClicked.connect(
            self._on_tool_slot_triggered)

        self._inksButtonGroup = QButtonGroup()
        self._inksButtonGroup.setExclusive(False)
        self._inksButtonGroup.buttonClicked.connect(
            self._on_ink_slot_triggered)

        self.setLayout(self._layout)

        self._toolbarSubPanel = None
        self._toolsListWidget = None
        self._toolsOptionsPanel = None

        self._init_edit_panel()

        self._add_ink_slot(0)
        self._add_ink_slot(1)

        self.resize(0, 50)

    # -------------------------------------------------------------------------

    def get_tool_by_name(self, name):

        return self._registeredTools[name]

    def register_tool(self, tool, is_default=None):

        if tool.name not in self._registeredTools:

            self._registeredTools[tool.name] = tool
            self._toolsListWidget.addItem(tool.name)

            if is_default is True:
                self._toolsListWidget.setCurrentRow(0)

            self._build_tool_options_pane(tool)

            if len(self._toolSlots) < 4:
                slot_index = self._add_tool_slot(is_default)
                self._assign_tool_to_slot(tool, slot_index)

    def register_ink(self, ink, slot):

        if not ink.name in self._registeredInks:
            self._registeredInks[ink.name] = ink

            self._inksListWidget.addItem(ink.name)

            self._build_ink_options_pane(ink)

            if self._inkSlots[slot]['id'] is None:
                self._assign_ink_to_slot(ink, slot)

    def switch_tool_slot(self, slot):

        self._previousActiveToolSlot = self._currentActiveToolSlot

        self._currentActiveToolSlot = slot

        if self._currentActiveToolSlot == self._previousActiveToolSlot:
            return

        tool_name = self._toolSlots[slot]['id']

        self._toolSlots[slot]['button'].setChecked(True)

        self.toolChanged.emit(tool_name)

        self._select_tool_on_list(tool_name)

    # -------------------------------------------------------------------------

    def _go_back_to_last_tool(self):
        self.switch_tool_slot(self._previousActiveToolSlot)

    def _add_tool_slot(self, selected=None):

        slot_button = QPushButton()
        slot_button.setCheckable(True)

        index = len(self._toolSlots)

        if selected is not None and selected is True:
            slot_button.setChecked(True)

        slot = {'id': None, 'button': slot_button}

        if selected:
            self._currentActiveToolSlot = index

        self._toolSlots.append(slot)

        self._toolsButtonGroup.addButton(slot_button, index)

        self._toolsLayout.addWidget(slot_button)

        return index

    def _add_ink_slot(self, slot_number):

        slot_button = QPushButton()
        slot_button.setFont(self.font())
        slot_button.setStyleSheet(
            "border-color: rgb(56,56,56); background-color: rgb(17,17,"
            "17); font-size: 12pt;")

        index = len(self._inkSlots)

        if slot_number == 0:

            icon = QIcon()
            icon.addPixmap(QPixmap(":/icons/ico_mouse_button1"), QIcon.Normal,
                           QIcon.Off)

            slot_button.setIcon(icon)
            slot_button.setIconSize(QSize(18, 23))

        elif slot_number == 1:

            icon = QIcon()
            icon.addPixmap(QPixmap(":/icons/ico_mouse_button2"), QIcon.Normal,
                           QIcon.Off)

            slot_button.setIcon(icon)
            slot_button.setIconSize(QSize(18, 23))

        slot = {'id': None, 'button': slot_button}

        self._inkSlots.append(slot)

        self._inksButtonGroup.addButton(slot_button)

        self._inksButtonGroup.setId(slot_button, index)

        self._inksLayout.addWidget(slot_button)

        return index

    def _assign_tool_to_slot(self, tool, slot):

        if slot < 0 or slot > len(self._toolSlots) - 1:
            raise Exception(
                '[ToolBar] > _assignToolToSlot : invalid slot parameter')

        self._toolSlots[slot]['id'] = tool.name

        icon = tool.icon

        if icon is not None:
            tool_button = self._toolSlots[slot]['button']
            tool_button.setIcon(tool.icon)
            tool_button.setIconSize(QSize(24, 24))

    def _assign_ink_to_slot(self, ink, slot):

        if slot != 0 and slot != 1:
            raise Exception(
                '[ToolBar] > _assignInkToSlot : invalid slot parameter')

        ink_name = ink.name

        self._inkSlots[slot]['id'] = ink_name
        self._inkSlots[slot]['button'].setText(ink_name)

        if slot == 0:
            self.primaryInkChanged.emit(ink_name)
        elif slot == 1:
            self.secondaryInkChanged.emit(ink_name)

    def _init_edit_panel(self):

        self._toolbarSubPanel = QStackedWidget()

        # 1. Initialize Tools Control Panel -----------------------------------

        self._toolsListWidget = QListWidget()

        self._toolsListWidget.currentRowChanged.connect(
            lambda v: self._toolsOptionsPanel.setCurrentIndex(v))

        self._toolsListWidget.setMaximumSize(QSize(150, 200))

        self._toolsListWidget.itemClicked.connect(
            self._on_tool_list_item_clicked)

        # Tools Subpanel ------------------------------------------------------

        tools_control_panel = QWidget()

        tools_control_panel_layout = QHBoxLayout()

        tools_control_panel.setLayout(tools_control_panel_layout)

        tools_control_panel_layout.setAlignment(Qt.AlignLeft)

        # Tools List ----------------------------------------------------------

        tools_list_sublayout = QVBoxLayout()

        tools_list_sublayout.setAlignment(Qt.AlignTop)

        tools_list_sublayout.setContentsMargins(0, 0, 0, 0)

        tools_list_sublayout.addWidget(QLabel("Tools"))

        tools_list_sublayout.addWidget(self._toolsListWidget)

        tools_control_panel_layout.addLayout(tools_list_sublayout)

        # Tools Options -------------------------------------------------------

        tools_options_sublayout = QVBoxLayout()

        tools_options_sublayout.setAlignment(Qt.AlignTop)

        tools_control_panel_layout.addLayout(tools_options_sublayout)

        self._toolsOptionsPanel = QStackedWidget()

        tools_options_sublayout.addWidget(QLabel("Tools Options"))

        tools_options_sublayout.addWidget(self._toolsOptionsPanel)

        self._toolbarSubPanel.addWidget(tools_control_panel)

        # 2. Initialize Inks Control Panel ------------------------------------

        self._inksListWidget = QListWidget()

        self._inksListWidget.currentRowChanged.connect(
            lambda v: self._inksOptionsPanel.setCurrentIndex(v))

        self._inksListWidget.setMaximumSize(QSize(150, 200))

        self._inksListWidget.itemClicked.connect(
            self._on_ink_list_item_clicked)

        # Inks Subpanel -------------------------------------------------------

        inks_control_panel = QWidget()

        inks_control_panel_layout = QHBoxLayout()

        inks_control_panel.setLayout(inks_control_panel_layout)

        inks_control_panel_layout.setAlignment(Qt.AlignLeft)

        # Inks List -----------------------------------------------------------

        inks_list_sublayout = QVBoxLayout()

        inks_list_sublayout.setAlignment(Qt.AlignTop)

        inks_list_sublayout.setContentsMargins(0, 0, 0, 0)

        inks_list_sublayout.addWidget(QLabel("Inks"))

        inks_list_sublayout.addWidget(self._inksListWidget)

        inks_control_panel_layout.addLayout(inks_list_sublayout)

        # Inks Options --------------------------------------------------------

        inks_options_sublayout = QVBoxLayout()

        inks_options_sublayout.setAlignment(Qt.AlignTop)

        inks_control_panel_layout.addLayout(inks_options_sublayout)

        self._inksOptionsPanel = QStackedWidget()

        inks_options_sublayout.addWidget(QLabel("Ink Options"))

        inks_options_sublayout.addWidget(self._inksOptionsPanel)

        self._toolbarSubPanel.addWidget(inks_control_panel)

        # ---------------------------------------------------------------------

        self._layout.addWidget(self._toolbarSubPanel)

        self._toolbarSubPanel.setVisible(False)

    def _build_tool_options_pane(self, tool):

        pane = QWidget()

        pane_layout = QVBoxLayout()
        pane_layout.setAlignment(Qt.AlignTop)
        pane.setLayout(pane_layout)

        for prop in tool.properties.values():
            field_layout = QHBoxLayout()

            field_layout.addWidget(QLabel(prop.description))

            prop_widget = prop.build_property_widget()

            field_layout.addWidget(prop_widget)

            pane_layout.addLayout(field_layout)

        self._toolsOptionsPanel.addWidget(pane)

    def _build_ink_options_pane(self, ink):

        pane = QWidget()

        pane_layout = QVBoxLayout()
        pane_layout.setAlignment(Qt.AlignTop)
        pane.setLayout(pane_layout)

        for prop in ink.properties.values():
            field_layout = QHBoxLayout()
            field_layout.addWidget(QLabel(prop.description))

            prop_widget = prop.build_property_widget()

            field_layout.addWidget(prop_widget)

            pane_layout.addLayout(field_layout)

        self._inksOptionsPanel.addWidget(pane)

    def _select_tool_on_list(self, tool_name):

        tool_list_item = \
            self._toolsListWidget.findItems(tool_name, Qt.MatchExactly)[0]

        if tool_list_item is not None:
            self._toolsListWidget.setCurrentItem(tool_list_item)

    def _select_ink_on_list(self, ink_name):

        ink_list_item = \
            self._inksListWidget.findItems(ink_name, Qt.MatchExactly)[0]

        if ink_list_item is not None:
            self._inksListWidget.setCurrentItem(ink_list_item)

    def _toggle_edit_mode(self):

        if not self._editMode:

            self._show_sub_panel()

        else:

            self._hide_sub_panel()

        self.update()

    def _show_sub_panel(self):

        self._editMode = True
        self.resize(self.width(), 300)
        self._toolbarSubPanel.setVisible(True)

    def _hide_sub_panel(self):

        self._editMode = False
        self.resize(self.width(), 50)
        self._toolbarSubPanel.setVisible(False)

        self._finish_ink_edit_mode()

    def _finish_ink_edit_mode(self):

        if self._currentEditedInkSlot is not None:
            self._inksButtonGroup.button(self._currentEditedInkSlot). \
                setStyleSheet("border-color: rgb(56,56,56);")
            self._currentEditedInkSlot = None
            self._previousEditedInkSlot = None
            self._inksListWidget.setCurrentRow(0)
            self._toolbarSubPanel.setCurrentIndex(0)

    # -------------------------------------------------------------------------

    def mousePressEvent(self, e):

        self._toggle_edit_mode()

        e.accept()

    def wheelEvent(self, e):

        e.accept()

    def enterEvent(self, e):

        self.mouseEntered.emit()
        self.setCursor(Qt.PointingHandCursor)

    def leaveEvent(self, e):

        self.mouseLeft.emit()

    def _on_tool_slot_triggered(self):

        self._toolbarSubPanel.setCurrentIndex(0)

        triggered_slot = self._toolsButtonGroup.checkedId()

        if self._currentEditedInkSlot is not None:
            self._finish_ink_edit_mode()

        self.switch_tool_slot(triggered_slot)

        self.update()

    def _on_ink_slot_triggered(self, slot_button):

        if not self._editMode:
            self._show_sub_panel()

        triggered_slot_id = self._inksButtonGroup.id(slot_button)

        if triggered_slot_id != self._currentEditedInkSlot:

            self._previousEditedInkSlot = self._currentEditedInkSlot

            self._currentEditedInkSlot = triggered_slot_id

            if self._previousEditedInkSlot is not None:
                self._inksButtonGroup. \
                    button(self._previousEditedInkSlot). \
                    setStyleSheet("border-color: rgb(56,56,56);")

            slot_button.setStyleSheet("border-color: rgb(255,0,0);")

            self._toolbarSubPanel.setCurrentIndex(1)

            ink_name = self._inkSlots[triggered_slot_id]['id']

            self._select_ink_on_list(ink_name)

            if triggered_slot_id == 0:
                self.primaryInkChanged.emit(ink_name)
            elif triggered_slot_id == 1:
                self.secondaryInkChanged.emit(ink_name)

        else:

            self._hide_sub_panel()

    def _on_tool_list_item_clicked(self, new_item):

        new_item_name = new_item.text()
        self._assign_tool_to_slot(self.get_tool_by_name(new_item_name),
                                  self._currentActiveToolSlot)
        self.toolChanged.emit(new_item_name)
        self._toolbarSubPanel.update()

    def _on_ink_list_item_clicked(self, item):

        item_name = item.text()

        ink = self._registeredInks[item_name]

        if ink is not None:
            self._assign_ink_to_slot(ink, self._currentEditedInkSlot)
コード例 #14
0
ファイル: fontdialog.py プロジェクト: umlfri/umlfri2
class FontDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.__font_db = QFontDatabase()

        self.setWindowTitle(_("Select font"))

        layout = QVBoxLayout()

        content_layout = QHBoxLayout()

        font_list_layout = QVBoxLayout()

        font_list_layout.addWidget(QLabel(_("Font family:")))

        self.__font_search = UpDownPassingLineEdit()
        self.__font_search.textEdited.connect(self.__family_search_edited)

        font_list_layout.addWidget(self.__font_search)

        self.__font_list = QListWidget()
        self.__font_list.setFocusPolicy(Qt.NoFocus)

        for font in sorted(self.__font_db.families()):
            if self.__font_db.isSmoothlyScalable(font):
                self.__font_list.addItem(font)

        self.__font_list.currentTextChanged.connect(self.__family_changed)

        self.__font_search.destination_widget = self.__font_list

        font_list_layout.addWidget(self.__font_list)

        content_layout.addLayout(font_list_layout)

        style_layout = QVBoxLayout()
        style_layout.setAlignment(Qt.AlignCenter)

        self.__bold_widget = self.__create_style_button(
            "B", _("Bold"), QKeySequence.Bold, FontStyle.bold)
        style_layout.addWidget(self.__bold_widget)

        self.__italic_widget = self.__create_style_button(
            "I", _("Italic"), QKeySequence.Italic, FontStyle.italic)
        style_layout.addWidget(self.__italic_widget)

        self.__underline_widget = self.__create_style_button(
            "U", _("Underline"), QKeySequence.Underline, FontStyle.underline)
        style_layout.addWidget(self.__underline_widget)

        self.__strike_widget = self.__create_style_button(
            "S", _("Strike Out"), STRIKE_OUT, FontStyle.strike)
        style_layout.addWidget(self.__strike_widget)

        content_layout.addLayout(style_layout)

        size_layout = QVBoxLayout()
        size_layout.setAlignment(Qt.AlignHCenter)

        size_layout.addWidget(QLabel(_("Size (px):")))

        self.__size_edit = UpDownPassingLineEdit()
        self.__size_edit.textEdited.connect(self.__size_edited)
        self.__size_edit.setValidator(QIntValidator(5, 60))
        size_layout.addWidget(self.__size_edit)
        self.__size_edit.setSizePolicy(
            QSizePolicy.Preferred,
            self.__size_edit.sizePolicy().verticalPolicy())

        self.__size_slider = QSlider(Qt.Vertical)
        self.__size_slider.setFocusPolicy(Qt.NoFocus)
        self.__size_slider.setRange(5, 60)
        self.__size_slider.setTickInterval(5)
        self.__size_slider.setTickPosition(QSlider.TicksRight)
        self.__size_slider.valueChanged.connect(self.__size_changed)

        self.__size_edit.destination_widget = self.__size_slider

        size_layout.addWidget(self.__size_slider)

        content_layout.addLayout(size_layout)

        layout.addLayout(content_layout)

        self.__example = QLineEdit("AaBbYyZz")
        self.__example.setFixedHeight(80)
        layout.addWidget(self.__example)

        button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                      | QDialogButtonBox.Cancel)
        button_box.button(QDialogButtonBox.Ok).setText(_("Ok"))
        button_box.button(QDialogButtonBox.Cancel).setText(_("Cancel"))
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)

        layout.addWidget(button_box)
        self.setLayout(layout)

        self.__font = Fonts.default

    def __create_style_button(self, text, tooltip, keys, font_style):
        def toggled(value):
            self.__font = self.__font.change(font_style, value)
            self.__refresh_example_font()

        shortcut = QKeySequence(keys)
        widget = QPushButton(text)
        widget.setShortcut(shortcut)
        widget.setToolTip("{0} ({1})".format(tooltip, shortcut.toString()))
        font = widget.font()
        if font_style == FontStyle.bold:
            font.setBold(True)
        elif font_style == FontStyle.italic:
            font.setItalic(True)
        elif font_style == FontStyle.underline:
            font.setUnderline(True)
        elif font_style == FontStyle.strike:
            font.setStrikeOut(True)
        widget.setFont(font)
        widget.setCheckable(True)
        widget.toggled.connect(toggled)
        return widget

    @property
    def ufl_font(self):
        return self.__font

    @ufl_font.setter
    def ufl_font(self, value):
        self.__font = value

        for item in self.__font_list.findItems(value.family, Qt.MatchExactly):
            self.__font_list.setCurrentItem(item)
            break
        else:
            self.__font_list.clearSelection()

        self.__font_search.setText(value.family)

        self.__bold_widget.setChecked(FontStyle.bold in value.style)
        self.__italic_widget.setChecked(FontStyle.italic in value.style)
        self.__underline_widget.setChecked(FontStyle.underline in value.style)
        self.__strike_widget.setChecked(FontStyle.strike in value.style)

        self.__size_slider.setValue(value.size)
        self.__size_edit.setText(str(value.size))

        self.__refresh_example_font()

    def __refresh_example_font(self):
        qfont = QFont(self.__font.family)
        qfont.setPixelSize(self.__font.size)
        qfont.setBold(FontStyle.bold in self.__font.style)
        qfont.setItalic(FontStyle.italic in self.__font.style)
        qfont.setStrikeOut(FontStyle.strike in self.__font.style)
        qfont.setUnderline(FontStyle.underline in self.__font.style)

        self.__example.setFont(qfont)

    def __family_search_edited(self, name):
        selected_text = self.__font_list.currentItem().text()
        if selected_text.startswith(name):
            return

        for item in self.__font_list.findItems(name, Qt.MatchStartsWith):
            self.__font_list.setCurrentItem(item)
            break

    def __family_changed(self, value):
        if self.__font.family != value:
            if not self.__font_search.hasFocus():
                self.__font_search.setText(value)
            self.__font = self.__font.change_family(value)
            self.__refresh_example_font()

    def __size_edited(self, value):
        if not value or int(value) < 5:
            return
        int_value = int(value)
        if self.__font.size != int_value:
            self.__font = self.__font.change_size(int_value)

            self.__size_slider.setValue(int_value)

            self.__refresh_example_font()

    def __size_changed(self, value):
        if self.__font.size != value:
            if self.__size_edit.text() != str(value):
                self.__size_edit.setText(str(value))
                self.__size_edit.selectAll()
            self.__font = self.__font.change_size(value)
            self.__refresh_example_font()
コード例 #15
0
ファイル: gui.py プロジェクト: PistaSaki/piannot
def _set_list_item_from_text(lst: qtw.QListWidget, text: str):
    lst.setCurrentItem(lst.findItems(text, Qt.MatchExactly)[0])
コード例 #16
0
class QSyllabusView(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        start_label = QLabel("Начало", self)
        self.start_edit = QDateEdit(calendarPopup=True)

        finish_label = QLabel("Конец", self)
        self.finish_edit = QDateEdit(calendarPopup=True)

        studyings_label = QLabel("Расписание", self)
        self.studyings_list = QListWidget(self)

        layout = QVBoxLayout()
        layout.addWidget(start_label)
        layout.addWidget(self.start_edit)
        layout.addWidget(finish_label)
        layout.addWidget(self.finish_edit)
        layout.addWidget(studyings_label)
        layout.addWidget(self.studyings_list)

        self.ok_btn = QPushButton('OK', parent=self)
        self.ok_btn.clicked.connect(self.ok)
        self.ok_cancel = QPushButton('Cancel', parent=self)
        self.ok_cancel.clicked.connect(self.cancel)

        btn_layout = QHBoxLayout()
        btn_layout.addWidget(self.ok_btn)
        btn_layout.addWidget(self.ok_cancel)

        layout.addLayout(btn_layout)
        layout.addStretch(0)

        self.setLayout(layout)

        self.presenter = SyllabusPresenter(self)

    def set_studying_items(self, items: str):
        for i in items:
            item = QListWidgetItem(i)
            item.setCheckState(Qt.Unchecked)
            self.studyings_list.addItem(item)

    def set_studyings(self, studyings):
        if not studyings:
            return
        for i in studyings:
            self.studyings_list.findItems(i, Qt.MatchFixedString)[0].setData(
                Qt.CheckStateRole, Qt.Checked)

    def get_studyings(self):
        return [
            self.studyings_list.item(i).text()
            for i in range(self.studyings_list.count())
            if self.studyings_list.item(i).checkState() == Qt.Checked
        ]

    def get_start(self):
        return self.start_edit.date().toPyDate()

    def set_start(self, value):
        if value is not None:
            self.start_edit.setDate(value)

    def get_finish(self):
        return self.finish_edit.date().toPyDate()

    def set_finish(self, value):
        if value is not None:
            self.finish_edit.setDate(value)

    def ok(self):
        self.presenter.ok()

    def cancel(self):
        self.presenter.cancel()

    def get_presenter(self):
        return self.presenter
コード例 #17
0
class QStudyingView(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.time_label = QLabel("Время начала", self)
        self.time_edit = QTimeEdit()

        self.date_label = QLabel("Дата", parent=self)
        self.date_edit = QDateEdit(calendarPopup=True)

        self.subjects_label = QLabel("Темы", self)
        self.subjects_list = QListWidget(self)

        layout = QVBoxLayout()
        layout.addWidget(self.time_label)
        layout.addWidget(self.time_edit)
        layout.addWidget(self.date_label)
        layout.addWidget(self.date_edit)
        layout.addWidget(self.subjects_label)
        layout.addWidget(self.subjects_list)

        self.ok_btn = QPushButton('OK', parent=self)
        self.ok_btn.clicked.connect(self.ok)
        self.ok_cancel = QPushButton('Cancel', parent=self)
        self.ok_cancel.clicked.connect(self.cancel)

        btn_layout = QHBoxLayout()
        btn_layout.addWidget(self.ok_btn)
        btn_layout.addWidget(self.ok_cancel)

        layout.addLayout(btn_layout)
        layout.addStretch(0)

        self.setLayout(layout)

        self.presenter = StudyingPresenter(self)

    def set_subject_items(self, items: str):
        for i in items:
            item = QListWidgetItem(i)
            item.setCheckState(Qt.Unchecked)
            self.subjects_list.addItem(item)

    def set_subjects(self, subjects):
        if not subjects:
            return
        for i in subjects:
            self.subjects_list.findItems(i, Qt.MatchFixedString)[0].setData(
                Qt.CheckStateRole, Qt.Checked)

    def get_subjects(self):
        return [
            self.subjects_list.item(i).text()
            for i in range(self.subjects_list.count())
            if self.subjects_list.item(i).checkState() == Qt.Checked
        ]

    def get_time(self):
        return self.time_edit.time().toPyTime()

    def set_time(self, value):
        if value is not None:
            self.time_edit.setTime(value)

    def get_date(self):
        return self.date_edit.date().toPyDate()

    def set_date(self, value):
        if value is not None:
            self.date_edit.setTime(value)

    def ok(self):
        self.presenter.ok()

    def cancel(self):
        self.presenter.cancel()

    def get_presenter(self):
        return self.presenter
コード例 #18
0
ファイル: simulation_gui.py プロジェクト: gansihxm/pymoskito
class SimulationGui(QMainWindow):
    """
    class for the graphical user interface
    """
    # TODO enable closing plot docks by right-clicking their name

    runSimulation = pyqtSignal()
    stopSimulation = pyqtSignal()
    playbackTimeChanged = pyqtSignal()
    regimeFinished = pyqtSignal()
    finishedRegimeBatch = pyqtSignal(bool)

    def __init__(self):
        # constructor of the base class
        QMainWindow.__init__(self)

        QCoreApplication.setOrganizationName("RST")
        QCoreApplication.setOrganizationDomain("https://tu-dresden.de/rst")
        QCoreApplication.setApplicationVersion(
            pkg_resources.require("PyMoskito")[0].version)
        QCoreApplication.setApplicationName(globals()["__package__"])

        # load settings
        self._settings = QSettings()
        self._read_settings()

        # initialize logger
        self._logger = logging.getLogger(self.__class__.__name__)

        # Create Simulation Backend
        self.guiProgress = None
        self.cmdProgress = None
        self.sim = SimulatorInteractor(self)
        self.runSimulation.connect(self.sim.run_simulation)
        self.stopSimulation.connect(self.sim.stop_simulation)
        self.sim.simulation_finalized.connect(self.new_simulation_data)
        self.currentDataset = None
        self.interpolator = None

        # sim setup viewer
        self.targetView = SimulatorView(self)
        self.targetView.setModel(self.sim.target_model)
        self.targetView.expanded.connect(self.target_view_changed)
        self.targetView.collapsed.connect(self.target_view_changed)

        # sim results viewer
        self.result_view = QTreeView()

        # the docking area allows to rearrange the user interface at runtime
        self.area = pg.dockarea.DockArea()

        # Window properties
        icon_size = QSize(25, 25)
        self.setCentralWidget(self.area)
        self.resize(1000, 700)
        self.setWindowTitle("PyMoskito")
        res_path = get_resource("mosquito.png")
        icon = QIcon(res_path)
        self.setWindowIcon(icon)

        # create docks
        self.propertyDock = pg.dockarea.Dock("Properties")
        self.animationDock = pg.dockarea.Dock("Animation")
        self.regimeDock = pg.dockarea.Dock("Regimes")
        self.dataDock = pg.dockarea.Dock("Data")
        self.logDock = pg.dockarea.Dock("Log")
        self.plotDockPlaceholder = pg.dockarea.Dock("Placeholder")

        # arrange docks
        self.area.addDock(self.animationDock, "right")
        self.area.addDock(self.regimeDock, "left", self.animationDock)
        self.area.addDock(self.propertyDock, "bottom", self.regimeDock)
        self.area.addDock(self.dataDock, "bottom", self.propertyDock)
        self.area.addDock(self.plotDockPlaceholder, "bottom",
                          self.animationDock)
        self.area.addDock(self.logDock, "bottom", self.dataDock)
        self.non_plotting_docks = list(self.area.findAll()[1].keys())

        # add widgets to the docks
        self.propertyDock.addWidget(self.targetView)

        if not vtk_available:
            self._logger.error(
                "loading vtk failed with:{}".format(vtk_error_msg))

        # check if there is a registered visualizer
        available_vis = get_registered_visualizers()
        self._logger.info("found visualizers: {}".format(
            [name for cls, name in available_vis]))
        if available_vis:
            # instantiate the first visualizer
            self._logger.info("loading visualizer '{}'".format(
                available_vis[0][1]))
            self.animationLayout = QVBoxLayout()

            if issubclass(available_vis[0][0], MplVisualizer):
                self.animationWidget = QWidget()
                self.visualizer = available_vis[0][0](self.animationWidget,
                                                      self.animationLayout)
                self.animationDock.addWidget(self.animationWidget)
            elif issubclass(available_vis[0][0], VtkVisualizer):
                if vtk_available:
                    # vtk window
                    self.animationFrame = QFrame()
                    self.vtkWidget = QVTKRenderWindowInteractor(
                        self.animationFrame)
                    self.animationLayout.addWidget(self.vtkWidget)
                    self.animationFrame.setLayout(self.animationLayout)
                    self.animationDock.addWidget(self.animationFrame)
                    self.vtk_renderer = vtkRenderer()
                    self.vtkWidget.GetRenderWindow().AddRenderer(
                        self.vtk_renderer)
                    self.visualizer = available_vis[0][0](self.vtk_renderer)
                    self.vtkWidget.Initialize()
                else:
                    self._logger.warning("visualizer depends on vtk which is "
                                         "not available on this system!")
            elif available_vis:
                raise NotImplementedError
        else:
            self.visualizer = None

        # regime window
        self.regime_list = QListWidget(self)
        self.regime_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.regimeDock.addWidget(self.regime_list)
        self.regime_list.itemDoubleClicked.connect(self.regime_dclicked)
        self._regimes = []
        self.regime_file_name = ""

        self.actDeleteRegimes = QAction(self.regime_list)
        self.actDeleteRegimes.setText("&Delete Selected Regimes")
        # TODO shortcut works always, not only with focus on the regime list
        # self.actDeleteRegimes.setShortcutContext(Qt.WindowShortcut)
        self.actDeleteRegimes.setShortcut(QKeySequence(Qt.Key_Delete))
        self.actDeleteRegimes.triggered.connect(self.remove_regime_items)

        self.actSave = QAction(self)
        self.actSave.setText('Save Results As')
        self.actSave.setIcon(QIcon(get_resource("save.png")))
        self.actSave.setDisabled(True)
        self.actSave.setShortcut(QKeySequence.Save)
        self.actSave.triggered.connect(self.export_simulation_data)

        self.actLoadRegimes = QAction(self)
        self.actLoadRegimes.setText("Load Regimes from File")
        self.actLoadRegimes.setIcon(QIcon(get_resource("load.png")))
        self.actLoadRegimes.setDisabled(False)
        self.actLoadRegimes.setShortcut(QKeySequence.Open)
        self.actLoadRegimes.triggered.connect(self.load_regime_dialog)

        self.actExitOnBatchCompletion = QAction(self)
        self.actExitOnBatchCompletion.setText("&Exit On Batch Completion")
        self.actExitOnBatchCompletion.setCheckable(True)
        self.actExitOnBatchCompletion.setChecked(
            self._settings.value("control/exit_on_batch_completion") == "True")
        self.actExitOnBatchCompletion.changed.connect(
            self.update_exit_on_batch_completion_setting)

        # regime management
        self.runningBatch = False
        self._current_regime_index = None
        self._current_regime_name = None
        self._regimes = []

        self.regimeFinished.connect(self.run_next_regime)
        self.finishedRegimeBatch.connect(self.regime_batch_finished)

        # data window
        self.dataList = QListWidget(self)
        self.dataDock.addWidget(self.dataList)
        self.dataList.itemDoubleClicked.connect(self.create_plot)

        # actions for simulation control
        self.actSimulateCurrent = QAction(self)
        self.actSimulateCurrent.setText("&Simulate Current Regime")
        self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png")))
        self.actSimulateCurrent.setShortcut(QKeySequence("F5"))
        self.actSimulateCurrent.triggered.connect(self.start_simulation)

        self.actSimulateAll = QAction(self)
        self.actSimulateAll.setText("Simulate &All Regimes")
        self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png")))
        self.actSimulateAll.setShortcut(QKeySequence("F6"))
        self.actSimulateAll.setDisabled(True)
        self.actSimulateAll.triggered.connect(self.start_regime_execution)

        # actions for animation control
        self.actAutoPlay = QAction(self)
        self.actAutoPlay.setText("&Autoplay Simulation")
        self.actAutoPlay.setCheckable(True)
        self.actAutoPlay.setChecked(
            self._settings.value("control/autoplay_animation") == "True")
        self.actAutoPlay.changed.connect(self.update_autoplay_setting)

        self.actPlayPause = QAction(self)
        self.actPlayPause.setText("Play Animation")
        self.actPlayPause.setIcon(QIcon(get_resource("play.png")))
        self.actPlayPause.setDisabled(True)
        self.actPlayPause.setShortcut(QKeySequence(Qt.Key_Space))
        self.actPlayPause.triggered.connect(self.play_animation)

        self.actStop = QAction(self)
        self.actStop.setText("Stop")
        self.actStop.setIcon(QIcon(get_resource("stop.png")))
        self.actStop.setDisabled(True)
        self.actStop.triggered.connect(self.stop_animation)

        self.actSlow = QAction(self)
        self.actSlow.setText("Slowest")
        self.actSlow.setIcon(QIcon(get_resource("slow.png")))
        self.actSlow.setDisabled(False)
        self.actSlow.triggered.connect(self.set_slowest_playback_speed)

        self.actFast = QAction(self)
        self.actFast.setText("Fastest")
        self.actFast.setIcon(QIcon(get_resource("fast.png")))
        self.actFast.setDisabled(False)
        self.actFast.triggered.connect(self.set_fastest_playback_speed)

        self.speedControl = QSlider(Qt.Horizontal, self)
        self.speedControl.setMaximumSize(200, 25)
        self.speedControl.setTickPosition(QSlider.TicksBothSides)
        self.speedControl.setDisabled(False)
        self.speedControl.setMinimum(0)
        self.speedControl.setMaximum(12)
        self.speedControl.setValue(6)
        self.speedControl.setTickInterval(6)
        self.speedControl.setSingleStep(2)
        self.speedControl.setPageStep(3)
        self.speedControl.valueChanged.connect(self.update_playback_speed)

        self.timeSlider = QSlider(Qt.Horizontal, self)
        self.timeSlider.setMinimum(0)
        self.timeSliderRange = 1000
        self.timeSlider.setMaximum(self.timeSliderRange)
        self.timeSlider.setTickInterval(1)
        self.timeSlider.setTracking(True)
        self.timeSlider.setDisabled(True)
        self.timeSlider.valueChanged.connect(self.update_playback_time)

        self.playbackTime = .0
        self.playbackGain = 1
        self.currentStepSize = .0
        self.currentEndTime = .0
        self.playbackTimer = QTimer()
        self.playbackTimer.timeout.connect(self.increment_playback_time)
        self.playbackTimeChanged.connect(self.update_gui)
        self.playbackTimeout = 33  # in [ms] -> 30 fps

        self.actResetCamera = QAction(self)
        self.actResetCamera.setText("Reset Camera")
        self.actResetCamera.setIcon(QIcon(get_resource("reset_camera.png")))
        self.actResetCamera.setDisabled(True)
        if available_vis:
            self.actResetCamera.setEnabled(self.visualizer.can_reset_view)
        self.actResetCamera.triggered.connect(self.reset_camera_clicked)

        # postprocessing
        self.actPostprocessing = QAction(self)
        self.actPostprocessing.setText("Launch Postprocessor")
        self.actPostprocessing.setIcon(QIcon(get_resource("processing.png")))
        self.actPostprocessing.setDisabled(False)
        self.actPostprocessing.triggered.connect(self.postprocessing_clicked)
        self.actPostprocessing.setShortcut(QKeySequence("F7"))

        self.postprocessor = None

        # toolbar
        self.toolbarSim = QToolBar("Simulation")
        self.toolbarSim.setContextMenuPolicy(Qt.PreventContextMenu)
        self.toolbarSim.setMovable(False)
        self.toolbarSim.setIconSize(icon_size)
        self.addToolBar(self.toolbarSim)
        self.toolbarSim.addAction(self.actLoadRegimes)
        self.toolbarSim.addAction(self.actSave)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actSimulateCurrent)
        self.toolbarSim.addAction(self.actSimulateAll)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actPlayPause)
        self.toolbarSim.addAction(self.actStop)
        self.toolbarSim.addWidget(self.timeSlider)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actSlow)
        self.toolbarSim.addWidget(self.speedControl)
        self.toolbarSim.addAction(self.actFast)
        self.toolbarSim.addSeparator()
        self.toolbarSim.addAction(self.actPostprocessing)
        self.toolbarSim.addAction(self.actResetCamera)
        self.postprocessor = None

        # log dock
        self.logBox = QPlainTextEdit(self)
        self.logBox.setReadOnly(True)
        self.logDock.addWidget(self.logBox)

        # init logger for logging box
        self.textLogger = PlainTextLogger(logging.INFO)
        self.textLogger.set_target_cb(self.logBox.appendPlainText)
        logging.getLogger().addHandler(self.textLogger)

        # menu bar
        fileMenu = self.menuBar().addMenu("&File")
        fileMenu.addAction(self.actLoadRegimes)
        fileMenu.addAction(self.actSave)
        fileMenu.addAction("&Quit", self.close)

        editMenu = self.menuBar().addMenu("&Edit")
        editMenu.addAction(self.actDeleteRegimes)

        simMenu = self.menuBar().addMenu("&Simulation")
        simMenu.addAction(self.actSimulateCurrent)
        simMenu.addAction(self.actSimulateAll)
        simMenu.addAction(self.actExitOnBatchCompletion)
        simMenu.addAction(self.actPostprocessing)

        animMenu = self.menuBar().addMenu("&Animation")
        animMenu.addAction(self.actPlayPause)
        animMenu.addAction("&Increase Playback Speed",
                           self.increment_playback_speed,
                           QKeySequence(Qt.CTRL + Qt.Key_Plus))
        animMenu.addAction("&Decrease Playback Speed",
                           self.decrement_playback_speed,
                           QKeySequence(Qt.CTRL + Qt.Key_Minus))
        animMenu.addAction("&Reset Playback Speed", self.reset_playback_speed,
                           QKeySequence(Qt.CTRL + Qt.Key_0))
        animMenu.addAction(self.actAutoPlay)
        animMenu.addAction(self.actResetCamera)

        helpMenu = self.menuBar().addMenu("&Help")
        helpMenu.addAction("&Online Documentation", self.show_online_docs)
        helpMenu.addAction("&About", self.show_info)

        # status bar
        self.status = QStatusBar(self)
        self.setStatusBar(self.status)
        self.statusLabel = QLabel("Ready.")
        self.statusBar().addPermanentWidget(self.statusLabel)
        self.timeLabel = QLabel("current time: 0.0")
        self.statusBar().addPermanentWidget(self.timeLabel)

        self._logger.info("Simulation GUI is up and running.")

    def _read_settings(self):

        # add default settings if none are present
        if not self._settings.contains("path/simulation_results"):
            self._settings.setValue(
                "path/simulation_results",
                os.path.join(os.path.curdir, "results", "simulation"))
        if not self._settings.contains("path/postprocessing_results"):
            self._settings.setValue(
                "path/postprocessing_results",
                os.path.join(os.path.curdir, "results", "postprocessing"))
        if not self._settings.contains("path/metaprocessing_results"):
            self._settings.setValue(
                "path/metaprocessing_results",
                os.path.join(os.path.curdir, "results", "metaprocessing"))

        if not self._settings.contains("control/autoplay_animation"):
            self._settings.setValue("control/autoplay_animation", "False")

        if not self._settings.contains("control/exit_on_batch_completion"):
            self._settings.setValue("control/exit_on_batch_completion",
                                    "False")

    def _write_settings(self):
        """ Store the application state. """
        pass

    @pyqtSlot()
    def update_autoplay_setting(self):
        self._settings.setValue("control/autoplay_animation",
                                str(self.actAutoPlay.isChecked()))

    @pyqtSlot()
    def update_exit_on_batch_completion_setting(self, state=None):
        if state is None:
            state = self.actExitOnBatchCompletion.isChecked()
        self._settings.setValue("control/exit_on_batch_completion", str(state))

    def set_visualizer(self, vis):
        self.visualizer = vis
        self.vtkWidget.Initialize()

    @pyqtSlot()
    def play_animation(self):
        """
        play the animation
        """
        self._logger.debug("Starting Playback")

        # if we are at the end, start from the beginning
        if self.playbackTime == self.currentEndTime:
            self.timeSlider.setValue(0)

        self.actPlayPause.setText("Pause Animation")
        self.actPlayPause.setIcon(QIcon(get_resource("pause.png")))
        self.actPlayPause.triggered.disconnect(self.play_animation)
        self.actPlayPause.triggered.connect(self.pause_animation)
        self.playbackTimer.start(self.playbackTimeout)

    @pyqtSlot()
    def pause_animation(self):
        """
        pause the animation
        """
        self._logger.debug("Pausing Playback")
        self.playbackTimer.stop()
        self.actPlayPause.setText("Play Animation")
        self.actPlayPause.setIcon(QIcon(get_resource("play.png")))
        self.actPlayPause.triggered.disconnect(self.pause_animation)
        self.actPlayPause.triggered.connect(self.play_animation)

    def stop_animation(self):
        """
        Stop the animation if it is running and reset the playback time.
        """
        self._logger.debug("Stopping Playback")
        if self.actPlayPause.text() == "Pause Animation":
            # animation is playing -> stop it
            self.playbackTimer.stop()
            self.actPlayPause.setText("Play Animation")
            self.actPlayPause.setIcon(QIcon(get_resource("play.png")))
            self.actPlayPause.triggered.disconnect(self.pause_animation)
            self.actPlayPause.triggered.connect(self.play_animation)

        self.timeSlider.setValue(0)

    @pyqtSlot()
    def start_simulation(self):
        """
        start the simulation and disable start button
        """
        if self._current_regime_index is None:
            regime_name = ""
        else:
            regime_name = str(
                self.regime_list.item(self._current_regime_index).text())

        self.statusLabel.setText("simulating {}".format(regime_name))
        self._logger.info("Simulating: {}".format(regime_name))

        self.actSimulateCurrent.setIcon(
            QIcon(get_resource("stop_simulation.png")))
        self.actSimulateCurrent.setText("Abort &Simulation")
        self.actSimulateCurrent.triggered.disconnect(self.start_simulation)
        self.actSimulateCurrent.triggered.connect(self.stop_simulation)

        if not self.runningBatch:
            self.actSimulateAll.setDisabled(True)

        self.guiProgress = QProgressBar(self)
        self.sim.simulationProgressChanged.connect(self.guiProgress.setValue)
        self.statusBar().addWidget(self.guiProgress)
        self.runSimulation.emit()

    @pyqtSlot()
    def stop_simulation(self):
        self.stopSimulation.emit()

    def export_simulation_data(self, ok):
        """
        Query the user for a custom name and export the current simulation
        results.

        :param ok: unused parameter from QAction.triggered() Signal
        """
        self._save_data()

    def _save_data(self, file_path=None):
        """
        Save the current simulation results.

        If *fie_name* is given, the result will be saved to the specified
        location, making automated exporting easier.

        Args:
            file_path(str): Absolute path of the target file. If `None` the
                use will be asked for a storage location.
        """
        regime_name = self._regimes[self._current_regime_index]["Name"]

        if file_path is None:
            # get default path
            path = self._settings.value("path/simulation_results")

            # create canonic file name
            suggestion = self._simfile_name(regime_name)
        else:
            path = os.path.dirname(file_path)
            suggestion = os.path.basename(file_path)

        # check if path exists otherwise create it
        if not os.path.isdir(path):
            box = QMessageBox()
            box.setText("Export Folder does not exist yet.")
            box.setInformativeText("Do you want to create it? \n"
                                   "{}".format(os.path.abspath(path)))
            box.setStandardButtons(QMessageBox.Ok | QMessageBox.No)
            box.setDefaultButton(QMessageBox.Ok)
            ret = box.exec_()
            if ret == QMessageBox.Ok:
                os.makedirs(path)
            else:
                path = os.path.abspath(os.path.curdir)
                file_path = None

        # If no path was given, present the default and let the user choose
        if file_path is None:
            dialog = QFileDialog(self)
            dialog.setAcceptMode(QFileDialog.AcceptSave)
            dialog.setFileMode(QFileDialog.AnyFile)
            dialog.setDirectory(path)
            dialog.setNameFilter("PyMoskito Results (*.pmr)")
            dialog.selectFile(suggestion)

            if dialog.exec_():
                file_path = dialog.selectedFiles()[0]
            else:
                self._logger.warning("Export Aborted")
                return -1

        # ask whether this should act as new default
        path = os.path.abspath(os.path.dirname(file_path))
        if path != self._settings.value("path/simulation_results"):
            box = QMessageBox()
            box.setText("Use this path as new default?")
            box.setInformativeText("{}".format(path))
            box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            box.setDefaultButton(QMessageBox.Yes)
            ret = box.exec_()
            if ret == QMessageBox.Yes:
                self._settings.setValue("path/simulation_results", path)

        self.currentDataset.update({"regime name": regime_name})
        with open(file_path, "wb") as f:
            pickle.dump(self.currentDataset, f, protocol=4)

        self.statusLabel.setText("results saved to {}".format(file_path))
        self._logger.info("results saved to {}".format(file_path))

    def _simfile_name(self, regime_name):
        """ Create a canonical name for a simulation result file
        """
        suggestion = (time.strftime("%Y%m%d-%H%M%S") + "_" + regime_name +
                      ".pmr")
        return suggestion

    def load_regime_dialog(self):
        regime_path = os.path.join(os.curdir)

        dialog = QFileDialog(self)
        dialog.setFileMode(QFileDialog.ExistingFile)
        dialog.setDirectory(regime_path)
        dialog.setNameFilter("Simulation Regime files (*.sreg)")

        if dialog.exec_():
            file = dialog.selectedFiles()[0]
            self.load_regimes_from_file(file)

    def load_regimes_from_file(self, file_name):
        """
        load simulation regime from file
        :param file_name:
        """
        self.regime_file_name = os.path.split(file_name)[-1][:-5]
        self._logger.info("loading regime file: {0}".format(
            self.regime_file_name))
        with open(file_name.encode(), "r") as f:
            self._regimes += yaml.load(f)

        self._update_regime_list()

        if self._regimes:
            self.actSimulateAll.setDisabled(False)

        self._logger.info("loaded {} regimes".format(len(self._regimes)))
        self.statusBar().showMessage(
            "loaded {} regimes.".format(len(self._regimes)), 1000)
        return

    def _update_regime_list(self):
        self.regime_list.clear()
        for reg in self._regimes:
            self._logger.debug("adding '{}' to regime list".format(
                reg["Name"]))
            self.regime_list.addItem(reg["Name"])

    def remove_regime_items(self):
        if self.regime_list.currentRow() >= 0:
            # flag all selected files as invalid
            items = self.regime_list.selectedItems()
            for item in items:
                del self._regimes[self.regime_list.row(item)]
                self.regime_list.takeItem(self.regime_list.row(item))

    @pyqtSlot(QListWidgetItem)
    def regime_dclicked(self, item):
        """
        Apply the selected regime to the current target.
        """
        self.apply_regime_by_name(str(item.text()))

    def apply_regime_by_name(self, regime_name):
        """
        Apply the regime given by `regime_name` und update the regime index.

        Returns:
            bool: `True` if successful, `False` if errors occurred.
        """
        # get regime idx
        try:
            idx = list(map(itemgetter("Name"),
                           self._regimes)).index(regime_name)
        except ValueError as e:
            self._logger.error(
                "apply_regime_by_name(): Error no regime called "
                "'{0}'".format(regime_name))
            return False

        # apply
        return self._apply_regime_by_idx(idx)

    def _apply_regime_by_idx(self, index=0):
        """
        Apply the given regime.

        Args:
            index(int): Index of the regime in the `RegimeList` .

        Returns:
            bool: `True` if successful, `False` if errors occurred.
        """
        if index >= len(self._regimes):
            self._logger.error("applyRegime: index error! ({})".format(index))
            return False

        reg_name = self._regimes[index]["Name"]
        self.statusBar().showMessage("regime {} applied.".format(reg_name),
                                     1000)
        self._logger.info("applying regime '{}'".format(reg_name))

        self._current_regime_index = index
        self._current_regime_name = reg_name

        return self.sim.set_regime(self._regimes[index])

    @pyqtSlot()
    def start_regime_execution(self):
        """
        Simulate all regimes in the regime list.
        """
        self.actSimulateAll.setText("Stop Simulating &All Regimes")
        self.actSimulateAll.setIcon(QIcon(get_resource("stop_batch.png")))
        self.actSimulateAll.triggered.disconnect(self.start_regime_execution)
        self.actSimulateAll.triggered.connect(self.stop_regime_excecution)

        self.runningBatch = True
        self._current_regime_index = -1
        self.regimeFinished.emit()

    def run_next_regime(self):
        """
        Execute the next regime in the regime batch.
        """
        # are we finished?
        if self._current_regime_index == len(self._regimes) - 1:
            self.finishedRegimeBatch.emit(True)
            return

        suc = self._apply_regime_by_idx(self._current_regime_index + 1)
        if not suc:
            self.finishedRegimeBatch.emit(False)
            return

        self.start_simulation()

    @pyqtSlot()
    def stop_regime_excecution(self):
        """ Stop the batch process.
        """
        self.stopSimulation.emit()
        self.finishedRegimeBatch.emit(False)

    def regime_batch_finished(self, status):
        self.runningBatch = False
        self.actSimulateAll.setDisabled(False)
        self.actSave.setDisabled(True)

        self.actSimulateAll.setText("Simulate &All Regimes")
        self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png")))
        self.actSimulateAll.triggered.disconnect(self.stop_regime_excecution)
        self.actSimulateAll.triggered.connect(self.start_regime_execution)

        if status:
            self.statusLabel.setText("All regimes have been simulated")
            self._logger.info("All Regimes have been simulated")
        else:
            self._logger.error("Batch simulation has been aborted")

        if self._settings.value("control/exit_on_batch_completion") == "True":
            self._logger.info("Shutting down SimulationGUI")
            self.close()

    @pyqtSlot(str, dict, name="new_simulation_data")
    def new_simulation_data(self, status, data):
        """
        Slot to be called when the simulation interface has completed the
        current job and new data is available.

        Args:
            status (str): Status of the simulation, either
                - `finished` : Simulation has been finished successfully or
                - `failed` : Simulation has failed.
            data (dict): Dictionary, holding the simulation data.
        """
        self._logger.info("Simulation {}".format(status))
        self.statusLabel.setText("Simulation {}".format(status))

        self.actSimulateCurrent.setText("&Simulate Current Regime")
        self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png")))
        self.actSimulateCurrent.triggered.disconnect(self.stop_simulation)
        self.actSimulateCurrent.triggered.connect(self.start_simulation)

        self.actPlayPause.setDisabled(False)
        self.actStop.setDisabled(False)
        self.actSave.setDisabled(False)
        self.speedControl.setDisabled(False)
        self.timeSlider.setDisabled(False)

        self.sim.simulationProgressChanged.disconnect(
            self.guiProgress.setValue)
        self.statusBar().removeWidget(self.guiProgress)

        self.stop_animation()

        self.currentDataset = data
        if data:
            self._read_results()
            self._update_data_list()
            self._update_plots()

        if self._settings.value("control/autoplay_animation") == "True":
            self.actPlayPause.trigger()

        if self.runningBatch:
            regime_name = self._regimes[self._current_regime_index]["Name"]
            file_name = self._simfile_name(regime_name)
            self._save_data(
                os.path.join(self._settings.value("path/simulation_results"),
                             file_name))
            self.regimeFinished.emit()
        else:
            self.actSimulateAll.setDisabled(False)

    def _read_results(self):
        state = self.currentDataset["results"]["Solver"]
        self.interpolator = interp1d(self.currentDataset["results"]["time"],
                                     state,
                                     axis=0,
                                     bounds_error=False,
                                     fill_value=(state[0], state[-1]))
        self.currentStepSize = 1.0 / self.currentDataset["simulation"][
            "measure rate"]
        self.currentEndTime = self.currentDataset["simulation"]["end time"]
        self.validData = True

    def increment_playback_speed(self):
        self.speedControl.setValue(self.speedControl.value() +
                                   self.speedControl.singleStep())

    def decrement_playback_speed(self):
        self.speedControl.setValue(self.speedControl.value() -
                                   self.speedControl.singleStep())

    def reset_playback_speed(self):
        self.speedControl.setValue(
            (self.speedControl.maximum() - self.speedControl.minimum()) / 2)

    def set_slowest_playback_speed(self):
        self.speedControl.setValue(self.speedControl.minimum())

    def set_fastest_playback_speed(self):
        self.speedControl.setValue(self.speedControl.maximum())

    def update_playback_speed(self, val):
        """
        adjust playback time to slider value

        :param val:
        """
        maximum = self.speedControl.maximum()
        self.playbackGain = 10**(3.0 * (val - maximum / 2) / maximum)

    @pyqtSlot()
    def increment_playback_time(self):
        """
        go one time step forward in playback
        """
        if self.playbackTime == self.currentEndTime:
            self.pause_animation()
            return

        increment = self.playbackGain * self.playbackTimeout / 1000
        self.playbackTime = min(self.currentEndTime,
                                self.playbackTime + increment)
        pos = int(self.playbackTime / self.currentEndTime *
                  self.timeSliderRange)
        self.timeSlider.blockSignals(True)
        self.timeSlider.setValue(pos)
        self.timeSlider.blockSignals(False)
        self.playbackTimeChanged.emit()

    def update_playback_time(self):
        """
        adjust playback time to slider value
        """
        self.playbackTime = self.timeSlider.value(
        ) / self.timeSliderRange * self.currentEndTime
        self.playbackTimeChanged.emit()
        return

    def update_gui(self):
        """
        updates the graphical user interface, including:
            - timestamp
            - visualisation
            - time cursor in diagrams
        """
        if not self.validData:
            return

        self.timeLabel.setText("current time: %4f" % self.playbackTime)

        # update time cursor in plots
        self._update_time_cursors()

        # update state of rendering
        if self.visualizer:
            state = self.interpolator(self.playbackTime)
            self.visualizer.update_scene(state)
            if isinstance(self.visualizer, MplVisualizer):
                pass
            elif isinstance(self.visualizer, VtkVisualizer):
                self.vtkWidget.GetRenderWindow().Render()

    def _update_data_list(self):
        self.dataList.clear()
        for module_name, results in self.currentDataset["results"].items():
            if not isinstance(results, np.ndarray):
                continue
            if len(results.shape) == 1:
                self.dataList.insertItem(0, module_name)
            elif len(results.shape) == 2:
                for col in range(results.shape[1]):
                    self.dataList.insertItem(
                        0, self._build_entry_name(module_name, (col, )))
            elif len(results.shape) == 3:
                for col in range(results.shape[1]):
                    for der in range(results.shape[2]):
                        self.dataList.insertItem(
                            0, self._build_entry_name(module_name, (col, der)))

    def _build_entry_name(self, module_name, idx):
        """
        Construct an identifier for a given entry of a module.
        Args:
            module_name (str): name of the module the entry belongs to.
            idx (tuple): Index of the entry.

        Returns:
            str: Identifier to use for display.
        """
        # save the user from defining 1d entries via tuples
        if len(idx) == 1:
            m_idx = idx[0]
        else:
            m_idx = idx

        mod_settings = self.currentDataset["modules"]
        info = mod_settings.get(module_name, {}).get("output_info", None)
        if info:
            if m_idx in info:
                return ".".join([module_name, info[m_idx]["Name"]])

        return ".".join([module_name] + [str(i) for i in idx])

    def _get_index_from_suffix(self, module_name, suffix):
        info = self.currentDataset["modules"].get(module_name,
                                                  {}).get("output_info", None)
        idx = next((i for i in info if info[i]["Name"] == suffix), None)
        return idx

    def _get_units(self, entry):
        """
        Return the unit that corresponds to a given entry.

        If no information is available, None is returned.

        Args:
            entry (str): Name of the entry. This can either be "Model.a.b" where
                a and b are numbers or if information is available "Model.Signal"
                where signal is the name of that part.

        Returns:

        """
        args = entry.split(".")
        module_name = args.pop(0)
        info = self.currentDataset["modules"].get(module_name,
                                                  {}).get("output_info", None)
        if info is None:
            return None

        if len(args) == 1:
            try:
                idx = int(args[0])
            except ValueError:
                idx = next((i for i in info if info[i]["Name"] == args[0]),
                           None)
        else:
            idx = (int(a) for a in args)

        return info[idx]["Unit"]

    def create_plot(self, item):
        """
        Creates a plot widget based on the given item.

        If a plot for this item is already open no new plot is created but the
        existing one is raised up again.

        Args:
            item(Qt.ListItem): Item to plot.
        """
        title = str(item.text())
        if title in self.non_plotting_docks:
            self._logger.error("Title '{}' not allowed for a plot window since"
                               "it would shadow on of the reserved "
                               "names".format(title))

        # check if plot has already been opened
        if title in self.area.findAll()[1]:
            self.area.docks[title].raiseDock()
            return

        # collect data
        data = self._get_data_by_name(title)
        t = self.currentDataset["results"]["time"]
        unit = self._get_units(title)
        if "." in title:
            name = title.split(".")[1]
        else:
            name = title

        # create plot widget
        widget = pg.PlotWidget(title=title)
        widget.showGrid(True, True)
        widget.plot(x=t, y=data)
        widget.getPlotItem().getAxis("bottom").setLabel(text="Time", units="s")
        widget.getPlotItem().getAxis("left").setLabel(text=name, units=unit)

        # add a time line
        time_line = pg.InfiniteLine(self.playbackTime,
                                    angle=90,
                                    movable=False,
                                    pen=pg.mkPen("#FF0000", width=2.0))
        widget.getPlotItem().addItem(time_line)

        # create dock container and add it to dock area
        dock = pg.dockarea.Dock(title, closable=True)
        dock.addWidget(widget)
        self.area.addDock(dock, "above", self.plotDockPlaceholder)

    def _get_data_by_name(self, name):
        tmp = name.split(".")
        module_name = tmp[0]
        if len(tmp) == 1:
            data = np.array(self.currentDataset["results"][module_name])
        elif len(tmp) == 2:
            try:
                idx = int(tmp[1])
            except ValueError:
                idx = self._get_index_from_suffix(module_name, tmp[1])
            finally:
                data = self.currentDataset["results"][module_name][..., idx]
        elif len(tmp) == 3:
            idx = int(tmp[1])
            der = int(tmp[2])
            data = self.currentDataset["results"][module_name][..., idx, der]
        else:
            raise ValueError("Format not supported")

        return data

    def _update_time_cursors(self):
        """
        Update the time lines of all plot windows
        """
        for title, dock in self.area.findAll()[1].items():
            if title in self.non_plotting_docks:
                continue
            for widget in dock.widgets:
                for item in widget.getPlotItem().items:
                    if isinstance(item, pg.InfiniteLine):
                        item.setValue(self.playbackTime)

    def _update_plots(self):
        """
        Update the data in all plot windows
        """
        for title, dock in self.area.findAll()[1].items():
            if title in self.non_plotting_docks:
                continue

            if not self.dataList.findItems(dock.name(), Qt.MatchExactly):
                # no data for this plot -> remove it
                dock.close()
                continue

            for widget in dock.widgets:
                for item in widget.getPlotItem().items:
                    if isinstance(item, pg.PlotDataItem):
                        x_data = self.currentDataset["results"]["time"]
                        y_data = self._get_data_by_name(dock.name())
                        item.setData(x=x_data, y=y_data)

    @pyqtSlot(QModelIndex)
    def target_view_changed(self, index):
        self.targetView.resizeColumnToContents(0)

    def postprocessing_clicked(self):
        """
        starts the post- and metaprocessing application
        """
        self._logger.info("launching postprocessor")
        self.statusBar().showMessage("launching postprocessor", 1000)
        if self.postprocessor is None:
            self.postprocessor = PostProcessor()

        self.postprocessor.show()

    def reset_camera_clicked(self):
        """
        reset camera in vtk window
        """
        self.visualizer.reset_camera()
        self.vtkWidget.GetRenderWindow().Render()

    def show_info(self):
        icon_lic = open(get_resource("license.txt"), "r").read()
        text = "This application was build using PyMoskito ver. {} .<br />" \
               "PyMoskito is free software distributed under GPLv3. <br />" \
               "It is developed by members of the " \
               "<a href=\'https://tu-dresden.de/ing/elektrotechnik/rst'>" \
               "Institute of Control Theory</a>" \
               " at the <a href=\'https://tu-dresden.de'>" \
               "Dresden University of Technology</a>. <br />" \
               "".format(pkg_resources.require("PyMoskito")[0].version) \
               + "<br />" + icon_lic
        box = QMessageBox.about(self, "PyMoskito", text)

    def show_online_docs(self):
        webbrowser.open("https://pymoskito.readthedocs.org")

    def closeEvent(self, QCloseEvent):
        self._logger.info("Close Event received, shutting down.")
        logging.getLogger().removeHandler(self.textLogger)
        super().closeEvent(QCloseEvent)
コード例 #19
0
class EditorGeneral(QWidget):
    """EditorGeneral widget class."""

    def __init__(self, parent):
        super(EditorGeneral, self).__init__()
        self._preferences, vbox = parent, QVBoxLayout(self)
        self.original_style = copy.copy(resources.CUSTOM_SCHEME)
        self.current_scheme, self._modified_editors = 'default', []
        self._font = settings.FONT

        groupBoxMini = QGroupBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_MINIMAP)
        groupBoxTypo = QGroupBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_TYPOGRAPHY)
        groupBoxScheme = QGroupBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_SCHEME)

        #Minimap
        formMini = QGridLayout(groupBoxMini)
        formMini.setContentsMargins(5, 15, 5, 5)
        self._checkShowMinimap = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_ENABLE_MINIMAP)
        self._spinMaxOpacity = QSpinBox()
        self._spinMaxOpacity.setRange(0, 100)
        self._spinMaxOpacity.setSuffix("% Max.")
        self._spinMinOpacity = QSpinBox()
        self._spinMinOpacity.setRange(0, 100)
        self._spinMinOpacity.setSuffix("% Min.")
        self._spinSize = QSpinBox()
        self._spinSize.setMaximum(100)
        self._spinSize.setMinimum(0)
        self._spinSize.setSuffix(
            translations.TR_PREFERENCES_EDITOR_GENERAL_AREA_MINIMAP)
        formMini.addWidget(self._checkShowMinimap, 0, 1)
        formMini.addWidget(QLabel(
            translations.TR_PREFERENCES_EDITOR_GENERAL_SIZE_MINIMAP), 1, 0,
            Qt.AlignRight)
        formMini.addWidget(self._spinSize, 1, 1)
        formMini.addWidget(QLabel(
            translations.TR_PREFERENCES_EDITOR_GENERAL_OPACITY), 2, 0,
            Qt.AlignRight)
        formMini.addWidget(self._spinMinOpacity, 2, 1)
        formMini.addWidget(self._spinMaxOpacity, 2, 2)
        #Typo
        gridTypo = QGridLayout(groupBoxTypo)
        gridTypo.setContentsMargins(5, 15, 5, 5)
        self._btnEditorFont = QPushButton('')
        gridTypo.addWidget(QLabel(
            translations.TR_PREFERENCES_EDITOR_GENERAL_EDITOR_FONT), 0, 0,
            Qt.AlignRight)
        gridTypo.addWidget(self._btnEditorFont, 0, 1)
        #Scheme
        vboxScheme = QVBoxLayout(groupBoxScheme)
        vboxScheme.setContentsMargins(5, 15, 5, 5)
        self._listScheme = QListWidget()
        vboxScheme.addWidget(self._listScheme)
        hbox = QHBoxLayout()
        btnDownload = QPushButton(
            translations.TR_PREFERENCES_EDITOR_DOWNLOAD_SCHEME)
        btnDownload.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        btnDownload.clicked['bool'].connect(self._open_schemes_manager)
        hbox.addWidget(btnDownload)
        btnAdd = QPushButton(QIcon(":img/add"),
                             translations.TR_EDITOR_CREATE_SCHEME)
        btnAdd.setIconSize(QSize(16, 16))
        btnAdd.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        btnAdd.clicked['bool'].connect(self._open_schemes_designer)
        btnRemove = QPushButton(QIcon(":img/delete"),
                                translations.TR_EDITOR_REMOVE_SCHEME)
        btnRemove.setIconSize(QSize(16, 16))
        btnRemove.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        btnRemove.clicked['bool'].connect(self._remove_scheme)
        hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding))
        hbox.addWidget(btnAdd)
        hbox.addWidget(btnRemove)
        vboxScheme.addLayout(hbox)

        vbox.addWidget(groupBoxMini)
        vbox.addWidget(groupBoxTypo)
        vbox.addWidget(groupBoxScheme)

        #Settings
        qsettings = IDE.ninja_settings()
        qsettings.beginGroup('preferences')
        qsettings.beginGroup('editor')
        self._checkShowMinimap.setChecked(settings.SHOW_MINIMAP)
        if settings.IS_MAC_OS:
            self._spinMinOpacity.setValue(100)
            self._spinMaxOpacity.setValue(100)
            self._spinMinOpacity.setDisabled(True)
            self._spinMaxOpacity.setDisabled(True)
        else:
            self._spinMinOpacity.setValue(settings.MINIMAP_MIN_OPACITY * 100)
            self._spinMaxOpacity.setValue(settings.MINIMAP_MAX_OPACITY * 100)
        self._spinSize.setValue(settings.SIZE_PROPORTION * 100)
        btnText = ', '.join(self._font.toString().split(',')[0:2])
        self._btnEditorFont.setText(btnText)
        self._listScheme.clear()
        self._listScheme.addItem('default')
        self._schemes = json_manager.load_editor_skins()
        for item in self._schemes:
            self._listScheme.addItem(item)
        items = self._listScheme.findItems(
            qsettings.value('scheme', defaultValue='',
                            type='QString'), Qt.MatchExactly)
        if items:
            self._listScheme.setCurrentItem(items[0])
        else:
            self._listScheme.setCurrentRow(0)
        qsettings.endGroup()
        qsettings.endGroup()

        #Signals
        self._btnEditorFont.clicked['bool'].connect(self._load_editor_font)
        self._listScheme.itemSelectionChanged.connect(self._preview_style)
        self._preferences.savePreferences.connect(self.save)

    def _open_schemes_manager(self):
        ninjaide = IDE.getInstance()
        ninjaide.show_schemes()
        # refresh schemes

    def _open_schemes_designer(self):
        name = self._listScheme.currentItem().text()
        scheme = self._schemes.get(name, resources.COLOR_SCHEME)
        designer = preferences_editor_scheme_designer.EditorSchemeDesigner(
            scheme, self)
        designer.exec_()
        if designer.saved:
            scheme_name = designer.line_name.text()
            scheme = designer.original_style
            self._schemes[scheme_name] = scheme
            result = self._listScheme.findItems(scheme_name, Qt.MatchExactly)
            if not result:
                self._listScheme.addItem(scheme_name)

    def _remove_scheme(self):
        name = self._listScheme.currentItem().text()
        fileName = ('{0}.color'.format(
            file_manager.create_path(resources.EDITOR_SKINS, name)))
        file_manager.delete_file(fileName)
        item = self._listScheme.takeItem(self._listScheme.currentRow())
        del item

    def hideEvent(self, event):
        super(EditorGeneral, self).hideEvent(event)
        resources.CUSTOM_SCHEME = self.original_style
        for editorWidget in self._modified_editors:
            try:
                editorWidget.restyle(editorWidget.lang)
            except RuntimeError:
                print('the editor has been removed')

    def _preview_style(self):
        scheme = self._listScheme.currentItem().text()
        if scheme == self.current_scheme:
            return
        main_container = IDE.get_service('main_container')
        if not main_container:
            return
        editorWidget = main_container.get_current_editor()
        if editorWidget is not None:
            resources.CUSTOM_SCHEME = self._schemes.get(
                scheme,
                resources.COLOR_SCHEME)
            editorWidget.restyle(editorWidget.lang)
            self._modified_editors.append(editorWidget)
        self.current_scheme = scheme

    def _load_editor_font(self):
        try:
            font, ok = QFontDialog.getFont(self._font, self)
            if ok:
                self._font = font
                btnText = ', '.join(self._font.toString().split(',')[0:2])
                self._btnEditorFont.setText(btnText)
        except:
            QMessageBox.warning(
                self,
                translations.TR_PREFERENCES_EDITOR_GENERAL_FONT_MESSAGE_TITLE,
                translations.TR_PREFERENCES_EDITOR_GENERAL_FONT_MESSAGE_BODY)

    def save(self):
        qsettings = IDE.ninja_settings()
        settings.FONT = self._font
        qsettings.setValue('preferences/editor/font', settings.FONT)
        settings.SHOW_MINIMAP = self._checkShowMinimap.isChecked()
        settings.MINIMAP_MAX_OPACITY = self._spinMaxOpacity.value() / 100.0
        settings.MINIMAP_MIN_OPACITY = self._spinMinOpacity.value() / 100.0
        settings.SIZE_PROPORTION = self._spinSize.value() / 100.0
        qsettings.setValue('preferences/editor/minimapMaxOpacity',
                           settings.MINIMAP_MAX_OPACITY)
        qsettings.setValue('preferences/editor/minimapMinOpacity',
                           settings.MINIMAP_MIN_OPACITY)
        qsettings.setValue('preferences/editor/minimapSizeProportion',
                           settings.SIZE_PROPORTION)
        qsettings.setValue('preferences/editor/minimapShow',
                           settings.SHOW_MINIMAP)
        scheme = self._listScheme.currentItem().text()
        resources.CUSTOM_SCHEME = self._schemes.get(scheme,
                                                    resources.COLOR_SCHEME)
        qsettings.setValue('preferences/editor/scheme', scheme)
class EditorGeneral(QWidget):
    """EditorGeneral widget class."""
    def __init__(self, parent):
        super(EditorGeneral, self).__init__()
        self._preferences, vbox = parent, QVBoxLayout(self)
        self.original_style = copy.copy(resources.CUSTOM_SCHEME)
        self.current_scheme, self._modified_editors = 'default', []
        self._font = settings.FONT

        groupBoxMini = QGroupBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_MINIMAP)
        groupBoxDocMap = QGroupBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_DOCMAP)
        groupBoxTypo = QGroupBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_TYPOGRAPHY)
        groupBoxScheme = QGroupBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_SCHEME)

        boxMiniAndDocMap = QHBoxLayout()
        # Minimap
        formMini = QGridLayout(groupBoxMini)
        formMini.setContentsMargins(5, 15, 5, 5)
        self._checkShowMinimap = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_ENABLE_MINIMAP)
        self._spinMaxOpacity = QSpinBox()
        self._spinMaxOpacity.setRange(0, 100)
        self._spinMaxOpacity.setSuffix("% Max.")
        self._spinMinOpacity = QSpinBox()
        self._spinMinOpacity.setRange(0, 100)
        self._spinMinOpacity.setSuffix("% Min.")
        self._spinSize = QSpinBox()
        self._spinSize.setMaximum(100)
        self._spinSize.setMinimum(0)
        self._spinSize.setSuffix(
            translations.TR_PREFERENCES_EDITOR_GENERAL_AREA_MINIMAP)
        formMini.addWidget(self._checkShowMinimap, 0, 1)
        formMini.addWidget(
            QLabel(translations.TR_PREFERENCES_EDITOR_GENERAL_SIZE_MINIMAP), 1,
            0, Qt.AlignRight)
        formMini.addWidget(self._spinSize, 1, 1)
        formMini.addWidget(
            QLabel(translations.TR_PREFERENCES_EDITOR_GENERAL_OPACITY), 2, 0,
            Qt.AlignRight)
        formMini.addWidget(self._spinMinOpacity, 2, 1)
        formMini.addWidget(self._spinMaxOpacity, 2, 2)
        boxMiniAndDocMap.addWidget(groupBoxMini)
        # Document Map
        formDocMap = QGridLayout(groupBoxDocMap)
        self._checkShowDocMap = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_ENABLE_DOCMAP)
        self._checkShowSlider = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_DOCMAP_SLIDER)
        self._checkOriginalScroll = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_ORIGINAL_SCROLLBAR)
        self._checkCurrentLine = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_DOCMAP_CURRENT_LINE)
        self._checkSearchLines = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_DOCMAP_SEARCH_LINES)
        self._spinWidth = QSpinBox()
        self._spinWidth.setRange(5, 40)
        formDocMap.addWidget(self._checkShowDocMap, 0, 0)
        formDocMap.addWidget(self._checkShowSlider, 0, 1)
        formDocMap.addWidget(self._checkOriginalScroll, 0, 2)
        formDocMap.addWidget(self._checkCurrentLine, 1, 0)
        formDocMap.addWidget(self._checkSearchLines, 1, 1)
        formDocMap.addWidget(
            QLabel(translations.TR_PREFERENCES_EDITOR_GENERAL_DOCMAP_WIDTH), 2,
            0)
        formDocMap.addWidget(self._spinWidth, 2, 1)
        boxMiniAndDocMap.addWidget(groupBoxDocMap)
        # Typo
        gridTypo = QGridLayout(groupBoxTypo)
        gridTypo.setContentsMargins(5, 15, 5, 5)
        self._btnEditorFont = QPushButton('')
        gridTypo.addWidget(
            QLabel(translations.TR_PREFERENCES_EDITOR_GENERAL_EDITOR_FONT), 0,
            0, Qt.AlignRight)
        gridTypo.addWidget(self._btnEditorFont, 0, 1)
        #Scheme
        vboxScheme = QVBoxLayout(groupBoxScheme)
        vboxScheme.setContentsMargins(5, 15, 5, 5)
        self._listScheme = QListWidget()
        vboxScheme.addWidget(self._listScheme)
        hbox = QHBoxLayout()
        btnDownload = QPushButton(
            translations.TR_PREFERENCES_EDITOR_DOWNLOAD_SCHEME)
        btnDownload.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        btnDownload.clicked['bool'].connect(self._open_schemes_manager)
        hbox.addWidget(btnDownload)
        btnAdd = QPushButton(QIcon(":img/add"),
                             translations.TR_EDITOR_CREATE_SCHEME)
        btnAdd.setIconSize(QSize(16, 16))
        btnAdd.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        btnAdd.clicked['bool'].connect(self._open_schemes_designer)
        btnRemove = QPushButton(QIcon(":img/delete"),
                                translations.TR_EDITOR_REMOVE_SCHEME)
        btnRemove.setIconSize(QSize(16, 16))
        btnRemove.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        btnRemove.clicked['bool'].connect(self._remove_scheme)
        hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding))
        hbox.addWidget(btnAdd)
        hbox.addWidget(btnRemove)
        vboxScheme.addLayout(hbox)

        vbox.addLayout(boxMiniAndDocMap)
        vbox.addWidget(groupBoxTypo)
        vbox.addWidget(groupBoxScheme)

        #Settings
        qsettings = IDE.ninja_settings()
        qsettings.beginGroup('preferences')
        qsettings.beginGroup('editor')
        self._checkShowMinimap.setChecked(settings.SHOW_MINIMAP)
        if settings.IS_MAC_OS:
            self._spinMinOpacity.setValue(100)
            self._spinMaxOpacity.setValue(100)
            self._spinMinOpacity.setDisabled(True)
            self._spinMaxOpacity.setDisabled(True)
        else:
            self._spinMinOpacity.setValue(settings.MINIMAP_MIN_OPACITY * 100)
            self._spinMaxOpacity.setValue(settings.MINIMAP_MAX_OPACITY * 100)
        self._checkShowDocMap.setChecked(settings.SHOW_DOCMAP)
        self._checkShowSlider.setChecked(settings.DOCMAP_SLIDER)
        self._checkOriginalScroll.setChecked(settings.EDITOR_SCROLLBAR)
        self._checkCurrentLine.setChecked(settings.DOCMAP_CURRENT_LINE)
        self._checkSearchLines.setChecked(settings.DOCMAP_SEARCH_LINES)
        self._spinWidth.setValue(settings.DOCMAP_WIDTH)
        self._spinSize.setValue(settings.SIZE_PROPORTION * 100)
        btnText = ', '.join(self._font.toString().split(',')[0:2])
        self._btnEditorFont.setText(btnText)
        self._listScheme.clear()
        self._listScheme.addItem('default')
        self._schemes = json_manager.load_editor_skins()
        for item in self._schemes:
            self._listScheme.addItem(item)
        items = self._listScheme.findItems(
            qsettings.value('scheme', defaultValue='', type='QString'),
            Qt.MatchExactly)
        if items:
            self._listScheme.setCurrentItem(items[0])
        else:
            self._listScheme.setCurrentRow(0)
        qsettings.endGroup()
        qsettings.endGroup()

        #Signals
        self._btnEditorFont.clicked['bool'].connect(self._load_editor_font)
        self._listScheme.itemSelectionChanged.connect(self._preview_style)
        self._preferences.savePreferences.connect(self.save)

    def _open_schemes_manager(self):
        ninjaide = IDE.getInstance()
        ninjaide.show_schemes()
        # refresh schemes

    def _open_schemes_designer(self):
        name = self._listScheme.currentItem().text()
        scheme = self._schemes.get(name, resources.COLOR_SCHEME)
        designer = preferences_editor_scheme_designer.EditorSchemeDesigner(
            scheme, self)
        designer.exec_()
        if designer.saved:
            scheme_name = designer.line_name.text()
            scheme = designer.original_style
            self._schemes[scheme_name] = scheme
            result = self._listScheme.findItems(scheme_name, Qt.MatchExactly)
            if not result:
                self._listScheme.addItem(scheme_name)

    def _remove_scheme(self):
        name = self._listScheme.currentItem().text()
        fileName = ('{0}.color'.format(
            file_manager.create_path(resources.EDITOR_SKINS, name)))
        file_manager.delete_file(fileName)
        item = self._listScheme.takeItem(self._listScheme.currentRow())
        del item

    def hideEvent(self, event):
        super(EditorGeneral, self).hideEvent(event)
        resources.CUSTOM_SCHEME = self.original_style
        for editorWidget in self._modified_editors:
            try:
                editorWidget.restyle(editorWidget.lang)
            except RuntimeError:
                print('the editor has been removed')

    def _preview_style(self):
        scheme = self._listScheme.currentItem().text()
        if scheme == self.current_scheme:
            return
        main_container = IDE.get_service('main_container')
        if not main_container:
            return
        editorWidget = main_container.get_current_editor()
        if editorWidget is not None:
            resources.CUSTOM_SCHEME = self._schemes.get(
                scheme, resources.COLOR_SCHEME)
            editorWidget.restyle(editorWidget.lang)
            self._modified_editors.append(editorWidget)
        self.current_scheme = scheme

    def _load_editor_font(self):
        try:
            font, ok = QFontDialog.getFont(self._font, self)
            if ok:
                self._font = font
                btnText = ', '.join(self._font.toString().split(',')[0:2])
                self._btnEditorFont.setText(btnText)
        except:
            QMessageBox.warning(
                self,
                translations.TR_PREFERENCES_EDITOR_GENERAL_FONT_MESSAGE_TITLE,
                translations.TR_PREFERENCES_EDITOR_GENERAL_FONT_MESSAGE_BODY)

    def save(self):
        qsettings = IDE.ninja_settings()
        settings.FONT = self._font
        qsettings.setValue('preferences/editor/font', settings.FONT)
        settings.SHOW_MINIMAP = self._checkShowMinimap.isChecked()
        settings.MINIMAP_MAX_OPACITY = self._spinMaxOpacity.value() / 100.0
        settings.MINIMAP_MIN_OPACITY = self._spinMinOpacity.value() / 100.0
        settings.SIZE_PROPORTION = self._spinSize.value() / 100.0
        qsettings.setValue('preferences/editor/minimapMaxOpacity',
                           settings.MINIMAP_MAX_OPACITY)
        qsettings.setValue('preferences/editor/minimapMinOpacity',
                           settings.MINIMAP_MIN_OPACITY)
        qsettings.setValue('preferences/editor/minimapSizeProportion',
                           settings.SIZE_PROPORTION)
        qsettings.setValue('preferences/editor/minimapShow',
                           settings.SHOW_MINIMAP)
        settings.SHOW_DOCMAP = self._checkShowDocMap.isChecked()
        settings.DOCMAP_SLIDER = self._checkShowSlider.isChecked()
        settings.EDITOR_SCROLLBAR = self._checkOriginalScroll.isChecked()
        settings.DOCMAP_CURRENT_LINE = self._checkCurrentLine.isChecked()
        settings.DOCMAP_SEARCH_LINES = self._checkSearchLines.isChecked()
        settings.DOCMAP_WIDTH = self._spinWidth.value()
        qsettings.setValue('preferences/editor/docmapShow',
                           settings.SHOW_DOCMAP)
        qsettings.setValue('preferences/editor/docmapSlider',
                           settings.DOCMAP_SLIDER)
        qsettings.setValue('preferences/editor/editorScrollBar',
                           settings.EDITOR_SCROLLBAR)
        qsettings.setValue('preferences/editor/docmapCurrentLine',
                           settings.DOCMAP_CURRENT_LINE)
        qsettings.setValue('preferences/editor/docmapSearchLines',
                           settings.DOCMAP_SEARCH_LINES)
        qsettings.setValue('preferences/editor/docmapWidth',
                           settings.DOCMAP_WIDTH)
        scheme = self._listScheme.currentItem().text()
        resources.CUSTOM_SCHEME = self._schemes.get(scheme,
                                                    resources.COLOR_SCHEME)
        qsettings.setValue('preferences/editor/scheme', scheme)
コード例 #21
0
class TagsPage(QWizardPage):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.files = set()
        self.old_files = set()
        self.new_common_tags = set()
        self.recently_deleted_tags = set()
        self.button_max_width = 50
        self.box_mini_width = 225
        self.create_files_box()
        self.create_common_tags_box()
        self.create_recommended_tags_box()
        self.create_all_tags_box()
        self.file_info_box = FileInfoBox()

        self._init_ui_()

    def _init_ui_(self) -> None:
        self.setTitle("Tags")
        self.setButtonText(QWizard.NextButton, "Preview")

        layout = QHBoxLayout()
        layout.addWidget(self.filesBox)
        layout.addWidget(self.file_info_box)

        tags_layout = QVBoxLayout()
        tags_layout.addWidget(self.commonTagsBox)
        tags_layout.addWidget(self.recommendTagsBox)
        layout.addLayout(tags_layout)

        layout.addWidget(self.allTagsBox)
        self.setLayout(layout)

    def initializePage(self):
        if self.old_files == self.files:
            return
        else:
            self.old_files = self.files

        self.fileList.clear()
        self.tagList.clear()
        self.recommendTagsList.clear()
        self.new_common_tags.clear()
        self.allTagsList.clear()

        for file in self.files:
            item = QListWidgetItem(self.fileList)
            item.setText(file)
            # item.setFlags(Qt.NoItemFlags)

        tag_set_list: List[set] = []
        for file in self.files:
            if Biji.get_biji_json_path(file).exists():
                biji = Biji.from_file(file)
                tag_set_list.append(set(biji.tags))
            else:
                tag_set_list.append(set())

        if tag_set_list:
            self.common_tags = tag_set_list[0].intersection(*tag_set_list[1:])
            all_tags = tag_set_list[0].union(*tag_set_list[1:])
        else:
            self.common_tags = set()
            all_tags = set()

        if self.common_tags:
            for tag in self.common_tags:
                item = QListWidgetItem(self.tagList)
                item.setText(tag)
                item.setCheckState(Qt.Unchecked)
                item.setFlags(item.flags() | Qt.ItemIsEditable)

        recommend_tags = all_tags - self.common_tags
        if recommend_tags:
            for tag in recommend_tags:
                item = QListWidgetItem(self.recommendTagsList)
                item.setText(tag)

        recently_deleted_tags = \
            self.recently_deleted_tags - recommend_tags - self.common_tags
        self.recommendTagsList.insertItems(0, recently_deleted_tags)

        for row in BijiDatabase.get_all_tags_desc():
            item = QListWidgetItem(self.allTagsList)
            item.setText(row['tag'])

        self.fileList.setCurrentRow(0)

    def validatePage(self):
        self.update_new_common_tags()
        return True

    def cleanupPage(self):
        self.update_new_common_tags()
        new_added_tags = self.new_common_tags - self.common_tags
        self.recently_deleted_tags |= new_added_tags

    def update_new_common_tags(self) -> None:
        self.new_common_tags.clear()
        for i in range(self.tagList.count()):
            self.new_common_tags.add(self.tagList.item(i).text().strip())

    def create_files_box(self) -> None:
        self.filesBox = QGroupBox("Files")
        self.filesBox.setMinimumWidth(self.box_mini_width)
        layout = QVBoxLayout()

        self.fileList = QListWidget()
        self.fileList.currentItemChanged.connect(self.update_file_info)

        layout.addWidget(self.fileList)
        self.filesBox.setLayout(layout)

    def create_common_tags_box(self) -> None:
        self.commonTagsBox = QGroupBox("Common Tags")
        self.commonTagsBox.setMinimumWidth(self.box_mini_width)
        layout = QVBoxLayout()

        buttons_layout = QHBoxLayout()
        all_tags_button = QPushButton("All")
        all_tags_button.setMaximumWidth(self.button_max_width)
        all_tags_button.clicked.connect(lambda: check_all(self.tagList))
        delete_tags_button = QPushButton("Del")
        delete_tags_button.setMaximumWidth(self.button_max_width)
        delete_tags_button.clicked.connect(self.delete_tags)
        delete_tags_button.setToolTip("Delete checked items from the list")

        add_tags_button = QPushButton("Add")
        add_tags_button.setMaximumWidth(self.button_max_width)
        add_tags_button.clicked.connect(self.add_tags)

        buttons_layout.addWidget(all_tags_button)
        buttons_layout.addWidget(delete_tags_button)
        buttons_layout.addStretch()
        buttons_layout.addWidget(add_tags_button)

        self.tagList = QListWidget()

        layout.addLayout(buttons_layout)
        layout.addWidget(self.tagList)
        self.commonTagsBox.setLayout(layout)

    def create_recommended_tags_box(self) -> None:
        self.recommendTagsBox = QGroupBox("Recommended Tags")
        self.recommendTagsBox.setMinimumWidth(self.box_mini_width)
        layout = QVBoxLayout()
        self.recommendTagsList = QListWidget()
        layout.addWidget(self.recommendTagsList)
        self.recommendTagsBox.setLayout(layout)

        self.recommendTagsList.doubleClicked.connect(self.move_to_common_tags)

    def create_all_tags_box(self) -> None:
        self.allTagsBox = QGroupBox("All Tags")
        self.allTagsBox.setMinimumWidth(self.box_mini_width)
        layout = QVBoxLayout()
        self.allTagsList = QListWidget()
        layout.addWidget(self.allTagsList)
        self.allTagsBox.setLayout(layout)

        self.allTagsList.doubleClicked.connect(self.copy_to_common_tags)

    def delete_tags(self) -> None:
        deleted_items = delete_from_list(self.tagList)
        self.recently_deleted_tags |= deleted_items

        for item in deleted_items:
            if not self.recommendTagsList.findItems(item, Qt.MatchExactly):
                self.recommendTagsList.insertItem(0, item)

    def add_tags(self) -> None:
        tag, ok = QInputDialog.getText(self, "New tag", "New tag:")
        tag = tag.strip()
        if not ok or len(tag) == 0:
            return
        if self.tagList.findItems(tag,
                                  Qt.MatchFixedString | Qt.MatchCaseSensitive):
            QMessageBox.information(self, "Duplicated.",
                                    "The tag is already in the list.\n",
                                    QMessageBox.Close)
            return
        item = QListWidgetItem(self.tagList)
        item.setText(tag)
        item.setCheckState(Qt.Unchecked)
        item.setFlags(item.flags() | Qt.ItemIsEditable)

    def copy_to_common_tags(self, index: QModelIndex) -> None:
        if not self.tagList.findItems(
                index.data(), Qt.MatchFixedString | Qt.MatchCaseSensitive):
            item = QListWidgetItem(self.tagList)
            item.setText(index.data())
            item.setCheckState(Qt.Unchecked)
            item.setFlags(item.flags() | Qt.ItemIsEditable)

    def move_to_common_tags(self, index: QModelIndex) -> None:
        self.copy_to_common_tags(index)
        self.recommendTagsList.takeItem(index.row())

    def update_file_info(self, current: QListWidgetItem) -> None:
        self.file_info_box.update_file_info(current)
コード例 #22
0
ファイル: gui.py プロジェクト: SilasiLab/microinfarcts
class StartWindow(QMainWindow):
    """
    Main entrance of the whole process.
    """
    def __init__(self):
        super().__init__()
        self.root_dir = os.getcwd()
        self.save_dir = os.getcwd()
        self.script_dir = "/home/silasi/ANTs/Scripts"
        # self.script_dir = os.getcwd()

        self.widget_main = QWidget()
        self.layout_main = QGridLayout()
        self.widget_main.setLayout(self.layout_main)

        self.allList = QListWidget()
        self.analyseList = QListWidget()
        self.btnAdd = QPushButton(">>")
        self.btnAdd.clicked.connect(self.add)

        self.buttonGroupWrite = QButtonGroup(self.widget_main)
        self.radio_write = QRadioButton("Write a sumarry")
        self.radio_show = QRadioButton("Show results visually")
        self.buttonGroupWrite.addButton(self.radio_write)
        self.buttonGroupWrite.addButton(self.radio_show)
        self.radio_write.setChecked(True)

        self.buttonGroupAutoSeg = QButtonGroup(self.widget_main)
        self.radio_auto = QRadioButton("Automatically detect microspheres")
        self.radio_man = QRadioButton(" Count microspheres manually")
        self.radio_auto.setChecked(True)

        self.labelnote = QLabel(
            "*make sure images are saved in the correct directory path (i.e. images/brain1/raw/)"
        )
        self.labelnote2 = QLabel(
            "*Select which analysis step you wish to perform")
        self.labelnote3 = QLabel("*Select the root directory")

        self.labelRootDir = QLabel(
            "Select root directory containing serial brain images. (i.e. images/)"
        )
        self.btnRootDir = QPushButton('Select')
        self.btnRootDir.clicked.connect(self.select_rootDir)
        self.lineRootDir = QLabel(self.root_dir)

        self.labelSaveDir = QLabel(
            "Select directory to save results (different from root directory)")
        self.btnSaveDir = QPushButton('Select')
        self.btnSaveDir.clicked.connect(self.select_saveDir)
        self.lineSaveDir = QLabel(self.save_dir)

        self.labelScriptDir = QLabel("Select ANTs script directory")
        self.btnScriptDir = QPushButton('Select')
        self.btnScriptDir.clicked.connect(self.select_scriptDir)
        self.lineScriptDir = QLabel(self.script_dir)

        self.btnRun = QPushButton("Analyse (Will not response for a while)")
        self.btnRun.clicked.connect(self.analyse)

        self.thickness_label = QLabel(self)
        self.thickness_label.setText(
            '                                                                       Section thickness:'
        )
        self.thickness_combo = QComboBox(self)
        self.thickness_combo.addItem("25")
        self.thickness_combo.addItem("50")
        self.thickness_combo.addItem("100")

        self.layout_main.addWidget(self.labelnote, 0, 0)
        self.layout_main.addWidget(self.labelRootDir, 1, 0)
        self.layout_main.addWidget(self.lineRootDir, 2, 0)
        self.layout_main.addWidget(self.btnRootDir, 2, 1)

        self.layout_main.addWidget(self.labelSaveDir, 3, 0)
        self.layout_main.addWidget(self.lineSaveDir, 4, 0)
        self.layout_main.addWidget(self.btnSaveDir, 4, 1)

        self.layout_main.addWidget(self.labelScriptDir, 5, 0)
        self.layout_main.addWidget(self.lineScriptDir, 6, 0)
        self.layout_main.addWidget(self.btnScriptDir, 6, 1)

        self.layout_main.addWidget(self.labelnote2, 7, 0)
        self.layout_main.addWidget(self.radio_show, 8, 0)
        self.layout_main.addWidget(self.radio_write, 8, 1)

        self.layout_main.addWidget(self.radio_auto, 9, 0)
        self.layout_main.addWidget(self.radio_man, 9, 1)

        self.layout_main.addWidget(self.labelnote3, 10, 0)
        self.layout_main.addWidget(self.allList, 11, 0)
        self.layout_main.addWidget(self.btnAdd, 11, 1)
        self.layout_main.addWidget(self.analyseList, 11, 2)

        self.layout_main.addWidget(self.thickness_label, 12, 0)

        self.layout_main.addWidget(self.thickness_combo, 12, 1)
        self.layout_main.addWidget(self.btnRun, 12, 2)

        self.setCentralWidget(self.widget_main)
        self.update_list()
        self.show()

    def update_list(self):
        for item in os.listdir(self.root_dir):
            self.allList.addItem(os.path.join(self.root_dir, item))

    def add(self):
        for item in self.allList.selectedItems():
            if len(self.analyseList.findItems(item.text(),
                                              Qt.MatchExactly)) == 0:
                self.analyseList.addItem(item.text())

    def choosePath(self):
        root = Tk()
        root.withdraw()
        result = askdirectory(
            initialdir="/",
            title="Select root directory containing all cage folders")
        return result

    def select_rootDir(self):
        self.root_dir = self.choosePath()
        if type(self.root_dir) is str:
            self.lineRootDir.setText(self.root_dir)
            self.allList.clear()
            self.analyseList.clear()
            self.update_list()

    def select_saveDir(self):
        self.save_dir = self.choosePath()
        if type(self.save_dir) is str:
            self.lineSaveDir.setText(self.save_dir)

    def select_scriptDir(self):
        self.script_dir = self.choosePath()
        if type(self.script_dir) is str:
            self.lineScriptDir.setText(self.script_dir)

    def analyse(self):
        self.thickness = int(str(self.thickness_combo.currentText()))
        d, okPressed = QInputDialog.getDouble(
            self, "How strict you would like to calculate the edge?",
            "Value(between 0 and 1):", 0., 0., 1., 2)
        if okPressed:
            strict = float(d)
        else:
            strict = 0.
        for brain_dir in [
                str(self.analyseList.item(i).text())
                for i in range(self.analyseList.count())
        ]:
            run_one_brain(brain_dir,
                          self.save_dir,
                          True,
                          True,
                          self.script_dir,
                          True,
                          self.radio_write.isChecked(),
                          self.radio_show.isChecked(),
                          False,
                          False,
                          self.radio_auto.isChecked(),
                          section_thickness=self.thickness,
                          strict=strict)
コード例 #23
0
class MyMainWindow(QMainWindow):
    def __init__(self):
        super(MyMainWindow, self).__init__()

        self.setWindowTitle('pyrodump')

        self.setMinimumSize(800, 640)

        self.setup_menubar()

        self.vlayout = QVBoxLayout()
        self.interface = None  # type: str
        self.btn_interface = QPushButton('No interface')
        self.btn_interface.pressed.connect(self.on_select_interface)

        self.hlayout1 = QHBoxLayout()
        self.vlayout.addLayout(self.hlayout1)
        self.hlayout1.addWidget(self.btn_interface)
        self.label_interface_type = QLabel("Type: unknown")
        self.hlayout1.addWidget(self.label_interface_type)

        self.vlayout.addWidget(QHLine())

        hlayout2 = QHBoxLayout()
        self.vlayout.addLayout(hlayout2)

        vlayout2 = QVBoxLayout()
        hlayout2.addLayout(vlayout2)
        vlayout2.addWidget(QLabel("Access Points"))
        self.ap_list = QListWidget()  # type: QListWidget
        self.ap_list.itemSelectionChanged.connect(
            self.ap_list_item_selection_changed)
        vlayout2.addWidget(self.ap_list)

        vlayout3 = QVBoxLayout()
        hlayout2.addLayout(vlayout3)
        vlayout3.addWidget(QLabel("Clients on selected AP"))
        self.client_list = QListWidget()
        self.client_list.itemSelectionChanged.connect(
            self.client_list_item_selection_changed)
        vlayout3.addWidget(self.client_list)

        self.label_ap_details = QLabel()
        self.display_ap_details()
        self.vlayout.addWidget(self.label_ap_details)

        window = QWidget()
        window.setLayout(self.vlayout)
        self.setCentralWidget(window)

        self.proc_start_monitor_mode = None  # type: QProcess
        self.proc_stop_monitor_mode = None  # type: QProcess
        self.proc_airodump = None  # type: QProcess

        self.proc_deauth = None  # type: QProcess
        self.proc_deauth_args = []  # save arguments for auto respawn
        self.proc_deauth2 = None  # type: QProcess

        self.airodump_analyzer = None  # type: AirodumpAnalyzer
        self.timer = None  # type: QTimer

        self.tempdir = tempfile.TemporaryDirectory()

        self.auto_select_interface()

    def display_ap_details(self,
                           essid="(None)",
                           bssid="(None}",
                           channel="(None)",
                           power="(None)",
                           num_beacons="(None)"):
        self.label_ap_details.setText(
            "ESSID: {}, BSSID: {}\nChannel: {}, Power: {}, # Beacons: {}".
            format(essid, bssid, channel, power, num_beacons))

    def set_interface(self, iface):
        self.interface = iface

        if iface is None:
            self.btn_interface.setText("None")
        else:
            self.btn_interface.setText(iface)
            iface_type = get_wifi_interface_type(iface)
            if iface_type:
                self.label_interface_type.setText(
                    "Type: {}".format(iface_type))
            else:
                self.label_interface_type.setText("Type: unknown")

    def auto_select_interface(self):
        ifaces = get_wifi_interfaces()

        if not ifaces:
            self.set_interface(None)
            return

        # try to find one with monitor mode enabled
        for iface in ifaces:
            if get_wifi_interface_type(iface) == "monitor":
                self.set_interface(iface)

        # fallback
        self.set_interface(ifaces[0])

    def on_select_interface(self):
        ifaces = get_wifi_interfaces()

        if not ifaces:
            QErrorDialog("No WiFi interfaces found.").exec_()
            return

        iface, ok = QInputDialog.getItem(self, "Select WiFi interface",
                                         "Select WiFi interface", ifaces, 0,
                                         False)

        self.set_interface(iface)

    def check_interface_ready(self):
        if get_wifi_interface_type(self.interface) != "monitor":
            QErrorDialog(
                "A monitor mode interface must be started or selected first."
            ).exec_()
            return False

        return True

    def start_airodump(self):
        print("Starting airodump...")

        if self.proc_airodump:
            self.stop_airodump()

        if not self.check_interface_ready():
            return

        cmd = "airodump-ng"
        cmd_args = [
            self.interface, "-w",
            os.path.join(self.tempdir.name, "airodump"), "--write-interval",
            "1", "-o", "csv", "--band", "ag"
        ]

        self.proc_airodump = QProcess()
        self.proc_airodump.start(cmd, cmd_args)
        self.monitor_mode_menu.setDisabled(True)

        self.timer = QTimer()
        self.timer.timeout.connect(self.update_airodump_display)
        self.timer.start(1000)

    def stop_airodump(self):
        print("Stopping airodump...")

        if self.timer:
            self.timer.stop()
        if self.airodump_analyzer:
            self.airodump_analyzer.running = False
        if self.proc_airodump:
            self.proc_airodump.kill()
        self.proc_airodump = None
        self.monitor_mode_menu.setDisabled(False)

    def start_deauth_client(self):
        if self.proc_airodump or not self.client_list.selectedItems():
            QErrorDialog("How did this happen?").exec_()
            return

        if not self.check_interface_ready():
            return

        m = re.compile("^(.*) \(").search(
            self.client_list.selectedItems()[0].text())
        if not m:
            QErrorDialog("Could not parse client MAC").exec_()
            return

        client_mac = m.group(1)

        ap_bssid = None
        ap_channel = None
        for bssid, ap_data in self.airodump_analyzer.access_points.items():
            if client_mac in ap_data["clients"]:
                ap_bssid = bssid
                ap_channel = ap_data["channel"]
                break

        if not ap_bssid:
            QErrorDialog(
                "Could not find AP BSSID of client with MAC {}".format(
                    client_mac)).exec_()
            return

        print("-- Trying deauth of client with MAC {} from BSSID {}...".format(
            client_mac, ap_bssid))

        self.monitor_mode_menu.setDisabled(True)
        self.airodump_menu.setDisabled(True)

        # start airodump-ng on correct channel
        self.proc_deauth2 = QProcess()
        self.proc_deauth2.start(
            "airodump-ng", [self.interface, "--band", "ag", "-c", ap_channel])

        self.proc_deauth = QProcess()
        # hack with stdbuf so that stdout/stderr is not buffered
        self.proc_deauth_args = [
            "-i0", "-o0", "-e0", "aireplay-ng", "--deauth=0", "-c", client_mac,
            "-a", ap_bssid, self.interface
        ]
        self.proc_deauth.finished.connect(self.deauth_proc_finished)
        self.proc_deauth.readyReadStandardOutput.connect(
            self.deauth_proc_print_output)
        self.proc_deauth.setProcessChannelMode(QProcess.MergedChannels)
        self.proc_deauth.start("stdbuf", self.proc_deauth_args,
                               QProcess.Unbuffered | QProcess.ReadWrite)

    def deauth_proc_print_output(self):
        if self.proc_deauth:
            print(self.proc_deauth.readAll().data().decode("utf-8"),
                  flush=True)

    def deauth_proc_finished(self):
        if self.proc_deauth:
            print("-- aireplay-ng finished by itself -> restart")
            self.proc_deauth.start("stdbuf", self.proc_deauth_args,
                                   QProcess.Unbuffered | QProcess.ReadWrite)

    def stop_deauth_client(self):
        print("Stopping deauth...")

        self.proc_deauth2 = None
        self.proc_deauth = None

        self.monitor_mode_menu.setDisabled(False)
        self.airodump_menu.setDisabled(False)

    def update_airodump_display(self):
        if not self.airodump_analyzer or not self.airodump_analyzer.running:
            if not self.proc_airodump:
                print(
                    "! Airodump analyzer not running. Cannot update display.")
                return
            else:
                # try to create analyzer object
                match_files = self.tempdir.name + "/airodump-*.csv"
                candidate_files = glob.glob(match_files)

                if not candidate_files:
                    QErrorDialog(
                        "Could not find airodump output at location \"{}\". Stopping Airodump."
                        .format(match_files)).exec_()
                    # TODO only stop on 3rd try
                    self.stop_airodump()
                    return

                file = sorted(candidate_files)[-1]  # select most recent one
                self.airodump_analyzer = AirodumpAnalyzer(file)

        self.airodump_analyzer.update()

        # remember selection - kind of ugly
        if self.ap_list.selectedItems():
            selected_item = self.ap_list.selectedItems()[0].text()
        else:
            selected_item = None

        self.ap_list.clear()

        last_channel = None
        for ap, ap_data in sorted(self.airodump_analyzer.access_points.items(),
                                  key=lambda kv: int(kv[1]["channel"])):
            if ap_data["channel"] != last_channel:
                last_channel = ap_data["channel"]
                self.ap_list.addItem("-- Channel {} --".format(last_channel))

            self.ap_list.addItem("{} (BSSID: {})".format(
                ap_data["essid"], ap_data["bssid"]))

        # for ap, ap_data in self.airodump_analyzer.access_points.items():
        #     self.ap_list.addItem("{} (BSSID: {})".format(ap_data["essid"], ap_data["bssid"]))

        if selected_item:
            newItemsMatching = self.ap_list.findItems(selected_item,
                                                      QtCore.Qt.MatchExactly)
            if newItemsMatching:
                newItemsMatching[0].setSelected(True)
                self.ap_list.setCurrentItem(newItemsMatching[0])

    def ap_list_item_selection_changed(self):
        self.display_ap_details()

        if not self.ap_list.selectedItems():
            # unselected item
            self.client_list.clear()
            return

        selected_bssid_match = re.compile("\(BSSID: (.*)\)$").search(
            self.ap_list.selectedItems()[0].text())

        if not selected_bssid_match:
            self.client_list.clear()
            return

        selected_bssid = selected_bssid_match.group(1)

        if not selected_bssid in self.airodump_analyzer.access_points:
            self.client_list.clear()
            return

        ap = self.airodump_analyzer.access_points[selected_bssid]  # shorthand
        self.display_ap_details(essid=ap["essid"],
                                bssid=ap["bssid"],
                                channel=ap["channel"],
                                power=ap["power"],
                                num_beacons=ap["num_beacons"])

        self.client_list.clear()
        for client_mac, client_data in ap["clients"].items():
            s = "{} (packets: {}, power: {})".format(
                client_data["mac"], client_data["num_packets"],
                client_data["power"])
            self.client_list.addItem(s)

    def client_list_item_selection_changed(self):
        self.client_menu.setDisabled(True)

        if self.client_list.selectedItems() and not self.proc_airodump:
            self.client_menu.setDisabled(False)

    def start_monitor_mode(self):
        if self.interface is None:
            QErrorDialog(
                "No interface selected.\nPlease select one first.").exec_()
            return

        print("Trying to start monitor mode on interface {}...".format(
            self.interface))

        cmd = "airmon-ng"
        cmd_args = ["start", self.interface]

        self.proc_start_monitor_mode = QProcess(self)
        self.proc_start_monitor_mode.finished.connect(
            self.on_start_monitor_mode_cb)
        self.proc_start_monitor_mode.start(cmd, cmd_args)
        self.menubar.setDisabled(True)

    def stop_monitor_mode(self):
        if self.interface is None:
            QErrorDialog(
                "No interface selected.\nPlease select one first.").exec_()
            return

        print("Trying to stop monitor mode on interface {}...".format(
            self.interface))

        cmd = "airmon-ng"
        cmd_args = ["stop", self.interface]

        self.proc_stop_monitor_mode = QProcess(self)
        self.proc_stop_monitor_mode.finished.connect(
            self.on_stop_monitor_mode_cb)
        self.proc_stop_monitor_mode.start(cmd, cmd_args)
        self.menubar.setDisabled(True)

    def on_start_monitor_mode_cb(self, exitCode, exitStatus):
        self.menubar.setDisabled(False)
        stdout = self.proc_start_monitor_mode.readAllStandardOutput().data(
        ).decode("utf-8")
        self.proc_start_monitor_mode = None

        expr1 = \
            re.compile("mac80211 monitor mode [a-z]* enabled for \[phy[0-9]*\][a-z0-9]* on \[phy[0-9]*\]([a-z0-9]*)")

        match = expr1.search(stdout)
        if not match:
            QErrorDialog(
                "Not sure if monitor mode interface was successfully created. Please select manually."
            ).exec_()
            self.auto_select_interface()
            return

        iface = match.group(1)
        print("Enabled monitor mode for interface {}".format(iface))
        self.set_interface(iface)

    def on_stop_monitor_mode_cb(self, exitCode, exitStatus):
        self.menubar.setDisabled(False)
        stdout = self.proc_stop_monitor_mode.readAllStandardOutput().data(
        ).decode("utf-8")
        self.proc_stop_monitor_mode = None
        self.auto_select_interface()

    def setup_menubar(self):
        self.menubar = self.menuBar()

        self.monitor_mode_menu = self.menubar.addMenu("Monitor mode")
        self.monitor_mode_menu.addAction("Start").triggered.connect(
            self.start_monitor_mode)
        self.monitor_mode_menu.addAction("Stop").triggered.connect(
            self.stop_monitor_mode)

        self.airodump_menu = self.menubar.addMenu("Airodump")
        self.airodump_menu.addAction("Start").triggered.connect(
            self.start_airodump)
        self.airodump_menu.addAction("Stop").triggered.connect(
            self.stop_airodump)

        self.client_menu = self.menubar.addMenu("Client")
        self.client_menu.setDisabled(True)
        self.client_menu.addAction("Start deauth").triggered.connect(
            self.start_deauth_client)
        self.client_menu.addAction("Stop deauth").triggered.connect(
            self.stop_deauth_client)
コード例 #24
0
class VectorDatabaseFrame(GenericFrame):
    def __init__(self, parent):
        super().__init__(QHBoxLayout(), 'Vector Database', parent)
        self.vectors = None
        self.vector_info = VectorInformationFrame()
        self.list = QListWidget()
        self.dialog = None

        self.buttons = QToolBar('Buttons')

        self.init_list()
        self.init_frame()

        self.list.itemClicked.connect(self.unsaved_dialog)
        self.vector_info.save_changes.clicked.connect(
            lambda: self.list.currentItem().setText(
                self.vector_info.currentVector.data.get("Name")))

    def init_frame(self):
        self.setFrameShape(QFrame.StyledPanel)
        self.init_buttons()

        self.layout.addWidget(self.list)
        self.layout.addWidget(self.vector_info)
        self.layout.addWidget(self.buttons)
        self.setMinimumSize(600, 400)

    def init_buttons(self):
        self.buttons.setOrientation(Qt.Vertical)
        self.buttons.setMovable(False)
        self.buttons.setStyleSheet("""
                    QToolBar {
                        spacing: 6px;
                        padding: 3px;
                    }
                """)

        # Buttons after this are set to the right side
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.buttons.addWidget(spacer)

        b1 = QPushButton('Add Vector')
        self.buttons.addWidget(b1)
        b1.clicked.connect(self.add_vector)

        b2 = QPushButton('Delete Vector')
        self.buttons.addWidget(b2)
        b2.clicked.connect(self.delete_vector_dialog)

        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.buttons.addWidget(spacer)

        b3 = QPushButton('Close')
        self.buttons.addWidget(b3)
        b3.clicked.connect(self.check_status)

    def init_list(self):
        self.vectors = get_vector_list()

        if self.vectors:
            for vector in self.vectors:
                self.list.addItem(vector.get("Name"))
            self.list.setCurrentItem(self.list.itemAt(0, 0))
            self.vector_info.update_frame(self.list.currentItem())

    def add_vector(self):
        v = Vector()
        self.vectors.append(v.data)

        self.list.addItem(self.vectors[-1].get("Name"))
        self.list.setCurrentItem(self.list.item(self.list.count() - 1))
        self.vector_info.update_frame(self.list.currentItem())

    def delete_vector_dialog(self):
        self.dialog = GenericWindow(QGridLayout())
        dialog_delete = QPushButton("Delete")
        dialog_cancel = QPushButton("Cancel")

        self.dialog.layout.addWidget(QLabel("Delete current vector?"), 0, 1)
        self.dialog.layout.addWidget(dialog_delete, 1, 0)
        self.dialog.layout.addWidget(dialog_cancel, 1, 2)

        dialog_delete.clicked.connect(self.dialog_confirm_delete)
        dialog_cancel.clicked.connect(self.dialog_cancel)
        self.dialog.show()

    def unsaved_dialog(self, vector, closeParent=None):
        if self.vector_info.save_changes.isEnabled():
            self.dialog = GenericWindow(QGridLayout(), self)
            dialog_confirm = QPushButton("Continue")
            dialog_cancel = QPushButton("Cancel")

            self.dialog.layout.addWidget(
                QLabel(
                    "Changes to current vector are pending.\nContinue without saving?"
                ), 0, 1)
            self.dialog.layout.addWidget(dialog_confirm, 1, 0)
            self.dialog.layout.addWidget(dialog_cancel, 1, 2)

            if closeParent is not None:
                dialog_confirm.clicked.connect(self.dialog_confirm_exit)
            else:
                dialog_confirm.clicked.connect(self.dialog_confirm_change)

            dialog_cancel.clicked.connect(self.dialog_cancel)
            self.dialog.show()
        else:
            self.vector_info.update_frame(vector)

    def dialog_confirm_exit(self):
        self.dialog.close()
        self.parentWidget().close()

    def dialog_confirm_change(self):
        self.vector_info.update_frame(self.list.currentItem())
        self.dialog.close()

    def dialog_confirm_delete(self):
        del_object("Name",
                   self.list.takeItem(self.list.currentRow()).text(), "Vector")
        if self.list.count() > 0:
            self.list.setCurrentItem(self.list.itemAt(0, 0))
            self.vector_info.update_frame(self.list.currentItem())
        else:
            self.vector_info.reset_frame()
        self.dialog.close()

    def dialog_cancel(self):
        self.list.setCurrentItem(
            self.list.findItems(
                self.vector_info.currentVector.data.get("Name"),
                Qt.MatchExactly)[0])
        self.dialog.close()

    def check_status(self):
        if not self.vector_info.save_changes.isEnabled():
            self.parentWidget().close()
        else:
            self.unsaved_dialog(self.vector_info.currentVector, True)
コード例 #25
0
class ActorWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        mainLayout = QHBoxLayout()
        mainLayout.setContentsMargins(0, 0, 0, 0)
        self._listWidget = QListWidget()
        self._listWidget.itemDoubleClicked.connect(self.onActorNameDblClicked)
        mainLayout.addWidget(self._listWidget)

        right = QVBoxLayout()
        right.setAlignment(Qt.AlignTop)
        self._newItem = QLineEdit()
        self._add = QPushButton('Add')
        self._add.clicked.connect(self.onAddClicked)
        self._remove = QPushButton('Remove')
        self._remove.clicked.connect(self.onRemoveClicked)
        right.addWidget(self._newItem)
        right.addWidget(self._add)
        right.addWidget(self._remove)
        mainLayout.addLayout(right)

        self.setLayout(mainLayout)

    def onActorNameDblClicked(self, item):
        self._newItem.setText(item.text())

    def onAddClicked(self):
        text = self._newItem.text().strip()
        if not len(text): return
        items = self._listWidget.findItems(text, Qt.MatchExactly)
        if (len(items) > 0):
            print('already exists actor name!')
            return

        self._listWidget.addItem(text)
        self._newItem.clear()
        QApplication.postEvent(
            self, QKeyEvent(QEvent.KeyPress, Qt.Key_Enter, Qt.NoModifier))

    def onRemoveClicked(self):
        if not self._listWidget.currentItem():
            return

        item = self._listWidget.currentItem()
        #print(item.text())
        self._listWidget.takeItem(self._listWidget.row(item))
        QApplication.postEvent(
            self, QKeyEvent(QEvent.KeyPress, Qt.Key_Enter, Qt.NoModifier))

    def getValue(self):
        vals = []
        for i in range(self._listWidget.count()):
            vals.append(self._listWidget.item(i).text())
        return ';'.join(vals)

    def setValue(self, value):
        self._listWidget.clear()
        if not value: return
        self._listWidget.insertItems(0, value.split(';'))

    Value = pyqtProperty(str, getValue, setValue)
コード例 #26
0
class DockElementPlotter(DockElement):

    selected = None
    registerKeySignal = pyqtSignal(str, str, str)

    __settings = {}

    def __init__(self, mainWindow, title):
        DockElement.__init__(self, mainWindow, 'Plotter')
        self.name = title
        self.setWindowTitle(title)
        self.setupUi()

    def setupUi(self):
        self.resize(500, 300)
        self.settingsWidget = QWidget()
        self.settingsWidget.setMinimumSize(900, 150)
        # Create the main widget as a container widget.
        self.mainWidget = QWidget(self)
        self.setWidget(self.mainWidget)
        mainLayout = QVBoxLayout(self.mainWidget)
        # Create 'Settings' dropdown button.
        self.settingsButton = QToolButton(self)
        self.settingsButton.setText("Settings")
        # Remove the menu indicator by applying a global style.
        self.settingsButton.setObjectName("noIndicator")
        # And replace it with a down arrow icon instead (which looks better).
        self.settingsButton.setArrowType(QtCore.Qt.DownArrow)
        self.settingsButton.setToolButtonStyle(
            QtCore.Qt.ToolButtonTextBesideIcon)
        # Set the popup that opens on button click.
        self.settingsButton.setPopupMode(QToolButton.InstantPopup)
        action = QWidgetAction(self.settingsButton)
        action.setDefaultWidget(self.settingsWidget)
        self.settingsButton.addAction(action)
        # Create a horizontal layout for the settings button.
        buttonLayout = QHBoxLayout()
        buttonLayout.addWidget(self.settingsButton)
        buttonLayout.addStretch()
        mainLayout.addLayout(buttonLayout)
        # Create the plotter widget.
        self.plotter = Qt5Plotter()
        self.plotter.setPlotterGUI(self)
        self.plotter.setMinimumSize(300, 200)
        self.plotter.plotLinear()
        mainLayout.addWidget(self.plotter)
        # Create the settings dialog.
        self.setupSettingsDialogUi()
        # Set keys that are plotted by default
        self.defaultKeys = {"TEST": ["loss"], "TRAIN": ["loss"]}

    def setupSettingsDialogUi(self):
        self.settingsDialogLayout = QGridLayout()
        # DropDown for plotAgainstTime or Iterations
        self.dbTimeIter = QComboBox()
        self.dbTimeIter.addItem("Iterations")
        self.dbTimeIter.addItem("Time")
        self.dbTimeIter.setFixedWidth(130)
        self.dbTimeIter.currentIndexChanged.connect(self.plotAgainstTimeIter)
        self.settingsDialogLayout.addWidget(self.dbTimeIter, 1, 3)
        # DropDown for plotting scale linear or logarithmic
        self.dbLinLoga = QComboBox()
        self.dbLinLoga.addItem("Linear")
        self.dbLinLoga.addItem("Logarithmic")
        self.dbLinLoga.setFixedWidth(130)
        self.dbLinLoga.currentIndexChanged.connect(self.plotLinLoga)
        self.settingsDialogLayout.addWidget(self.dbLinLoga, 3, 3)
        # Dynamic checkboxes
        # Key lists from parser that define the table rows for train and test
        self.test_dict_list = []
        self.train_dict_list = []
        # Lists for checkboxes for train and test
        self.test_cb = []
        self.train_cb = []
        # Text labels for better display
        testLabel = QLabel("Test keys:")
        trainLabel = QLabel("Train keys:")
        timeIterLabel = QLabel("Plot against:")
        LinLogaLabel = QLabel("Plot scale:")
        self.settingsDialogLayout.addWidget(testLabel, 0, 4)
        self.settingsDialogLayout.addWidget(trainLabel, 2, 4)
        self.settingsDialogLayout.addWidget(timeIterLabel, 0, 3)
        self.settingsDialogLayout.addWidget(LinLogaLabel, 2, 3)
        # create default Checkboxes
        self.createCheckboxes(self.test_dict_list, self.train_dict_list)
        # Button select Logfiles
        self.btn1 = QPushButton(
            QtGui.QIcon(QApplication.style().standardIcon(
                QStyle.SP_DialogOpenButton)), "")
        self.btn1.setFixedWidth(40)
        self.btn1.clicked.connect(self.getFiles)
        self.settingsDialogLayout.addWidget(self.btn1, 3, 0)
        # Button remove item
        self.btn2 = QPushButton(
            QtGui.QIcon(QApplication.style().standardIcon(
                QStyle.SP_TrashIcon)), "")
        self.btn2.setFixedWidth(40)
        self.btn2.clicked.connect(self.removeItem)
        self.settingsDialogLayout.addWidget(self.btn2, 3, 1)
        # Button CSV export
        self.btn3 = QPushButton("Export as CSV")
        self.btn3.setFixedWidth(110)
        self.btn3.clicked.connect(self.exportCSV)
        self.settingsDialogLayout.addWidget(self.btn3, 3, 2)
        # Create Logfile list
        self.leftlist = QListWidget()
        self.leftlist.setFixedSize(200, 100)
        self.Stack = QStackedWidget(self)
        self.settingsDialogLayout.addWidget(self.Stack, 0, 0, 3, 3)
        self.settingsDialogLayout.addWidget(self.leftlist, 0, 0, 3, 3)
        self.leftlist.currentRowChanged.connect(self.display)
        self.leftlist.currentItemChanged.connect(self.selectedLogItem)
        self.settingsWidget.setLayout(self.settingsDialogLayout)
        # Signal handling
        self.registerKeySignal.connect(self.registerKey)

    def btnstatetest(self, checkbox):
        """
        Plot the selected checkbox for Test-Data
        """
        if self.selected:
            self.plotter.showMetricTest(self.selected, checkbox.text(),
                                        checkbox.isChecked())
            self.displayPlot()
            self.__settings["checkBoxes"][self.selected]["TEST"][
                checkbox.text()] = checkbox.isChecked()

    def btnstatetrain(self, checkbox):
        """
        Plot the selected checkbox for Train-Data
        """
        if self.selected:
            self.plotter.showMetricTrain(self.selected, checkbox.text(),
                                         checkbox.isChecked())
            self.displayPlot()
            self.__settings["checkBoxes"][self.selected]["TRAIN"][
                checkbox.text()] = checkbox.isChecked()

    def displayPlot(self):
        """
        Update the plotter.
        """
        if self.selected:
            self.plotter.plot()
            self.plotter.show()
            self.createNecessaryDocks()

    def createNecessaryDocks(self):
        """
        Create the necessary docks if they don't exist.
        """
        if self.__settings is None:
            self.__settings = {}
        if "checkBoxes" not in self.__settings:
            self.__settings["checkBoxes"] = {}
        if self.selected not in self.__settings["checkBoxes"] or \
                isinstance(self.__settings["checkBoxes"][self.selected], list):
            self.__settings["checkBoxes"][self.selected] = {
                "TEST": {},
                "TRAIN": {}
            }

    def existsKeySelection(self):
        """
        Returns true if there is a selection by the user of keys to be plotted
        """
        if self.__settings is None:
            self.__settings = {}
        if "checkBoxes" not in self.__settings:
            self.__settings["checkBoxes"] = {}
        if self.selected not in self.__settings["checkBoxes"]:
            return False
        return True

    def createCheckboxes(self, test_list, train_list):
        """
        Dynamically creates checkboxes, which are in the lists: test_list and train_list
        """
        self.test_dict_list = test_list
        self.train_dict_list = train_list
        # As long as there are elements in TEST list: create Checkboxes
        if self.test_dict_list is not None:
            for i in range(len(self.test_dict_list)):
                self.test_cb.append(QCheckBox(self.test_dict_list[i], self))
                self.settingsDialogLayout.addWidget(self.test_cb[i], 1, 4 + i)
                self.test_cb[i].stateChanged.connect(
                    lambda checked, i=i: self.btnstatetest(self.test_cb[i]))
                if self.existsKeySelection() and self.test_cb[i].text() in \
                        self.__settings["checkBoxes"][self.selected]["TEST"]:
                    # Compatibility for older projects
                    try:
                        self.test_cb[i].setChecked(
                            self.__settings["checkBoxes"][self.selected]
                            ["TEST"].get(self.test_cb[i].text(), True))
                    except AttributeError:
                        self.test_cb[i].setChecked(True)
                else:
                    self.test_cb[i].setChecked(
                        self.test_cb[i].text() in self.defaultKeys["TEST"])
                self.btnstatetest(self.test_cb[i])

        # As long as there are elements in TRAIN list: create Checkboxes
        if self.train_dict_list is not None:
            for i in range(len(self.train_dict_list)):
                self.train_cb.append(QCheckBox(self.train_dict_list[i], self))
                self.settingsDialogLayout.addWidget(self.train_cb[i], 3, 4 + i)
                self.train_cb[i].stateChanged.connect(
                    lambda checked, i=i: self.btnstatetrain(self.train_cb[i]))
                if self.existsKeySelection() and self.train_cb[i].text() in \
                        self.__settings["checkBoxes"][self.selected]["TRAIN"]:
                    # Compatibility for older projects
                    try:
                        self.train_cb[i].setChecked(
                            self.__settings["checkBoxes"][self.selected]
                            ["TRAIN"].get(self.train_cb[i].text(), True))
                    except AttributeError:
                        self.train_cb[i].setChecked(True)

                else:
                    self.train_cb[i].setChecked(
                        self.train_cb[i].text() in self.defaultKeys["TRAIN"])
                self.btnstatetrain(self.train_cb[i])
        self.settingsWidget.setLayout(self.settingsDialogLayout)

    def display(self, i):
        self.Stack.setCurrentIndex(i)

    def loadFiles(self, filenames):
        """
        Loads every log file in the list of filenames and adds them to the list of logs.
        """
        for i in range(len(filenames)):
            f = filenames[i]
            if not os.path.exists(f):
                Log.error("External log not found: " + f,
                          Log.getCallerId("plotter"))
            else:
                parser = Parser(open(f, 'r'), OrderedDict(),
                                Log.getCallerId(str(f)))
                head, tail = os.path.split(str(f))
                logId = "external_" + tail
                logName = "[ext] " + tail
                self.putLog(logId, parser, logName=logName)
                parser.parseLog()

                # This is for saving the loaded logs in the project file
                # (create the necessary docks if they don't exist)
                if self.__settings is None:
                    self.__settings = {"logFiles": {logId: f}}
                elif "logFiles" not in self.__settings:
                    self.__settings["logFiles"] = {logId: f}
                else:
                    self.__settings["logFiles"][logId] = f

    def getFiles(self):
        """
        Dialog for adding different data files to list for plotting
        """
        dlg = QFileDialog()
        dlg.setFileMode(QFileDialog.ExistingFiles)
        if dlg.exec_():
            self.loadFiles(dlg.selectedFiles())
        self.selectLast()

    def plotAgainstTimeIter(self):
        if self.dbTimeIter.currentText() == "Time":
            self.plotter.plotAgainstTime()
            self.__settings["againstTime"] = "Time"
            self.plotter.plot()
        else:
            if self.dbTimeIter.currentText() == "Iterations":
                self.plotter.plotAgainstIterations()
                self.__settings["againstTime"] = "Iterations"
                self.plotter.plot()

    def plotLinLoga(self):
        if self.dbLinLoga.currentText() == "Linear":
            self.plotter.plotLinear()
            self.__settings["logarithmic"] = "Linear"
            self.plotter.plot()
        else:
            if self.dbLinLoga.currentText() == "Logarithmic":
                self.plotter.plotLogarithmic()
                self.__settings["logarithmic"] = "Logarithmic"
                self.plotter.plot()

    def putLog(self, logId, parser, plotOnUpdate=False, logName=None):
        """
        Add a log (i.e. parser) to the list of shown logs. If a log with the
        log name already exists primes are added to the name until it is unique.
        """
        for i in range(self.leftlist.count()):
            if logId == self.leftlist.item(i).data(Qt.UserRole):
                return
        if not logName:
            logName = logId
        self.test_dict_list = []
        self.train_dict_list = []
        while self.leftlist.findItems(logName, Qt.MatchExactly):
            logName += "'"
        self.plotter.putLog(logId, logName, parser, plotOnUpdate)
        logItem = QListWidgetItem(logName)
        v = QVariant(logId)
        logItem.setData(Qt.UserRole, v)
        self.leftlist.insertItem(self.leftlist.count(), logItem)
        self.selectLast()

    def removeLog(self, logId, logName):
        try:
            self.__settings["checkBoxes"].pop(logId)
        except KeyError:
            pass
        self.plotter.removeLog(logId)
        item = self.leftlist.findItems(logName, Qt.MatchExactly)
        if item:
            row = self.leftlist.row(item[0])
            self.leftlist.takeItem(row)
            self.test_dict_list = []
            self.train_dict_list = []
            self.selectLast()
            self.plotter.plot()
            self.plotter.show()

    def registerKey(self, logId, phase, key):
        """
        Registers the key given by the signal. This is only of interest if the key belongs
        to the selected log file. A new checkbox is then created for that key.
        """
        if self.leftlist.currentItem():
            if str(self.leftlist.currentItem().data(Qt.UserRole)) == logId:
                if phase == "TEST":
                    self.test_dict_list.append(key)
                if phase == "TRAIN":
                    self.train_dict_list.append(key)
                self.updateCheckboxes(self.test_dict_list,
                                      self.train_dict_list)

    def removeItem(self):
        """
        Removes the selected item from the list and also from plot.
        """
        item = self.leftlist.takeItem(self.leftlist.currentRow())
        if item is not None:
            self.removeLog(item.data(Qt.UserRole), item.text())
            self.displayPlot()
            self.selectLast()
            if (self.__settings is not None and "logFiles" in self.__settings
                    and str(item.data(
                        Qt.UserRole)) in self.__settings["logFiles"]):
                del self.__settings["logFiles"][str(item.data(Qt.UserRole))]

    def selectLast(self):
        if len(self.leftlist) > 0:
            self.leftlist.setCurrentItem(
                self.leftlist.item(len(self.leftlist) - 1))
            self.selectedLogItem(self.leftlist.item(len(self.leftlist) - 1))
        else:
            self.test_dict_list = []
            self.train_dict_list = []
            self.updateCheckboxes(self.test_dict_list, self.train_dict_list)

    def selectedLogItem(self, item, update=True):
        """
        Memorize which logfile is selected, block signals, and asks plotter if the checkbox is
        selected or not.
        """
        if item:
            if update:
                self.updateCheckboxes(
                    list(
                        self.plotter.getParser(str(item.data(
                            Qt.UserRole))).getKeys('TEST')),
                    list(
                        self.plotter.getParser(str(item.data(
                            Qt.UserRole))).getKeys('TRAIN')))
            self.selected = str(str(item.data(Qt.UserRole)))
            self.tickBoxes()
            self.__settings["selectedIndex"] = self.leftlist.row(item)

    def updateCheckboxes(self, test_list, train_list):
        """
        Clear all checkboxes and clear the layer and create new checkboxes and
        add them to the layer again
        """
        for i in range(len(self.test_cb)):
            self.settingsDialogLayout.removeWidget(self.test_cb[i])
            self.test_cb[i].deleteLater()
            self.test_cb[i] = None

        for i in range(len(self.train_cb)):
            self.settingsDialogLayout.removeWidget(self.train_cb[i])
            self.train_cb[i].deleteLater()
            self.train_cb[i] = None

        self.test_cb = []
        self.train_cb = []
        # create new Checkboxes with new Keys
        self.createCheckboxes(test_list, train_list)
        self.tickBoxes()

    def tickBoxes(self):
        """
        Whether a plot is shown is stored in the plotter object. This method
        ticks the checkboxes of the keys whose plot is shown in the plotter.
        """
        for i in range(len(self.test_cb)):
            self.test_cb[i].blockSignals(True)
            self.test_cb[i].setChecked(
                self.plotter.isMetricTestShown(self.selected,
                                               self.test_cb[i].text()))
            self.test_cb[i].blockSignals(False)
        for i in range(len(self.train_cb)):
            self.train_cb[i].blockSignals(True)
            self.train_cb[i].setChecked(
                self.plotter.isMetricTrainShown(self.selected,
                                                self.train_cb[i].text()))
            self.train_cb[i].blockSignals(False)

    def setProject(self, project):
        """
        This sets the project object. The plotter settings have the following structure:

        "plotter": {
                   "checkBoxes": {
                       "logName1": {
                           "TEST": {
                                "accuracy": true,
                                "loss": false
                            },
                            "TRAIN": {
                                "LearningRate": true,
                                "loss": false
                            }
                       },
                       "logName2": {
                           ...
                       },
                       ...
                   },
                   "selectedIndex": 0,
                   "againstTime": "Iterations",
                   "logarithmic": "Logarithmic"
               }

        """

        if "plotter" not in project.getSettings():
            project.getSettings()["plotter"] = self.__settings
        else:
            self.__settings = project.getSettings()["plotter"]
        selectedIndex = -1
        if "selectedIndex" in self.__settings:
            selectedIndex = self.__settings["selectedIndex"]
        if "logFiles" in self.__settings:
            self.loadFiles(list(self.__settings["logFiles"].values()))
        if "checkBoxes" in self.__settings:
            for name in self.__settings["checkBoxes"]:
                for key in self.__settings["checkBoxes"][name]["TEST"]:
                    # Compatibility for older projects
                    try:
                        self.plotter.showMetricTest(
                            name, key, self.__settings["checkBoxes"][name]
                            ["TEST"].get(key))
                    except AttributeError:
                        self.plotter.showMetricTest(name, key, True)
                for key in self.__settings["checkBoxes"][name]["TRAIN"]:
                    # Compatibility for older projects
                    try:
                        self.plotter.showMetricTrain(
                            name, key, self.__settings["checkBoxes"][name]
                            ["TRAIN"].get(key))
                    except AttributeError:
                        self.plotter.showMetricTrain(name, key, True)
        if "againstTime" in self.__settings:
            index = self.dbTimeIter.findText(self.__settings["againstTime"],
                                             Qt.MatchFixedString)
            if index >= 0:
                self.dbTimeIter.setCurrentIndex(index)
        if "logarithmic" in self.__settings:
            index = self.dbLinLoga.findText(self.__settings["logarithmic"],
                                            Qt.MatchFixedString)
            if index >= 0:
                self.dbLinLoga.setCurrentIndex(index)
        #for sid in project.getSessions():
        #    session = project.getSession(sid)
        #    self.putLog(str(session.getLogId()), session.getParser(), True, str(session.getLogFileName(True)))
        self.plotter.plot()
        if selectedIndex >= 0:
            self.leftlist.setCurrentItem(self.leftlist.item(selectedIndex))
            self.selectedLogItem(self.leftlist.item(selectedIndex), False)
        else:
            self.selectLast()
        # Delete plot if session is deleted
        project.deleteSession.connect(lambda sid: self.removeLog(
            str(project.getSession(sid).getLogId()),
            str(project.getSession(sid).getLogFileName(True))))
        project.deleteSession.connect(
            lambda sid: project.deletePlotterSettings(
                str(project.getSession(sid).getLogId())))
        project.resetSession.connect(lambda sid: self.removeLog(
            str(project.getSession(sid).getLogId()),
            str(project.getSession(sid).getLogFileName(True))))
        project.resetSession.connect(lambda sid: project.deletePlotterSettings(
            str(project.getSession(sid).getLogId())))

    def exportCSV(self):
        """
        Opens a file dialog and the plotter saves the shown plots to the selected file.
        The file dialog only appears if there is at least one plot.
        """
        if self.plotter.numGraphs() == 0:
            QMessageBox.question(self, 'PyQt5 message', "Nothing selected!",
                                 QMessageBox.Ok, QMessageBox.Ok)
        else:
            path = str(
                QFileDialog.getSaveFileName(
                    filter="CSV table (*.csv) ;; Text file (*.txt)")[0])
            if path:
                if not (path.endswith(".csv") or path.endswith(".txt")):
                    path = path + ".csv"
                self.plotter.exportCSVToFile(path)
コード例 #27
0
class BotManagerView(QWidget):
    def __init__(self, parent, botlist):
        super(QWidget, self).__init__(parent)
        self.innerLayout = QVBoxLayout()
        self.groupBox = QGroupBox('Saved Bots')
        self.layout = QVBoxLayout(self)
        self.botListView = QListWidget(self)
        self.botListView.setWindowTitle('Your saved bots')
        self.botListView.itemSelectionChanged.connect(self.loadBot)
        self.botListView.itemChanged.connect(self.getSelectedBots)
        self.removeButton = QPushButton('Remove')
        self.removeButton.setEnabled(False)
        self.compareButton = QPushButton('Select 2 Bots to Compare')
        self.compareButton.setEnabled(False)
        self.botManager = BotManager(self)
        self.parent = parent

        self.botList = {}
        self.compareList = dict()
        self._setup(botlist)

        self.error_msg = QLabel()
        self.error_msg.setWordWrap(True)
        self.error_msg.setAlignment(QtCore.Qt.AlignLeft)

        self.innerLayout.addWidget(self.botListView)
        self.innerLayout.addWidget(self.removeButton)
        self.innerLayout.addWidget(self.compareButton)
        self.innerLayout.addWidget(self.error_msg)
        self.groupBox.setLayout(self.innerLayout)
        self.layout.addWidget(self.groupBox)
        self.setLayout(self.layout)

        self.windows = []

    def _setup(self, botlist):
        for b in botlist:
            self.botList[b] = b
            item = QListWidgetItem(b)
            item.setCheckState(False)
            item.setText(b)
            self.botListView.addItem(item)

        self.removeButton.clicked.connect(self.removeButtonPressed)
        self.compareButton.clicked.connect(self.compareButtonPressed)

    def refresh(self):
        self.botListView.clear()
        self.compareList.clear()
        self.compareButton.setText('Select 2 Bots to Compare')
        self.compareButton.setEnabled(False)
        self.removeButton.setEnabled(False)
        for b in self.botList:
            item = QListWidgetItem(b)
            item.setCheckState(False)
            item.setText(b)
            self.botListView.addItem(item)

    def loadBot(self):
        if self.botListView.currentItem() is None:
            return
        alias = self.botListView.currentItem().text()
        self.parent.loadSavedBot(alias)

    def addBot(self, alias):
        self.botList[alias] = alias
        self.refresh()

    def getSelected(self):
        item = self.botListView.currentItem()
        if item is not None:
            return item.text()
        return ''

    def removeButtonPressed(self):
        count = len(self.compareList)
        result = QMessageBox.question(
            self, "Delete Bots",
            "Are you sure you want to delete " + str(count) + " bots?",
            QMessageBox.Yes, QMessageBox.No)
        if result == QMessageBox.Yes:
            self._removeManyBots()

    def _removeSingleBot(self):
        item = self.botListView.currentItem()
        if item is not None:
            alias = item.text()
            self.parent.deleteBot(alias)
            self.botList.pop(alias)
            self.refresh()

    def _removeManyBots(self):
        for item in self.compareList:
            self.parent.deleteBot(item)
            self.botList.pop(item)
        self.refresh()

    def setSelection(self, name):
        item = self.botListView.findItems(name, Qt.MatchExactly)
        if len(item) > 0:
            self.botListView.setCurrentItem(item[0])

    def buttonPressed(self):
        result = QMessageBox.question(
            self, "Delete Bot", "Are you sure you want to delete this bot?",
            QMessageBox.Yes, QMessageBox.No)
        if result == QMessageBox.Yes:
            self._removeSingleBot()

    def getSelectedBots(self, item):
        alias = item.text()
        if alias not in self.compareList:
            self.compareList[alias] = alias
        elif alias in self.compareList:
            del self.compareList[alias]
        if len(self.compareList) == 2:
            self.compareButton.setText('Compare Bots')
            self.compareButton.setEnabled(True)
        else:
            self.compareButton.setText('Select 2 Bots to Compare')
            self.compareButton.setEnabled(False)
        if len(self.compareList) > 0:
            self.removeButton.setEnabled(True)
        else:
            self.removeButton.setEnabled(False)

    def displayMessage(self, msg, colour='black'):
        self.error_msg.setText(msg)
        self.error_msg.setStyleSheet('color: ' + colour)

    def removeMessage(self):
        self.error_msg.setText('')
        self.error_msg.setStyleSheet('color: black')

    def compareButtonPressed(self):
        self.removeMessage()
        if len(self.compareList) != 2:
            msgBox = QMessageBox()
            msgBox.setIcon(QMessageBox.Warning)
            msgBox.setText(
                "Invalid number of bots selected - Please select 2 bots")
            msgBox.setWindowTitle("Compare Bots")
            msgBox.setStandardButtons(QMessageBox.Ok)
            msgBox.exec()
            return

        startDate, endDate, coin, ok = ComparisonInputDialog.getUserInput(self)

        if ok:
            bot1 = list(self.compareList.keys())[0]
            bot2 = list(self.compareList.keys())[1]
            self.botOneObject = self.botManager.loadBot(bot1)
            self.botTwoObject = self.botManager.loadBot(bot2)
            self.botOneObject.changeParam('Start Trading Date', startDate)
            self.botOneObject.changeParam('End Trading Date', endDate)
            self.botTwoObject.changeParam('Start Trading Date', startDate)
            self.botTwoObject.changeParam('End Trading Date', endDate)
            if not self.checkBot(self.botOneObject):
                return
            if not self.checkBot(self.botTwoObject):
                return
            try:
                self.data = fetchData.getData(exchange="Gemini",
                                              resolution="day",
                                              coin=coin)
                buySellDataOne = self.botOneObject.processHistoricalData(
                    self.data)
                buySellDataTwo = self.botTwoObject.processHistoricalData(
                    self.data)
                window = CompareWindow(self.data, bot1, bot2, buySellDataOne,
                                       buySellDataTwo, coin, self.botManager)
                self.windows.append(window)
                window.show()
            except IndexError as IE:
                self.displayMessage('Please select an appropriate date range',
                                    'red')

    def checkBot(self, bot):
        try:
            bot.checkParameters()
            return True
        except exceptions.InvalidStartEndDates as NE:
            self.displayMessage(
                'Invalid Input - Please give appropriate start and end dates',
                'red')
        except exceptions.InvalidMovingAvgs as II:
            self.displayMessage(
                'Invalid Input - Please give appropriate moving day averages',
                'red')
        except exceptions.InvalidDays as ID:
            self.displayMessage(
                'Invalid input - Please give a positive number of consecutive days',
                'red')
        except exceptions.InvalidIntervals as IV:
            self.displayMessage(
                'Invalid input - Please give appropriate interval values',
                'red')
        except exceptions.InvalidThresholds as IT:
            self.displayMessage(
                'Invalid Input - Please give appropriate threshold values',
                'red')
        return False
コード例 #28
0
class outlineBasics(QAbstractItemView):
    def __init__(self, parent=None):
        pass

    def getSelection(self):
        sel = []
        for i in self.selectedIndexes():
            if i.column() != 0:
                continue
            if not i in sel:
                sel.append(i)
        return sel

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.RightButton:
            self.menu = self.makePopupMenu()
            self.menu.popup(event.globalPos())
        else:
            QAbstractItemView.mouseReleaseEvent(self, event)

    def makePopupMenu(self):
        index = self.currentIndex()
        sel = self.getSelection()
        clipboard = qApp.clipboard()

        menu = QMenu(self)

        # Add / remove items
        self.actOpen = QAction(QIcon.fromTheme("go-right"),
                               qApp.translate("outlineBasics", "Open Item"),
                               menu)
        self.actOpen.triggered.connect(self.openItem)
        menu.addAction(self.actOpen)

        menu.addSeparator()

        # Add / remove items
        self.actAddFolder = QAction(
            QIcon.fromTheme("folder-new"),
            qApp.translate("outlineBasics", "New Folder"), menu)
        self.actAddFolder.triggered.connect(self.addFolder)
        menu.addAction(self.actAddFolder)

        self.actAddText = QAction(QIcon.fromTheme("document-new"),
                                  qApp.translate("outlineBasics", "New Text"),
                                  menu)
        self.actAddText.triggered.connect(self.addText)
        menu.addAction(self.actAddText)

        self.actDelete = QAction(QIcon.fromTheme("edit-delete"),
                                 qApp.translate("outlineBasics", "Delete"),
                                 menu)
        self.actDelete.triggered.connect(self.delete)
        menu.addAction(self.actDelete)

        menu.addSeparator()

        # Copy, cut, paste
        self.actCopy = QAction(QIcon.fromTheme("edit-copy"),
                               qApp.translate("outlineBasics", "Copy"), menu)
        self.actCopy.triggered.connect(self.copy)
        menu.addAction(self.actCopy)

        self.actCut = QAction(QIcon.fromTheme("edit-cut"),
                              qApp.translate("outlineBasics", "Cut"), menu)
        self.actCut.triggered.connect(self.cut)
        menu.addAction(self.actCut)

        self.actPaste = QAction(QIcon.fromTheme("edit-paste"),
                                qApp.translate("outlineBasics", "Paste"), menu)
        self.actPaste.triggered.connect(self.paste)
        menu.addAction(self.actPaste)

        menu.addSeparator()

        # POV
        self.menuPOV = QMenu(qApp.translate("outlineBasics", "Set POV"), menu)
        mw = mainWindow()
        a = QAction(QIcon.fromTheme("dialog-no"),
                    qApp.translate("outlineBasics", "None"), self.menuPOV)
        a.triggered.connect(lambda: self.setPOV(""))
        self.menuPOV.addAction(a)
        self.menuPOV.addSeparator()

        menus = []
        for i in [
                qApp.translate("outlineBasics", "Main"),
                qApp.translate("outlineBasics", "Secondary"),
                qApp.translate("outlineBasics", "Minor")
        ]:
            m = QMenu(i, self.menuPOV)
            menus.append(m)
            self.menuPOV.addMenu(m)

        mpr = QSignalMapper(self.menuPOV)
        for i in range(mw.mdlCharacter.rowCount()):
            a = QAction(mw.mdlCharacter.icon(i), mw.mdlCharacter.name(i),
                        self.menuPOV)
            a.triggered.connect(mpr.map)
            mpr.setMapping(a, int(mw.mdlCharacter.ID(i)))

            imp = toInt(mw.mdlCharacter.importance(i))

            menus[2 - imp].addAction(a)

        mpr.mapped.connect(self.setPOV)
        menu.addMenu(self.menuPOV)

        # Status
        self.menuStatus = QMenu(qApp.translate("outlineBasics", "Set Status"),
                                menu)
        # a = QAction(QIcon.fromTheme("dialog-no"), qApp.translate("outlineBasics", "None"), self.menuStatus)
        # a.triggered.connect(lambda: self.setStatus(""))
        # self.menuStatus.addAction(a)
        # self.menuStatus.addSeparator()

        mpr = QSignalMapper(self.menuStatus)
        for i in range(mw.mdlStatus.rowCount()):
            a = QAction(mw.mdlStatus.item(i, 0).text(), self.menuStatus)
            a.triggered.connect(mpr.map)
            mpr.setMapping(a, i)
            self.menuStatus.addAction(a)
        mpr.mapped.connect(self.setStatus)
        menu.addMenu(self.menuStatus)

        # Labels
        self.menuLabel = QMenu(qApp.translate("outlineBasics", "Set Label"),
                               menu)
        mpr = QSignalMapper(self.menuLabel)
        for i in range(mw.mdlLabels.rowCount()):
            a = QAction(
                mw.mdlLabels.item(i, 0).icon(),
                mw.mdlLabels.item(i, 0).text(), self.menuLabel)
            a.triggered.connect(mpr.map)
            mpr.setMapping(a, i)
            self.menuLabel.addAction(a)
        mpr.mapped.connect(self.setLabel)
        menu.addMenu(self.menuLabel)

        menu.addSeparator()

        # Custom icons
        self.menuCustomIcons = QMenu(
            qApp.translate("outlineBasics", "Set Custom Icon"), menu)
        a = QAction(qApp.translate("outlineBasics", "Restore to default"),
                    self.menuCustomIcons)
        a.triggered.connect(lambda: self.setCustomIcon(""))
        self.menuCustomIcons.addAction(a)
        self.menuCustomIcons.addSeparator()

        txt = QLineEdit()
        txt.textChanged.connect(self.filterLstIcons)
        txt.setPlaceholderText("Filter icons")
        txt.setStyleSheet("background: transparent; border: none;")
        act = QWidgetAction(self.menuCustomIcons)
        act.setDefaultWidget(txt)
        self.menuCustomIcons.addAction(act)

        self.lstIcons = QListWidget()
        for i in customIcons():
            item = QListWidgetItem()
            item.setIcon(QIcon.fromTheme(i))
            item.setData(Qt.UserRole, i)
            item.setToolTip(i)
            self.lstIcons.addItem(item)
        self.lstIcons.itemClicked.connect(self.setCustomIconFromItem)
        self.lstIcons.setViewMode(self.lstIcons.IconMode)
        self.lstIcons.setUniformItemSizes(True)
        self.lstIcons.setResizeMode(self.lstIcons.Adjust)
        self.lstIcons.setMovement(self.lstIcons.Static)
        self.lstIcons.setStyleSheet(
            "background: transparent; background: none;")
        self.filterLstIcons("")
        act = QWidgetAction(self.menuCustomIcons)
        act.setDefaultWidget(self.lstIcons)
        self.menuCustomIcons.addAction(act)

        menu.addMenu(self.menuCustomIcons)

        # Disabling stuff
        if len(sel) > 0 and index.isValid() and not index.internalPointer().isFolder() \
                or not clipboard.mimeData().hasFormat("application/xml"):
            self.actPaste.setEnabled(False)

        if len(sel) > 0 and index.isValid(
        ) and not index.internalPointer().isFolder():
            self.actAddFolder.setEnabled(False)
            self.actAddText.setEnabled(False)

        if len(sel) == 0:
            self.actOpen.setEnabled(False)
            self.actCopy.setEnabled(False)
            self.actCut.setEnabled(False)
            self.actDelete.setEnabled(False)
            self.menuPOV.setEnabled(False)
            self.menuStatus.setEnabled(False)
            self.menuLabel.setEnabled(False)
            self.menuCustomIcons.setEnabled(False)

        return menu

    def openItem(self):
        idx = self.currentIndex()
        from manuskript.functions import MW
        MW.openIndex(idx)

    def addFolder(self):
        self.addItem("folder")

    def addText(self):
        self.addItem("text")

    def addItem(self, _type="folder"):
        if len(self.selectedIndexes()) == 0:
            parent = self.rootIndex()
        else:
            parent = self.currentIndex()

        if _type == "text":
            _type = settings.defaultTextType

        item = outlineItem(title=qApp.translate("outlineBasics", "New"),
                           _type=_type)
        self.model().appendItem(item, parent)

    def copy(self):
        mimeData = self.model().mimeData(
            self.selectionModel().selectedIndexes())
        qApp.clipboard().setMimeData(mimeData)

    def paste(self):
        index = self.currentIndex()
        if len(self.getSelection()) == 0:
            index = self.rootIndex()
        data = qApp.clipboard().mimeData()
        self.model().dropMimeData(data, Qt.CopyAction, -1, 0, index)

    def cut(self):
        self.copy()
        self.delete()

    def delete(self):
        self.model().removeIndexes(self.getSelection())

    def setPOV(self, POV):
        for i in self.getSelection():
            self.model().setData(i.sibling(i.row(), Outline.POV.value),
                                 str(POV))

    def setStatus(self, status):
        for i in self.getSelection():
            self.model().setData(i.sibling(i.row(), Outline.status.value),
                                 str(status))

    def setLabel(self, label):
        for i in self.getSelection():
            self.model().setData(i.sibling(i.row(), Outline.label.value),
                                 str(label))

    def setCustomIcon(self, customIcon):
        for i in self.getSelection():
            item = i.internalPointer()
            item.setCustomIcon(customIcon)

    def setCustomIconFromItem(self, item):
        icon = item.data(Qt.UserRole)
        self.setCustomIcon(icon)
        self.menu.close()

    def filterLstIcons(self, text):
        for l in self.lstIcons.findItems("", Qt.MatchContains):
            l.setHidden(not text in l.data(Qt.UserRole))
コード例 #29
0
ファイル: selection_panel.py プロジェクト: zyktrcn/ipp-crypto
class SelectionPanel(QWidget):
    autobuild = QtCore.pyqtSignal()

    def __init__(self, settings):
        super().__init__()
        self.settings = settings

        # Initializing GUI elements
        self.domains_list = QComboBox(self)
        self.search = QLineEdit(self)
        self.functions_list = QListWidget(self)
        self.autobuild_button = QPushButton('Autobuild')

        # Preparing elements by giving initial values, etc
        self.setMinimumHeight(500)
        self.search.setPlaceholderText('Search...')

        # Setting all widgets in their places
        layout = QVBoxLayout()
        layout.addWidget(self.domains_list)
        layout.addWidget(self.search)
        layout.addWidget(self.functions_list)
        layout.addWidget(self.autobuild_button)
        self.setLayout(layout)

        self.domains_list.activated[str].connect(self.on_select_domain)
        self.search.textEdited.connect(self.on_search)
        self.autobuild_button.clicked.connect(self.on_autobuild)
        self.settings.package_changed.connect(self.init_selection_panel)
        self.settings.tl_changed.connect(self.refresh)

    def init_selection_panel(self):
        if not self.settings.package.broken:
            self.functions_dict = copy.deepcopy(
                self.settings.package.functions)
            self.search.setEnabled(True)
            self.refresh()
        else:
            self.search.setEnabled(False)
            self.reset()

    def refresh(self):
        self.domains_type = (self.settings.package.type
                             if not self.settings.tl_group.isChecked() else
                             utils.THREADING_LAYER)
        domains_list = self.functions_dict[self.domains_type].keys()
        self.set_widget_items(self.domains_list, domains_list)
        self.on_select_domain()

    def on_select_domain(self):
        self.current_domain = self.domains_list.currentText()
        self.on_search(self.search.text())

    def on_search(self, search_request):
        self.set_widget_items(self.functions_list, [
            entry for entry in self.functions_dict[self.domains_type]
            [self.current_domain] if search_request.upper() in entry.upper()
        ])

    def on_autobuild(self):
        self.autobuild.emit()

    def set_widget_items(self, widget, items):
        """
        Adds items to widget
        :param widget: widget
        :param items: list of strings
        """
        widget.clear()
        widget.addItems(items)

    def reset(self):
        self.domains_list.clear()
        self.functions_list.clear()

    def add_function(self, function):
        """
        Adds new function to required list

        :param function: name if function
        """
        domain_type, domain, index = self.find_function(function)
        self.functions_dict[domain_type][domain].insert(index, function)
        if domain == self.current_domain:
            self.functions_list.insertItem(index, QListWidgetItem(function))
            self.on_search(self.search.text())

    def remove_function(self, function):
        """
        Removes function from left list
        """
        domain_type, domain, index = self.find_function(function)
        self.functions_dict[domain_type][domain].remove(function)
        if self.current_domain == domain:
            item = self.functions_list.findItems(function,
                                                 QtCore.Qt.MatchExactly)
            if item:
                self.functions_list.takeItem(self.functions_list.row(item[0]))

    def find_function(self, function_name):
        previous_domain = ''
        initial_functions_dict = self.settings.package.functions

        for domain_type, domain, function in utils.walk_dict(
                initial_functions_dict):
            if domain != previous_domain:
                index = 0

            if function_name == function:
                return domain_type, domain, index
            elif function in self.functions_dict[domain_type][domain]:
                previous_domain = domain
                index = self.functions_dict[domain_type][domain].index(
                    function) + 1
コード例 #30
0
class DownloadedPage(QWidget):
    send_trigger = pyqtSignal(dict)

    def __init__(self, parent=None):
        super(DownloadedPage, self).__init__(parent)
        self.data_path = '../../../data/downloaded.json'
        self.rm_file_check = True
        self.downloaded_list_widget = QListWidget(self)
        self.downloaded_list_widget.setStyleSheet("QListWidget{border:none;}")
        self.downloaded_list_widget.setSpacing(2)
        self.downloaded_list_widget.setGeometry(QRect(10, 20, 950, 800))
        self.init_ui()

    def init_ui(self):
        downloaded_dict_list = self.load_downloaded_dict_list()
        if not downloaded_dict_list:
            return
        for downloaded_dict in downloaded_dict_list:
            self.add_downloaded_item_to_widget(downloaded_dict)

    def add_downloaded_item(self, downloaded_dict):
        downloaded_dict['date'] = time.strftime('%Y-%m-%d %H:%M:%S',
                                                time.localtime(time.time()))
        self.add_downloaded_item_to_widget(downloaded_dict)
        self.add_downloaded_item_to_data(downloaded_dict)

    def add_downloaded_item_to_widget(self, downloaded_dict):
        item = QListWidgetItem()
        item.setText(downloaded_dict['id'])
        item.setSizeHint(QSize(608, 86))
        self.downloaded_list_widget.addItem(item)
        self.downloaded_list_widget.setItemWidget(
            item, self.create_downloaded_item(downloaded_dict))

    def add_downloaded_item_to_data(self, downloaded_dict):
        downloaded_dict_list = self.load_downloaded_dict_list()
        downloaded_dict_list.append(downloaded_dict)
        self.write_data(downloaded_dict_list)

    def load_downloaded_dict_list(self):
        if not os.path.exists(self.data_path):
            os.makedirs('../../../data', exist_ok=True)
            return []
        with open(self.data_path, 'r', encoding='utf-8') as f:
            downloaded_dict_list = json.load(f)
            return downloaded_dict_list

    def create_downloaded_item(self, downloaded_dict):
        book_name = downloaded_dict['file_name']
        item_widget = QWidget()
        item_widget.setObjectName(f'{downloaded_dict["id"]}')
        hbox_layout = QHBoxLayout()

        item_label = QLabel()
        pixmap, push = self.get_icon_by_file_type(book_name)
        item_label.setPixmap(pixmap)
        hbox_layout.addWidget(item_label)

        vlayout = QVBoxLayout()
        file_name_label = QLabel(book_name)
        vlayout.addWidget(file_name_label)

        h_in_v_item_layout = QHBoxLayout()
        h_in_v_item_layout.addWidget(QLabel(downloaded_dict['size']),
                                     Qt.AlignRight)

        downloaded_date_label = QLabel()
        downloaded_date_label.setObjectName(f'{downloaded_dict["id"]}_date')
        downloaded_date_label.setText(f'{downloaded_dict["date"]}')
        h_in_v_item_layout.addWidget(downloaded_date_label, Qt.AlignRight)

        vlayout.addLayout(h_in_v_item_layout)
        hbox_layout.addLayout(vlayout)

        file_path = f'../../../downloads/{book_name}'

        if os.path.exists(file_path):
            del_btn = QPushButton()
            del_btn.setIcon(QIcon('delete.png'))
            del_btn.setStyleSheet("QPushButton{border:none}")
            del_btn.clicked.connect(
                lambda: self.remove_task(downloaded_dict["id"], file_path))

            if push:
                send_btn = QPushButton()
                send_btn.setIcon(QIcon('send.png'))
                send_btn.setStyleSheet("QPushButton{border:none}")
                downloaded_dict['file_path'] = file_path
                send_btn.clicked.connect(
                    lambda: self.send_to_kindle(downloaded_dict))
                hbox_layout.addWidget(send_btn)

            hbox_layout.addWidget(del_btn)
        else:
            download_btn = QPushButton()
            download_btn.setIcon(QIcon('download_btn.png'))
            download_btn.setStyleSheet("QPushButton{border:none}")
            download_btn.clicked.connect(lambda: self.regain(downloaded_dict))
            hbox_layout.addWidget(download_btn)

        item_widget.setLayout(hbox_layout)
        item_widget_style_sheet = f'#{downloaded_dict["id"]}{{background-color:rgb(241,231,230);color:rgb(210,10,10)}}'
        item_widget.setStyleSheet(item_widget_style_sheet)
        return item_widget

    def remove_task(self, id, file_path):
        if self.remove_file_or_not():
            if self.rm_file_check:
                self.remove_file(file_path)
            self.remove_task_from_list_widget(id)
            self.remove_task_from_data(id)

    def remove_file_or_not(self):
        rm_file_checkbox = QCheckBox('同时删除源文件')
        rm_file_checkbox.setChecked(True)
        rm_file_checkbox.stateChanged.connect(self.checkbox_check)
        message_box = QMessageBox()
        message_box.setIcon(QMessageBox.Warning)
        message_box.setText("您确定要删除此任务吗?")
        sure = message_box.addButton('确定', QMessageBox.AcceptRole)
        cancel = message_box.addButton('取消', QMessageBox.RejectRole)
        message_box.setDefaultButton(sure)
        message_box.setCheckBox(rm_file_checkbox)
        reply = message_box.exec()
        if reply == QMessageBox.AcceptRole:
            return True
        return False

    def checkbox_check(self, state):
        check = self.sender()
        if state == Qt.Unchecked:
            self.rm_file_check = False
        else:
            self.rm_file_check = True

    def remove_task_from_data(self, id):
        downloaded_dict_list = self.load_downloaded_dict_list()
        for downloaded_dict in downloaded_dict_list:
            if id in downloaded_dict['id']:
                downloaded_dict_list.remove(downloaded_dict)
        self.write_data(downloaded_dict_list)

    def write_data(self, downloaded_dict_list):
        with open(self.data_path, 'w') as f:
            json.dump(downloaded_dict_list, f)

    def remove_task_from_list_widget(self, id):
        item = self.downloaded_list_widget.findItems(id, Qt.MatchExactly)[0]
        self.downloaded_list_widget.takeItem(
            self.downloaded_list_widget.row(item))

    def remove_file(self, file_path):
        if os.path.exists(file_path):
            os.remove(file_path)

    def send_to_kindle(self, downloaded_dict):
        if not check_send_config():
            reply = QMessageBox.question(self, '配置提示',
                                         '推送配置未完成,不能推送书籍,需要现在配置吗?',
                                         QMessageBox.Yes | QMessageBox.No,
                                         QMessageBox.Yes)
            if reply == QMessageBox.Yes:
                config_dialog = ConfigDialog(self)
                config_dialog.show()
            return
        downloaded_dict['status'] = "0"
        self.send_trigger.emit(downloaded_dict)
        QMessageBox.information(self, '推送通知', '正在推送...')
        self.send_work = SendWorker(downloaded_dict)
        self.send_work.send_trigger.connect(self.call_back_send)
        self.send_work.start()

    def regain(self, downloaded_dict):
        pass

    def get_file_extension(self, file_name):
        return file_name.strip().split(".")[-1]

    def get_icon_by_file_type(self, file_name):
        extension = self.get_file_extension(file_name)
        if extension in ['rar', 'zip', '7z']:
            return QPixmap('zip.png'), False
        elif extension == 'mobi':
            return QPixmap('book.png'), True
        elif extension == 'pdf':
            return QPixmap('pdf.png'), True
        else:
            return QPixmap('file.png'), False

    def get_status_msg(self, status_code):
        status_dict = {"1": "推送成功", "-1": "推送失败", "0": "正在推送"}
        if str(status_code) in status_dict:
            return status_dict[str(status_code)]
        else:
            return '未知状态'

    def call_back_send(self, send_dict):
        QMessageBox.information(self, '推送完成',
                                self.get_status_msg(send_dict['status']))
        self.send_trigger.emit(send_dict)
コード例 #31
0
class DownloadingPage(QWidget):
    def __init__(self, parent=None):
        super(DownloadingPage, self).__init__(parent)
        self.download_worker_dict = {}
        self.data_path = os.path.join(data, 'downloading.json')
        self.downloaing_list_widget = QListWidget(self)
        self.downloaing_list_widget.setStyleSheet("QListWidget{border:none;}")
        self.downloaing_list_widget.setSpacing(2)
        self.downloaing_list_widget.setGeometry(QRect(10, 20, 950, 800))
        self.init_ui()

    def check_download(self, downloading_dict):
        downloading_dict_list = self.load_downloading_dict_list()
        if not downloading_dict_list:
            return True
        for downloading_dict in downloading_dict_list:
            if downloading_dict['url'].find("lanjuhua"):
                return False

    def init_ui(self):
        downloading_dict_list = self.load_downloading_dict_list()
        if not downloading_dict_list:
            return
        for downloading_dict in downloading_dict_list:
            self.add_downloading_item_to_widget(downloading_dict)

    def add_downloading_item(self, downloading_dict, worker=None):
        self.download_worker_dict[downloading_dict['id']] = worker
        self.add_downloading_item_to_widget(downloading_dict)
        self.add_downloading_item_to_data(downloading_dict)

    def add_downloading_item_to_widget(self, downloading_dict):
        item = QListWidgetItem()
        item.setText(downloading_dict['id'])
        item.setSizeHint(QSize(608, 86))
        self.downloaing_list_widget.addItem(item)
        self.downloaing_list_widget.setItemWidget(
            item, self.create_downloading_item(downloading_dict))

    def add_downloading_item_to_data(self, downloading_dict):
        downloading_dict_list = self.load_downloading_dict_list()
        downloading_dict_list.append(downloading_dict)
        self.write_data(downloading_dict_list)

    def load_downloading_dict_list(self):
        if not os.path.exists(self.data_path):
            os.makedirs(data, exist_ok=True)
            return []
        with open(self.data_path, 'r', encoding='utf-8') as f:
            downloading_dict_list = json.load(f)
            return downloading_dict_list

    def create_downloading_item(self, downloading_dict):
        book_name = downloading_dict['file_name']
        item_widget = QWidget()
        item_widget.setObjectName(f'{downloading_dict["id"]}')
        hbox_layout = QHBoxLayout()

        item_label = QLabel()
        item_label.setPixmap(QPixmap(os.path.join(assets, 'book.png')))
        hbox_layout.addWidget(item_label)

        vlayout = QVBoxLayout()
        file_name_label = QLabel(book_name)
        download_progress = ProgressBar(
            minimum=0,
            maximum=100,
            textVisible=False,
            objectName=f'{downloading_dict["id"]}_progress_bar')
        style_sheet = f'''
            #{downloading_dict["id"]}_progress_bar {{
                min-height: 6px;
                max-height: 6px;
                border-radius: 6px;
            }}
            #{downloading_dict["id"]}_progress_bar::chunk {{
                border-radius: 6px;
                width:12px;
                background-color: #D20A0A;
            }}        
        '''
        download_progress.setStyleSheet(style_sheet)
        vlayout.addWidget(file_name_label)
        vlayout.addWidget(download_progress)

        h_in_v_item_layout = QHBoxLayout()
        h_in_v_item_layout.addWidget(QLabel(downloading_dict['size']),
                                     Qt.AlignLeft)

        percent_label = QLabel()
        percent_label.setObjectName(f'{downloading_dict["id"]}_percent')
        percent_label.setText(f'已经下载:0%')
        h_in_v_item_layout.addWidget(percent_label, Qt.AlignHCenter)

        speed_label = QLabel()
        speed_label.setObjectName(f'{downloading_dict["id"]}_speed')
        speed_label.setText("0 kb/s")
        h_in_v_item_layout.addWidget(speed_label, Qt.AlignRight)

        vlayout.addLayout(h_in_v_item_layout)
        hbox_layout.addLayout(vlayout)

        del_btn = QPushButton()
        del_btn.setIcon(QIcon(os.path.join(assets, 'delete.png')))
        del_btn.setStyleSheet("QPushButton{border:none}")
        del_btn.clicked.connect(
            lambda: self.remove_task(downloading_dict["id"]))
        hbox_layout.addWidget(del_btn)

        item_widget.setLayout(hbox_layout)
        item_widget_style_sheet = f'#{downloading_dict["id"]}{{background-color:rgb(241,231,230);color:rgb(210,10,10)}}'
        item_widget.setStyleSheet(item_widget_style_sheet)
        return item_widget

    def downloading_callback(self, downloading_dict):
        downloading_dict = list(downloading_dict)

    def remove_task(self, id):
        print(f'remove_task {id}...')
        self.remove_task_form_list_widget(id)
        self.remove_task_from_data(id)

    def remove_task_from_data(self, id):
        downloading_dict_list = self.load_downloading_dict_list()
        for downloading_dict in downloading_dict_list:
            if id in downloading_dict['id']:
                if id in self.download_worker_dict:
                    eval(str(self.download_worker_dict[id]()))
                downloading_dict_list.remove(downloading_dict)
        self.write_data(downloading_dict_list)

    def write_data(self, downloading_dict_list):
        with open(self.data_path, 'w') as f:
            json.dump(downloading_dict_list, f)

    def remove_task_form_list_widget(self, id):
        item = self.downloaing_list_widget.findItems(id, Qt.MatchExactly)[0]
        self.downloaing_list_widget.takeItem(
            self.downloaing_list_widget.row(item))

    def update_progress(self, progress_dict):
        percent_label = self.downloaing_list_widget.findChild(
            QLabel, f'{progress_dict["id"]}_percent')
        if percent_label:
            percent_label.setText(f'已经下载:{progress_dict["progress"]}%')

            speed_label = self.downloaing_list_widget.findChild(
                QLabel, f'{progress_dict["id"]}_speed')
            speed_label.setText(f'{progress_dict["speed"]} kb/s')

            progress_bar = self.downloaing_list_widget.findChild(
                ProgressBar, f'{progress_dict["id"]}_progress_bar')
            progress_bar.setValue(progress_dict["progress"])
コード例 #32
0
class Indexer(QWidget):
    def __init__(self, wheres_the_fck_receipt: api_interface.WheresTheFckReceipt, parent=None):
        QWidget.__init__(self, parent=None)
        self.wheres_the_fck_receipt = wheres_the_fck_receipt
        self.index_job = None  # type: api_interface.IndexJob
        self.index_job_timer = QTimer()
        self.index_job_timer.timeout.connect(self.index_job_timer_timeout)

        # WIDGETS
        # add dir button
        self.add_directory = QPushButton('Add Directory')
        self.add_directory.setEnabled(True)
        self.add_directory.clicked.connect(self.add_directory_clicked)

        # locations
        self.directories = QListWidget()
        self.directories.itemSelectionChanged.connect(self.directories_selection_changed)
        self.directories.setSelectionMode(QAbstractItemView.SingleSelection)
        for dir in self.wheres_the_fck_receipt.get_directories():
            self.directories.addItem(dir)

        # the locations_action_bar
        self.index = QPushButton('Update')
        self.index.clicked.connect(self.update_clicked)
        self.remove_dir = QPushButton('Remove')
        self.remove_dir.clicked.connect(self.remove_clicked)
        self.re_index = QPushButton('Re-Index')
        self.re_index.clicked.connect(self.reindex_clicked)
        file_list_action_bar_layout = QHBoxLayout()
        file_list_action_bar_layout.setContentsMargins(0, 0, 0, 0)
        file_list_action_bar_layout.addWidget(self.index)
        file_list_action_bar_layout.addWidget(self.remove_dir)
        file_list_action_bar_layout.addWidget(self.re_index)
        self.file_list_action_bar_widget = QWidget()
        self.file_list_action_bar_widget.setLayout(file_list_action_bar_layout)
        self.file_list_action_bar_widget.setEnabled(False)

        # index_status_widget
        self.index_progress = QProgressBar()
        self.index_progress.setEnabled(False)
        self.stop_index = QPushButton('Stop Indexing')
        self.stop_index.setEnabled(False)
        self.stop_index.clicked.connect(self.stop_index_clicked)
        index_status_widget_layout = QHBoxLayout()
        index_status_widget_layout.setContentsMargins(0, 0, 0, 0)
        index_status_widget_layout.addWidget(self.index_progress)
        index_status_widget_layout.addWidget(self.stop_index)
        index_status_widget = QWidget()
        index_status_widget.setLayout(index_status_widget_layout)

        # index console
        self.index_console = QTextEdit()
        self.index_console.setReadOnly(True)
        # self.index_console.setEnabled(False)

        # layout
        layout = QVBoxLayout()
        layout.addWidget(QLabel("Indexed Directories:"))
        layout.addWidget(self.add_directory)
        layout.addWidget(self.directories)
        layout.addWidget(self.file_list_action_bar_widget)
        layout.addWidget(QLabel("Indexer Status:"))
        layout.addWidget(index_status_widget)
        layout.addWidget(self.index_console)
        self.setLayout(layout)

    def directories_selection_changed(self):
        list_items = self.directories.selectedItems()
        self.file_list_action_bar_widget.setEnabled(len(list_items) == 1)

    def add_directory_clicked(self):

        settings = QSettings('WheresTheFckReceipt', 'WheresTheFckReceipt')
        last_directory_added = settings.value("last_directory_added", "")

        directory = str(QFileDialog.getExistingDirectory(self, "Select Directory",
                                                         last_directory_added,
                                                         QFileDialog.ShowDirsOnly))
        if directory:
            settings.setValue("last_directory_added", directory)
            del settings
            # get the job
            if not self.directories.findItems(directory, Qt.MatchExactly):
                self.directories.addItem(directory)
            self.index_job = self.wheres_the_fck_receipt.add_directory(directory)
            self.run_indexer()

    def run_indexer(self):
        # manage gui
        self.add_directory.setEnabled(False)
        self.directories.setEnabled(False)
        self.file_list_action_bar_widget.setEnabled(False)
        self.index_progress.setEnabled(True)
        self.index_progress.reset()
        self.stop_index.setEnabled(True)
        # self.index_console.setEnabled(True)
        self.index_console.clear()
        # start job
        self.stop_index.setEnabled(True)
        self.index_job.start()
        self.index_job_timer.start(500)

    def update_clicked(self):
        self.index_job = self.wheres_the_fck_receipt.update_directory(self.directories.currentItem().text())
        self.run_indexer()

    def remove_clicked(self):

        self.add_directory.setEnabled(False)
        self.directories.setEnabled(False)
        self.file_list_action_bar_widget.setEnabled(False)
        progress_updater = ProgressUpdater(self)
        self.wheres_the_fck_receipt.remove_directory(self.directories.currentItem().text(), progress_updater)
        if progress_updater.canceled() is False:
            self.directories.takeItem(self.directories.currentRow())

        self.add_directory.setEnabled(True)
        self.directories.setEnabled(True)
        self.file_list_action_bar_widget.setEnabled(True)

    def reindex_clicked(self):
        self.index_job = self.wheres_the_fck_receipt.reindex_directory(self.directories.currentItem().text())
        self.run_indexer()

    def stop_index_clicked(self):
        self.index_job.stop()
        self.indexing_stopped()

    def indexing_stopped(self):
        self.index_job_timer.stop()
        self.add_directory.setEnabled(True)
        self.directories.setEnabled(True)
        self.file_list_action_bar_widget.setEnabled(len(self.directories.selectedItems()) > 0)
        self.index_progress.setEnabled(False)
        self.stop_index.setEnabled(False)
        # self.index_console.setEnabled(False)
        self.index_job = None

    def index_job_timer_timeout(self):
        for msg in self.index_job.get_messages():
            self.index_console.append(msg)
        num_files = self.index_job.get_num_files()
        if num_files and self.index_progress.maximum() != num_files:
            self.index_progress.setRange(0, num_files)
        curr_file_idx = self.index_job.get_curr_file_index()
        if curr_file_idx:
            self.index_progress.setValue(curr_file_idx)
        if self.index_job.is_finished():
            self.index_progress.setValue(self.index_progress.maximum())
            self.indexing_stopped()
コード例 #33
0
class SubwindowMisc(QWidget):
    """Show subwindow with miscellaneous settings."""

    current_tab = -1

    def createWindow(self, mainWindow, tab=''):
        """Create subwindow with miscellaneous settings."""
        try:
            parent = None
            super().__init__(parent)
            # self.setWindowFlags(Qt.WindowStaysOnTopHint)

            self.setWindowIcon(
                QIcon(scctool.settings.getResFile('settings.png')))
            self.setWindowModality(Qt.ApplicationModal)
            self.mainWindow = mainWindow
            self.passEvent = False
            self.controller = mainWindow.controller
            self.__dataChanged = False

            self.createButtonGroup()
            self.createTabs(tab)

            mainLayout = QVBoxLayout()

            mainLayout.addWidget(self.tabs)
            mainLayout.addLayout(self.buttonGroup)

            self.setLayout(mainLayout)

            self.resize(
                QSize(int(mainWindow.size().width() * 0.9),
                      self.sizeHint().height()))
            relativeChange = QPoint(int(mainWindow.size().width() / 2),
                                    int(mainWindow.size().height() / 3))\
                - QPoint(int(self.size().width() / 2),
                         int(self.size().height() / 3))
            self.move(mainWindow.pos() + relativeChange)

            self.setWindowTitle(_("Miscellaneous Settings"))

        except Exception:
            module_logger.exception("message")

    def createTabs(self, tab=''):
        """Create tabs."""
        self.tabs = QTabWidget()

        self.createMapsBox()
        self.createFavBox()
        self.createAliasBox()
        self.createOcrBox()
        self.createAlphaBox()
        self.createSC2ClientAPIBox()
        self.createAligulacTab()
        self.createCounterTab()

        # Add tabs
        self.tabs.addTab(self.mapsBox, _("Map Manager"))
        self.tabs.addTab(self.favBox, _("Favorites"))
        self.tabs.addTab(self.aliasBox, _("Alias"))
        self.tabs.addTab(self.ocrBox, _("OCR"))
        self.tabs.addTab(self.alphaBox, _("AlphaTL && Ingame Score"))
        self.tabs.addTab(self.clientapiBox, _("SC2 Client API"))
        self.tabs.addTab(self.aligulacTab, _("Aligulac"))
        self.tabs.addTab(self.counterTab, _("Countdown && Ticker"))

        table = dict()
        table['mapmanager'] = 0
        table['favorites'] = 1
        table['alias'] = 2
        table['ocr'] = 3
        table['alphatl'] = 4
        table['sc2clientapi'] = 5
        table['aligulac'] = 6
        table['counter'] = 7
        self.tabs.setCurrentIndex(table.get(tab, SubwindowMisc.current_tab))
        self.tabs.currentChanged.connect(self.tabChanged)

    @classmethod
    def tabChanged(cls, idx):
        """Save the current tab index."""
        SubwindowMisc.current_tab = idx

    def changed(self):
        """Handle changes."""
        self.__dataChanged = True

    def createAlphaBox(self):
        """Create Alpha QWidget."""
        self.alphaBox = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("AlphaTL"))
        layout = QHBoxLayout()

        self.cb_trans_banner = QCheckBox(
            " " + _("Download transparent Banner of the Match"))
        self.cb_trans_banner.setChecked(
            scctool.settings.config.parser.getboolean(
                "SCT", "transparent_match_banner"))
        self.cb_trans_banner.stateChanged.connect(self.changed)

        layout.addWidget(self.cb_trans_banner)
        box.setLayout(layout)

        mainLayout.addWidget(box)

        box = QGroupBox(_("Set Ingame Score Task"))
        layout = QVBoxLayout()

        self.cb_ctrlx = QCheckBox(" " +
                                  _('Automatically press Ctrl+X to apply the'
                                    ' correct player order ingame'))
        self.cb_ctrlx.setToolTip(
            _("This will ensure that the player of the first team is always"
              " on the left/top in the ingame Observer UI."))
        self.cb_ctrlx.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlX"))
        self.cb_ctrlx.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrlx)

        self.cb_ctrln = QCheckBox(" " + _('Automatically press Ctrl+N before'
                                          ' OCR to display player names'))
        self.cb_ctrln.setToolTip(
            _("This is recommended for Standard and Gawliq Observer UI."))
        self.cb_ctrln.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlN"))
        self.cb_ctrln.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrln)

        self.cb_ctrlshifts = QCheckBox(
            " " + _('Automatically press Ctrl+Shift+S to display'
                    ' the ingame score'))
        self.cb_ctrlshifts.setToolTip(
            _("Ctrl+Shift+S is needed for the WCS-Gameheart Oberserver"
              " Overlay, but disables the sound for other overlays."))
        self.cb_ctrlshifts.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlShiftS"))
        self.cb_ctrlshifts.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrlshifts)

        self.cb_ctrlshiftc = QCheckBox(
            " " + _('Automatically press Ctrl+Shift+C to toogle the clan tag'))
        self.cb_ctrlshiftc.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlShiftC"))
        self.cb_ctrlshiftc.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrlshiftc)

        container = QHBoxLayout()
        self.cb_ctrlshiftr = QComboBox()
        self.cb_ctrlshiftr.addItem("0")
        self.cb_ctrlshiftr.addItem("1")
        self.cb_ctrlshiftr.addItem("2")
        try:
            self.cb_ctrlshiftr.setCurrentIndex(
                scctool.settings.config.parser.getint("SCT", "CtrlShiftR"))
        except Exception:
            self.cb_ctrlshiftr.setCurrentIndex(0)
        self.cb_ctrlshiftr.setMaximumWidth(40)
        self.cb_ctrlshiftr.currentIndexChanged.connect(self.changed)
        container.addWidget(
            QLabel(
                _('Automatically press Ctrl+Shift+R to toogle the race icon '))
        )
        container.addWidget(self.cb_ctrlshiftr)
        container.addWidget(QLabel(_(' time(s)')))
        layout.addLayout(container)

        self.cb_blacklist = QCheckBox(" " + _('Activate Blacklist for'
                                              ' Ingame Score'))
        self.cb_blacklist.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "blacklist_on"))
        self.cb_blacklist.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_blacklist)

        box.setLayout(layout)

        mainLayout.addWidget(box)

        box = QGroupBox(_("Blacklist for Ingame Score"))
        layout = QVBoxLayout()

        blacklistDesc = _("Enter your SC2 client usernames to deactivate"
                          " automatically setting the ingame score and"
                          " toogling the production tab when you are playing"
                          " yourself. Replays are exempt.")
        label = QLabel(blacklistDesc)
        label.setAlignment(Qt.AlignJustify)
        label.setWordWrap(True)
        layout.addWidget(label)

        self.list_blacklist = ListTable(4,
                                        scctool.settings.config.getBlacklist())
        self.list_blacklist.dataModified.connect(self.changed)
        self.list_blacklist.setFixedHeight(50)
        layout.addWidget(self.list_blacklist)
        box.setLayout(layout)

        mainLayout.addWidget(box)

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))

        self.alphaBox.setLayout(mainLayout)

    def createFavBox(self):
        """Create favorites box."""
        self.favBox = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Players"))
        layout = QHBoxLayout()

        self.list_favPlayers = ListTable(
            4, scctool.settings.config.getMyPlayers())
        self.list_favPlayers.dataModified.connect(self.changed)
        self.list_favPlayers.setFixedHeight(150)
        layout.addWidget(self.list_favPlayers)
        box.setLayout(layout)

        mainLayout.addWidget(box)

        box = QGroupBox(_("Teams"))
        layout = QVBoxLayout()

        self.list_favTeams = ListTable(3, scctool.settings.config.getMyTeams())
        self.list_favTeams.dataModified.connect(self.changed)
        self.list_favTeams.setFixedHeight(100)
        layout.addWidget(self.list_favTeams)
        self.cb_swapTeams = QCheckBox(
            _('Swap my favorite team always to the left'))
        self.cb_swapTeams.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "swap_myteam"))
        self.cb_swapTeams.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_swapTeams)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))

        self.favBox.setLayout(mainLayout)

    def createAliasBox(self):
        """Create favorites box."""
        self.aliasBox = QWidget()
        mainLayout = QGridLayout()

        aliasDesc = _(
            'Player, team, and league aliases are replaced by the actual name when'
            + ' encountered by the match grabber. Additionally, SC2 player' +
            ' names listed as aliases are replaced in the intros' +
            ' and used to identify players by the automatic' +
            ' background tasks "Auto Score Update" and "Set Ingame Score".')
        label = QLabel(aliasDesc)
        label.setAlignment(Qt.AlignJustify)
        label.setWordWrap(True)

        mainLayout.addWidget(label, 1, 0, 1, 3)

        box = QGroupBox(_("Player Aliases"))
        layout = QVBoxLayout()
        self.list_aliasPlayers = AliasTreeView(self)
        self.list_aliasPlayers.aliasRemoved.connect(
            self.controller.aliasManager.removePlayerAlias)
        layout.addWidget(self.list_aliasPlayers)
        addButton = QPushButton(_("Add Alias"))
        addButton.clicked.connect(
            lambda: self.addAlias(self.list_aliasPlayers, _('Player Name')))
        layout.addWidget(addButton)
        box.setLayout(layout)
        mainLayout.addWidget(box, 0, 0)

        box = QGroupBox(_("Team Aliases"))
        layout = QVBoxLayout()
        self.list_aliasTeams = AliasTreeView(self)
        self.list_aliasTeams.aliasRemoved.connect(
            self.controller.aliasManager.removeTeamAlias)
        layout.addWidget(self.list_aliasTeams)
        addButton = QPushButton(_("Add Alias"))
        addButton.clicked.connect(
            lambda: self.addAlias(self.list_aliasTeams, _('Team Name')))
        layout.addWidget(addButton)
        box.setLayout(layout)
        mainLayout.addWidget(box, 0, 1)

        box = QGroupBox(_("League Aliases"))
        layout = QVBoxLayout()
        self.list_aliasLeagues = AliasTreeView(self)
        self.list_aliasLeagues.aliasRemoved.connect(
            self.controller.aliasManager.removeLeagueAlias)
        layout.addWidget(self.list_aliasLeagues)
        addButton = QPushButton(_("Add Alias"))
        addButton.clicked.connect(
            lambda: self.addAlias(self.list_aliasLeagues, _('League Name')))
        layout.addWidget(addButton)
        box.setLayout(layout)
        mainLayout.addWidget(box, 0, 2)

        alias_list = self.controller.aliasManager.playerAliasList()
        for player, aliases in alias_list.items():
            self.list_aliasPlayers.insertAliasList(player, aliases)

        alias_list = self.controller.aliasManager.teamAliasList()
        for league, aliases in alias_list.items():
            self.list_aliasTeams.insertAliasList(league, aliases)

        alias_list = self.controller.aliasManager.leagueAliasList()
        for league, aliases in alias_list.items():
            self.list_aliasLeagues.insertAliasList(league, aliases)

        self.aliasBox.setLayout(mainLayout)

    def addAlias(self, widget, scope, name=""):
        """Add an alias."""
        name, ok = QInputDialog.getText(self, scope, scope + ':', text=name)
        if not ok:
            return

        name = name.strip()
        alias, ok = QInputDialog.getText(self,
                                         _('Alias'),
                                         _('Alias of {}').format(name) + ':',
                                         text="")

        alias = alias.strip()
        if not ok:
            return

        try:
            if widget == self.list_aliasPlayers:
                self.controller.aliasManager.addPlayerAlias(name, alias)
            elif widget == self.list_aliasTeams:
                self.controller.aliasManager.addTeamAlias(name, alias)
            elif widget == self.list_aliasLeagues:
                self.controller.aliasManager.addLeagueAlias(name, alias)
            widget.insertAlias(name, alias, True)
        except Exception as e:
            module_logger.exception("message")
            QMessageBox.critical(self, _("Error"), str(e))

    def createSC2ClientAPIBox(self):
        """Create form for SC2 Client API config."""
        self.clientapiBox = QWidget()

        mainLayout = QVBoxLayout()

        box = QGroupBox(_("SC2 Client API Address"))

        layout = QGridLayout()

        self.cb_usesc2listener = QCheckBox(
            " " + _("Listen to SC2 Client API running"
                    " on a different PC in the network."))
        self.cb_usesc2listener.setChecked(
            scctool.settings.config.parser.getboolean(
                "SCT", "sc2_network_listener_enabled"))
        self.cb_usesc2listener.stateChanged.connect(self.changed)

        self.listener_address = MonitoredLineEdit()
        self.listener_address.setAlignment(Qt.AlignCenter)
        self.listener_address.setText(
            scctool.settings.config.parser.get("SCT",
                                               "sc2_network_listener_address"))
        self.listener_address.textModified.connect(self.changed)
        # self.tesseract.setAlignment(Qt.AlignCenter)
        self.listener_address.setPlaceholderText("[Your SC2 PC IP]:6119")
        self.listener_address.setToolTip(
            _('IP address and port of machine running SC2.'))
        ip_port = (
            r"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)" +
            r"{3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):[0-9]+$")
        self.listener_address.setValidator(QRegExpValidator(QRegExp(ip_port)))

        self.test_listener = QPushButton(" " +
                                         _("Test SC2 Client API Connection") +
                                         " ")
        self.test_listener.clicked.connect(self.testClientAPI)

        text = _("Activate this option if you are using a two computer "
                 "setup with StarCraft Casting Tool running on a different"
                 " PC than your SC2 client. Open the Battle.net launcher "
                 "on the latter PC, click 'Options', 'Game Settings', and "
                 "under SC2, check 'Additional Command Line Arguments', and "
                 "enter '-clientapi 6119'. Finally set as network"
                 " address below: '[Your SC2 PC IP]:6119'.")

        label = QLabel(text)
        label.setAlignment(Qt.AlignJustify)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setMargin(5)
        layout.addWidget(label, 1, 0, 1, 3)

        layout.addWidget(self.cb_usesc2listener, 0, 0, 1, 3)
        layout.addWidget(QLabel(_("Network Address") + ": "), 3, 0)
        layout.addWidget(self.listener_address, 3, 1)
        layout.addWidget(self.test_listener, 3, 2)

        box.setLayout(layout)
        mainLayout.addWidget(box)
        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.clientapiBox.setLayout(mainLayout)

    def testClientAPI(self):
        """Test for connection to sc2 client api."""
        QApplication.setOverrideCursor(Qt.WaitCursor)
        address = self.listener_address.text().strip()
        url = "http://{}/ui".format(address)
        try:
            r = requests.get(url, timeout=10)
            r.raise_for_status()
            successfull = True
        except Exception:
            successfull = False
            module_logger.error("message")
        finally:
            QApplication.restoreOverrideCursor()

        title = _("Connection Test")

        if successfull:
            QMessageBox.information(
                self, title, _('Connection to SC2 client API established!'))
        else:
            QMessageBox.warning(
                self, title,
                _('Unable to connect to SC2 client API.'
                  ' Please make sure that SC2 is currently'
                  ' running on that machine.'))

    def createOcrBox(self):
        """Create forms for OCR."""
        self.ocrBox = QWidget()

        mainLayout = QVBoxLayout()

        box = QGroupBox(
            _("Optical Character Recognition for"
              " Automatic Setting of Ingame Score"))

        layout = QGridLayout()

        self.cb_useocr = QCheckBox(" " +
                                   _("Activate Optical Character Recognition"))
        self.cb_useocr.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "use_ocr"))
        self.cb_useocr.stateChanged.connect(self.changed)

        self.tesseract = MonitoredLineEdit()
        self.tesseract.setText(
            scctool.settings.config.parser.get("SCT", "tesseract"))
        self.tesseract.textModified.connect(self.changed)
        # self.tesseract.setAlignment(Qt.AlignCenter)
        self.tesseract.setPlaceholderText(
            "C:\\Program Files (x86)\\Tesseract-OCR\\tesseract")
        self.tesseract.setReadOnly(True)
        self.tesseract.setToolTip(_('Tesseract-OCR Executable'))

        self.browse = QPushButton(_("Browse..."))
        self.browse.clicked.connect(self.selectTesseract)

        text = _(
            "Sometimes the order of players given by the SC2-Client-API"
            " differs from the order in the Observer-UI resulting in a"
            " swapped match score. To correct this via Optical Character"
            " Recognition you have to download {} and install and select the"
            " exectuable below, if it is not detected automatically.")
        url = 'https://github.com/UB-Mannheim/tesseract' + \
            '/wiki#tesseract-at-ub-mannheim'
        href = "<a href='{}'>" + "Tesseract-OCR" + "</a>"
        href = href.format(url)

        label = QLabel(text.format(href))
        label.setAlignment(Qt.AlignJustify)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setMargin(5)
        layout.addWidget(label, 1, 0, 1, 2)

        layout.addWidget(self.cb_useocr, 0, 0, 1, 2)
        layout.addWidget(QLabel(_("Tesseract-OCR Executable") + ":"), 2, 0)
        layout.addWidget(self.tesseract, 3, 0)
        layout.addWidget(self.browse, 3, 1)

        box.setLayout(layout)
        mainLayout.addWidget(box)
        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.ocrBox.setLayout(mainLayout)

        if (not scctool.settings.windows):
            self.cb_useocr.setEnabled(False)
            self.cb_useocr.setAttribute(Qt.WA_AlwaysShowToolTips)
            self.cb_useocr.setToolTip(
                _("This feature is only available in Windows."))
            self.tesseract.setEnabled(False)
            self.tesseract.setAttribute(Qt.WA_AlwaysShowToolTips)
            self.tesseract.setToolTip(
                _("This feature is only available in Windows."))
            self.browse.setEnabled(False)
            self.browse.setAttribute(Qt.WA_AlwaysShowToolTips)
            self.browse.setToolTip(
                _("This feature is only available in Windows."))

    def selectTesseract(self):
        """Create forms for tesseract."""
        old_exe = self.tesseract.text()
        default = scctool.settings.config.findTesserAct(old_exe)
        exe, ok = QFileDialog.getOpenFileName(
            self, _("Select Tesseract-OCR Executable"), default,
            _("Tesseract-OCR Executable") + " (tesseract.exe);; " +
            _("Executable") + " (*.exe);; " + _("All files") + " (*)")
        if (ok and exe != old_exe):
            self.tesseract.setText(exe)
            self.changed()

    def createAligulacTab(self):
        """Create the aligulac tab."""
        self.aligulacTab = QWidget()

        layout = QGridLayout()
        self.aligulacTreeview = AligulacTreeView(
            self, self.controller.aligulacManager)

        layout.addWidget(self.aligulacTreeview, 0, 0, 3, 1)

        self.pb_addAligulacID = QPushButton(_("Add Aligluac ID"))
        self.pb_addAligulacID.clicked.connect(
            lambda x, self=self: self.addAligulacID())
        layout.addWidget(self.pb_addAligulacID, 1, 1)

        self.pb_removeAligulacID = QPushButton(_("Remove Aligulac ID"))
        self.pb_removeAligulacID.clicked.connect(self.removeAligulacID)
        layout.addWidget(self.pb_removeAligulacID, 2, 1)

        layout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Minimum), 0, 1)

        self.aligulacTab.setLayout(layout)

    def addAligulacID(self, name='', aligulac_id=1):
        """Add an aligulac ID."""
        text, ok = QInputDialog.getText(self,
                                        _('Player Name'),
                                        _('Player Name') + ':',
                                        text=name)
        text = text.strip()
        if not ok or not text:
            return
        aligulac_id, ok = QInputDialog.getInt(self,
                                              _('Aligulac ID'),
                                              _('Aligulac ID') + ':',
                                              value=aligulac_id,
                                              min=1)
        if not ok:
            return

        self.aligulacTreeview.insertItem(text, aligulac_id)

    def removeAligulacID(self):
        """Remove an selected aligulac ID."""
        self.aligulacTreeview.removeSelected()

    def createCounterTab(self):
        """Create the aligulac tab."""
        self.counterTab = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Countdown"))
        layout = QFormLayout()
        self.le_countdown_replacement = QLineEdit()
        self.le_countdown_replacement.setText(
            scctool.settings.config.parser.get("Countdown", "replacement"))
        self.le_countdown_replacement.textChanged.connect(self.changed)
        layout.addRow(QLabel(_('Replacement Text')),
                      self.le_countdown_replacement)
        self.cb_counter_matchgrabber_update = QCheckBox('')
        self.cb_counter_matchgrabber_update.setChecked(
            scctool.settings.config.parser.getboolean("Countdown",
                                                      "matchgrabber_update"))
        self.cb_counter_matchgrabber_update.stateChanged.connect(self.changed)
        layout.addRow(QLabel(_('Update Static Countdown via MatchGrabber')),
                      self.cb_counter_matchgrabber_update)
        self.counter_pretext = QPlainTextEdit()
        self.counter_pretext.setPlainText(
            scctool.settings.config.parser.get("Countdown", "pre_txt"))
        self.counter_pretext.textChanged.connect(self.changed)
        self.counter_posttext = QPlainTextEdit()
        self.counter_posttext.setPlainText(
            scctool.settings.config.parser.get("Countdown", "post_txt"))
        self.counter_posttext.textChanged.connect(self.changed)
        layout.addRow(QLabel(_('Pre-Text (in countdown.txt)')),
                      self.counter_pretext)
        layout.addRow(QLabel(_('Post-Text (in countdown.txt)')),
                      self.counter_posttext)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        box = QGroupBox(_("Ticker"))
        layout = QFormLayout()
        box.setLayout(layout)
        self.ticker_pretext = QLineEdit()
        self.ticker_pretext.setText(
            scctool.settings.config.parser.get("Ticker", "prefix"))
        self.ticker_pretext.textChanged.connect(self.changed)
        layout.addRow(QLabel(_('Prefix text (in ticker.txt)')),
                      self.ticker_pretext)
        mainLayout.addWidget(box)

        self.counterTab.setLayout(mainLayout)

    def createMapsBox(self):
        """Create box for map manager."""
        self.mapsize = 300

        self.mapsBox = QWidget()

        layout = QGridLayout()

        self.maplist = QListWidget()
        self.maplist.setSortingEnabled(True)
        for sc2map in scctool.settings.maps:
            self.maplist.addItem(QListWidgetItem(sc2map))
        self.maplist.setCurrentItem(self.maplist.item(0))
        self.maplist.currentItemChanged.connect(self.changePreview)
        # self.maplist.setFixedHeight(self.mapsize)
        self.maplist.setMinimumWidth(150)

        layout.addWidget(self.maplist, 0, 1, 2, 1)
        self.mapPreview = QLabel()
        self.mapPreview.setFixedWidth(self.mapsize)
        self.mapPreview.setFixedHeight(self.mapsize)
        self.mapPreview.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.mapPreview, 0, 0)
        self.mapInfo = QLabel()
        self.mapInfo.setIndent(10)
        layout.addWidget(self.mapInfo, 1, 0)

        self.pb_addMapLiquipedia = QPushButton(_("Add from Liquipedia"))
        self.pb_addMapLiquipedia.clicked.connect(self.addFromLquipedia)
        self.pb_addMap = QPushButton(_("Add from File"))
        self.pb_addMap.clicked.connect(self.addMap)
        self.pb_renameMap = QPushButton(_("Rename"))
        self.pb_renameMap.clicked.connect(self.renameMap)
        self.pb_changeMap = QPushButton(_("Change Image"))
        self.pb_changeMap.clicked.connect(self.changeMap)
        self.pb_removeMap = QPushButton(_("Remove"))
        self.pb_removeMap.clicked.connect(self.deleteMap)

        self.sc_removeMap = QShortcut(QKeySequence("Del"), self.maplist)
        self.sc_removeMap.setAutoRepeat(False)
        self.sc_removeMap.setContext(Qt.WidgetWithChildrenShortcut)
        self.sc_removeMap.activated.connect(self.deleteMap)

        self.cb_newMapsPrompt = QCheckBox(
            _('Prompt to download new ladders maps.'))
        self.cb_newMapsPrompt.setChecked(
            scctool.settings.config.parser.getboolean("SCT",
                                                      "new_maps_prompt"))
        self.cb_newMapsPrompt.stateChanged.connect(self.changed)

        self.pb_downloadLadderMaps = QPushButton(_("Download Ladder Maps"))
        self.pb_downloadLadderMaps.clicked.connect(self.downloadLadderMaps)

        box = QWidget()
        container = QHBoxLayout()

        container.addWidget(self.pb_addMapLiquipedia, 0)
        container.addWidget(self.pb_addMap, 0)
        container.addWidget(QLabel(), 1)
        container.addWidget(self.pb_downloadLadderMaps, 0)
        container.addWidget(QLabel(), 1)
        container.addWidget(self.pb_renameMap, 0)
        container.addWidget(self.pb_changeMap, 0)
        container.addWidget(self.pb_removeMap, 0)
        box.setLayout(container)

        layout.addWidget(box, 2, 0, 1, 2)

        layout.addWidget(self.cb_newMapsPrompt, 3, 0, 1, 1)
        layout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding), 3,
            2, 1, 2)

        layout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding), 4,
            0, 1, 2)

        self.changePreview()
        self.mapsBox.setLayout(layout)

    def renameMap(self):
        """Rename maps."""
        item = self.maplist.currentItem()
        mapname = item.text()
        text, ok = QInputDialog.getText(self,
                                        _('Map Name'),
                                        _('Map Name') + ':',
                                        text=mapname)
        if not ok:
            return
        text = text.strip()
        if (text == mapname):
            return
        if text.lower() == 'tbd':
            QMessageBox.critical(
                self, _("Error"),
                _('"{}" is not a valid map name.').format(text))
            return
        if (text in scctool.settings.maps):
            buttonReply = QMessageBox.warning(
                self, _("Duplicate Entry"),
                _("Map is already in list! Overwrite?"),
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if buttonReply == QMessageBox.No:
                return

        self.controller.addMap(self.controller.getMapImg(mapname, True), text)
        self.controller.deleteMap(mapname)
        item.setText(text)

    def changeMap(self):
        """Change a map."""
        current_map = self.maplist.currentItem().text()
        fileName, ok = QFileDialog.getOpenFileName(
            self, _("Select Map Image (> 500x500px recommended)"), "",
            _("Supported Images") + " (*.png *.jpg *.jpeg)")
        if ok:
            base = os.path.basename(fileName)
            name, __ = os.path.splitext(base)
            name = name.replace("_", " ")
            self.controller.deleteMap(current_map)
            self.controller.addMap(fileName, current_map)
            self.changePreview()

    def addMap(self):
        """Add a map."""
        fileName, ok = QFileDialog.getOpenFileName(
            self, _("Select Map Image (> 500x500px recommended)"), "",
            _("Supported Images") + " (*.png *.jpg  *.jpeg)")
        if ok:
            base = os.path.basename(fileName)
            name, __ = os.path.splitext(base)
            name = name.replace("_", " ")
            map_name, ok = QInputDialog.getText(self,
                                                _('Map Name'),
                                                _('Map Name') + ':',
                                                text=name)
            map_name = map_name.strip()
            if ok:
                if map_name.lower() == 'tbd':
                    QMessageBox.critical(
                        self, _("Error"),
                        _('"{}" is not a valid map name.').format(map_name))
                    return

                if (map_name in scctool.settings.maps):
                    buttonReply = QMessageBox.warning(
                        self, _("Duplicate Entry"),
                        _("Map is already in list! Overwrite?"),
                        QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                    if buttonReply == QMessageBox.No:
                        return
                    else:
                        self.controller.deleteMap(map_name)

                self.controller.addMap(fileName, map_name)
                items = self.maplist.findItems(map_name, Qt.MatchExactly)
                if len(items) == 0:
                    item = QListWidgetItem(map_name)
                    self.maplist.addItem(item)
                    self.maplist.setCurrentItem(item)
                else:
                    self.maplist.setCurrentItem(items[0])
                self.changePreview()

    def downloadLadderMaps(self):
        players_per_team, ok = QInputDialog.getItem(
            self,
            _('Select the type of ladder maps to download'),
            _('Please select a map type') + ':',
            ['1vs1', '2vs2', '3vs3', '4vs4'],
            editable=False)
        players_per_team = int(players_per_team[0])
        found_a_map = False
        for sc2map in LiquipediaGrabber().get_ladder_mappool(players_per_team):
            if not sc2map in scctool.settings.maps:
                found_a_map = True
                self.controller.autoDownloadMap(sc2map, self)
                scctool.settings.maps.append(sc2map)
                items = self.maplist.findItems(sc2map, Qt.MatchExactly)
                if len(items) == 0:
                    item = QListWidgetItem(sc2map)
                    self.maplist.addItem(item)
                    self.maplist.setCurrentItem(item)
                else:
                    self.maplist.setCurrentItem(items[0])
                self.changePreview()
        if not found_a_map:
            QMessageBox.information(
                self, _("No missing map"),
                _('All of the current ladder maps are already present.'))

    def addFromLquipedia(self):
        """Add a map from Liquipedia."""
        grabber = LiquipediaGrabber()
        search_str = ''
        while True:
            search_str, ok = QInputDialog.getText(self,
                                                  _('Map Name'),
                                                  _('Map Name') + ':',
                                                  text=search_str)
            search_str.strip()
            try:
                if ok and search_str:
                    if search_str.lower() == 'tbd':
                        QMessageBox.critical(
                            self, _("Error"),
                            _('"{}" is not a valid map name.').format(
                                search_str))
                        continue
                    try:
                        QApplication.setOverrideCursor(Qt.WaitCursor)
                        sc2map = grabber.get_map(search_str)
                    except MapNotFound:
                        QMessageBox.critical(
                            self, _("Map not found"),
                            _('"{}" was not found on Liquipedia.').format(
                                search_str))
                        continue
                    finally:
                        QApplication.restoreOverrideCursor()
                    map_name = sc2map.get_name()

                    if (map_name in scctool.settings.maps):
                        buttonReply = QMessageBox.warning(
                            self, _("Duplicate Entry"),
                            _("Map {} is already in list! Overwrite?".format(
                                map_name)), QMessageBox.Yes | QMessageBox.No,
                            QMessageBox.No)
                        if buttonReply == QMessageBox.No:
                            break
                        else:
                            self.controller.deleteMap(map_name)

                    try:
                        QApplication.setOverrideCursor(Qt.WaitCursor)
                        images = grabber.get_images(sc2map.get_map_images())
                        image = ""
                        for size in sorted(images):
                            if not image or size <= 2500 * 2500:
                                image = images[size]
                        url = grabber._base_url + image

                        downloader = MapDownloader(self, map_name, url)
                        downloader.download()
                        if map_name not in scctool.settings.maps:
                            scctool.settings.maps.append(map_name)
                        items = self.maplist.findItems(map_name,
                                                       Qt.MatchExactly)
                        if len(items) == 0:
                            item = QListWidgetItem(map_name)
                            self.maplist.addItem(item)
                            self.maplist.setCurrentItem(item)
                        else:
                            self.maplist.setCurrentItem(items[0])
                        self.changePreview()
                    except Exception:
                        raise
                    finally:
                        QApplication.restoreOverrideCursor()
            except Exception as e:
                module_logger.exception("message")
                QMessageBox.critical(self, _("Error"), str(e))
            break

    def deleteMap(self):
        """Delete a map."""
        item = self.maplist.currentItem()
        mapname = item.text()
        buttonReply = QMessageBox.question(
            self, _('Delete map?'),
            _("Delete '{}' permanently?").format(mapname),
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if buttonReply == QMessageBox.Yes:
            self.controller.deleteMap(mapname)
            self.maplist.takeItem(self.maplist.currentRow())

    def changePreview(self):
        """Change the map preview."""
        if self.maplist.count() < 1:
            return

        mapname = self.maplist.currentItem().text()
        if (mapname == "TBD"):
            self.pb_renameMap.setEnabled(False)
            self.pb_removeMap.setEnabled(False)
            self.sc_removeMap.setEnabled(False)
        else:
            self.pb_removeMap.setEnabled(True)
            self.pb_renameMap.setEnabled(True)
            self.sc_removeMap.setEnabled(True)

        file = self.controller.getMapImg(mapname, True)
        pixmap = QPixmap(file)
        height = pixmap.height()
        width = pixmap.width()
        ext = os.path.splitext(file)[1].replace(".", "").upper()
        size = humanize.naturalsize(os.path.getsize(file))
        pixmap = QPixmap(file).scaled(self.mapsize, self.mapsize,
                                      Qt.KeepAspectRatio)
        self.mapPreview.setPixmap(pixmap)
        text = f"{width}x{height}px, {size}, {ext}"
        self.mapInfo.setText(text)

    def createButtonGroup(self):
        """Create buttons."""
        try:
            layout = QHBoxLayout()

            layout.addWidget(QLabel(""))

            buttonCancel = QPushButton(_('Cancel'))
            buttonCancel.clicked.connect(self.closeWindow)
            layout.addWidget(buttonCancel)

            buttonSave = QPushButton(_('&Save && Close'))
            buttonSave.setToolTip(_("Shortcut: {}").format("Ctrl+S"))
            self.shortcut = QShortcut(QKeySequence("Ctrl+S"), self)
            self.shortcut.setAutoRepeat(False)
            self.shortcut.activated.connect(self.saveCloseWindow)
            buttonSave.clicked.connect(self.saveCloseWindow)
            layout.addWidget(buttonSave)

            self.buttonGroup = layout
        except Exception:
            module_logger.exception("message")

    def saveData(self):
        """Save the data."""
        if (self.__dataChanged):
            scctool.settings.config.parser.set(
                "SCT", "myteams", ", ".join(self.list_favTeams.getData()))
            scctool.settings.config.parser.set(
                "SCT", "commonplayers",
                ", ".join(self.list_favPlayers.getData()))
            scctool.settings.config.parser.set("SCT", "tesseract",
                                               self.tesseract.text().strip())
            scctool.settings.config.parser.set("SCT", "use_ocr",
                                               str(self.cb_useocr.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "new_maps_prompt",
                str(self.cb_newMapsPrompt.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "transparent_match_banner",
                str(self.cb_trans_banner.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftS", str(self.cb_ctrlshifts.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftC", str(self.cb_ctrlshiftc.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "swap_myteam", str(self.cb_swapTeams.isChecked()))
            scctool.settings.config.parser.set("SCT", "CtrlN",
                                               str(self.cb_ctrln.isChecked()))
            scctool.settings.config.parser.set("SCT", "CtrlX",
                                               str(self.cb_ctrlx.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftR", str(self.cb_ctrlshiftr.currentText()))
            scctool.settings.config.parser.set(
                "SCT", "blacklist_on", str(self.cb_blacklist.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "blacklist", ", ".join(self.list_blacklist.getData()))
            scctool.settings.config.parser.set(
                "SCT", "sc2_network_listener_address",
                self.listener_address.text().strip())
            scctool.settings.config.parser.set(
                "SCT", "sc2_network_listener_enabled",
                str(self.cb_usesc2listener.isChecked()))
            scctool.settings.config.parser.set(
                "Countdown", "matchgrabber_update",
                str(self.cb_counter_matchgrabber_update.isChecked()))
            scctool.settings.config.parser.set(
                "Countdown", "replacement",
                self.le_countdown_replacement.text())
            scctool.settings.config.parser.set(
                "Countdown", "pre_txt", self.counter_pretext.toPlainText())
            scctool.settings.config.parser.set(
                "Countdown", "post_txt", self.counter_posttext.toPlainText())
            scctool.settings.config.parser.set(
                "Ticker", "prefix",
                self.ticker_pretext.text().strip())
            self.controller.matchControl.tickerChanged.emit()
            self.controller.refreshButtonStatus()
            # self.controller.setCBS()
            self.__dataChanged = False

    def saveCloseWindow(self):
        """Save and close window."""
        self.saveData()
        self.passEvent = True
        self.close()

    def closeWindow(self):
        """Close window."""
        self.passEvent = True
        self.close()

    def closeEvent(self, event):
        """Handle close event."""
        try:
            self.mainWindow.updateAllMapCompleters()
            if (not self.__dataChanged):
                event.accept()
                return
            if (not self.passEvent):
                if (self.isMinimized()):
                    self.showNormal()
                buttonReply = QMessageBox.question(
                    self, _('Save data?'), _("Save data?"),
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                if buttonReply == QMessageBox.Yes:
                    self.saveData()
            event.accept()
        except Exception:
            module_logger.exception("message")