Beispiel #1
0
class MetricsWindowTab(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.name = self.tr("Metrics Window")

        self.inputTextLabel = QLabel(self.tr("Default text:"), self)
        self.inputTextList = QListWidget(self)
        self.inputTextList.setDragDropMode(QAbstractItemView.InternalMove)
        self.addItemButton = QPushButton("+", self)
        self.addItemButton.clicked.connect(self.addItem)
        self.removeItemButton = QPushButton("−", self)
        self.removeItemButton.clicked.connect(self.removeItem)

        layout = QGridLayout(self)
        l = 0
        layout.addWidget(self.inputTextLabel, l, 0, 1, 3)
        l += 1
        layout.addWidget(self.inputTextList, l, 0, 1, 3)
        l += 1
        layout.addWidget(self.addItemButton, l, 0)
        layout.addWidget(self.removeItemButton, l, 1)
        self.setLayout(layout)

        self.readSettings()

    def addItem(self):
        item = QListWidgetItem(self.inputTextList)
        item.setFlags(item.flags() | Qt.ItemIsEditable)
        self.inputTextList.setCurrentItem(item)
        self.inputTextList.editItem(item)
        self.removeItemButton.setEnabled(True)

    def removeItem(self):
        i = self.inputTextList.currentRow()
        self.inputTextList.takeItem(i)
        if not self.inputTextList.count():
            self.removeItemButton.setEnabled(False)

    def readSettings(self):
        self.inputTextList.clear()
        entries = settings.metricsWindowComboBoxItems()
        for entry in entries:
            item = QListWidgetItem(entry, self.inputTextList)
            item.setFlags(item.flags() | Qt.ItemIsEditable)
        if not len(entries):
            self.removeItemButton.setEnabled(False)

    def writeSettings(self):
        entries = []
        for i in range(self.inputTextList.count()):
            item = self.inputTextList.item(i)
            entries.append(item.text())
        settings.setMetricsWindowComboBoxItems(entries)
Beispiel #2
0
class PatchesPage(QtWidgets.QWizardPage):
    def __init__(self, Wizard, parent=None):
        super(PatchesPage, self).__init__(parent)

        self.base = Wizard.base

        self.setTitle(self.tr("Patches page"))
        self.setSubTitle(self.tr("Select patches"))

        self.addButton = QPushButton("+")
        self.removeButton = QPushButton("-")
        patchesLabel = QLabel("Patches")
        self.listPatches = QListWidget()
        self.addButton.setMaximumWidth(68)
        self.addButton.setMaximumHeight(60)
        self.addButton.clicked.connect(self.openPatchesPageFileDialog)
        self.removeButton.setMaximumWidth(68)
        self.removeButton.setMaximumHeight(60)
        self.removeButton.clicked.connect(self.removeItemFromListWidget)

        grid = QGridLayout()
        grid.addWidget(patchesLabel, 0, 0)
        grid.addWidget(self.addButton, 0, 1,)
        grid.addWidget(self.removeButton, 0, 2)
        grid.addWidget(self.listPatches, 1, 0, 1, 0)

        self.setLayout(grid)

    def removeItemFromListWidget(self):
        self.item = self.listPatches.takeItem(self.listPatches.currentRow())
        self.item = None

    def openPatchesPageFileDialog(self):
        brows = QFileDialog()
        self.getPath = brows.getOpenFileName(self,
                                             "Choose patches",
                                             "/home",
                                             "All files (*)")
        self.newPath = self.getPath[0]
        self.listPatches.addItem(self.newPath)

    def validatePage(self):
        self.itemsCount = self.listPatches.count()
        self.pathes = []
        for i in range(0, self.itemsCount):
            self.pathes.append(self.listPatches.item(i).text())

        self.base.apply_patches(self.pathes)
        self.base.run_patched_sources_analysis()
        return True

    def nextId(self):
        return Wizard.PageScripts
Beispiel #3
0
class SearchCity(QDialog):
    id_signal = pyqtSignal([tuple])
    city_signal = pyqtSignal([tuple])
    country_signal = pyqtSignal([tuple])

    def __init__(self, accurate_url, appid, parent=None):
        super(SearchCity, self).__init__(parent)
        self.settings = QSettings()
        self.delay = 1000
        self.search_string = self.tr('Searching...')
        self.timer = QTimer()
        self.accurate_url = accurate_url
        self.suffix = '&type=like&mode=xml' + appid
        self.layout = QVBoxLayout()
        self.lineLayout = QHBoxLayout()
        self.buttonSearch = QPushButton()
        self.buttonSearch.setIcon(QIcon(':/find'))
        self.buttonSearch.clicked.connect(self.search)
        self.line_search = QLineEdit(QCoreApplication.translate(
            'Search city dialogue', 'Start typing the city...', ''))
        self.line_search.selectAll()
        self.listWidget = QListWidget()
        self.status = QLabel()
        self.lineLayout.addWidget(self.line_search)
        self.lineLayout.addWidget(self.buttonSearch)
        self.layout.addLayout(self.lineLayout)
        self.layout.addWidget(self.listWidget)
        self.layout.addWidget(self.status)
        self.buttonLayout = QHBoxLayout()
        self.buttonLayout.addStretch()
        self.buttonOk = QPushButton(self.tr('&Ok'))
        self.buttonOk.setEnabled(False)
        self.buttonCancel = QPushButton(self.tr('&Cancel'))
        self.buttonLayout.addWidget(self.buttonOk)
        self.buttonLayout.addWidget(self.buttonCancel)
        self.layout.addLayout(self.buttonLayout)
        self.setMinimumWidth(int(len(self.line_search.text())*20))
        self.setLayout(self.layout)
        self.line_search.returnPressed.connect(self.search)
        self.line_search.textChanged.connect(self.timer_run)
        self.buttonOk.clicked.connect(self.accept)
        self.buttonCancel.clicked.connect(self.reject)
        self.listWidget.itemSelectionChanged.connect(self.buttonCheck)
        self.listWidget.itemDoubleClicked['QListWidgetItem *'].connect(
            self.accept)
        self.restoreGeometry(self.settings.value("SearchCity/Geometry",
                                                 QByteArray()))
        self.timer_search = QTimer(self)
        self.timer_search.timeout.connect(self.search)

    def timer_run(self):
        self.timer_search.start(1000)

    def closeEvent(self, event):
        self.settings.setValue("SearchCity/Geometry", self.saveGeometry())

    def moveEvent(self, event):
        self.settings.setValue("SearchCity/Geometry", self.saveGeometry())

    def resizeEvent(self, event):
        self.settings.setValue("SearchCity/Geometry", self.saveGeometry())

    def buttonCheck(self):
        '''Enable OK button if an item is selected'''
        row = self.listWidget.currentRow()
        item = self.listWidget.item(row)
        if item is not None:
            self.buttonOk.setEnabled(True)

    def accept(self):
        row = self.listWidget.currentRow()
        item = self.listWidget.item(row)
        if item is not None:
            selected_city = item.text()
            city_list = selected_city.split(' - ')
            for c in range(len(city_list)):
                city_list[c] = city_list[c].strip()
            id_ = 'ID', city_list[0]
            city = 'City', city_list[1]
            country = 'Country', city_list[2]
            self.id_signal[tuple].emit(id_)
            self.city_signal[tuple].emit(city)
            self.country_signal[tuple].emit(country)
        QDialog.accept(self)

    def thread_terminate(self):
        if hasattr(self, 'workThread'):
            if self.workThread.isRunning():
                self.workThread.terminate()

    def search(self):
        self.timer_search.stop()
        self.city = (self.line_search.text())
        self.thread_terminate()
        if len(self.city) < 3:
            self.status.setText(self.tr('Please type more than three letters'))
            return
        self.lista = []
        self.errorStatus = False
        self.buttonOk.setEnabled(False)
        self.listWidget.clear()
        self.status.setText(self.search_string)
        self.workThread = WorkThread(self.accurate_url, self.city, self.suffix)
        self.workThread.setTerminationEnabled(True)
        self.workThread.city_signal['QString'].connect(self.addlist)
        self.workThread.finished.connect(self.result)
        self.workThread.error['QString'].connect(self.error)
        self.workThread.searching['QString'].connect(self.searching)
        self.workThread.started.connect(self.thread_started)
        self.timer.singleShot(self.delay, self.threadstart)

    def searching(self, message):
        '''Display a status message when searching takes a while'''
        self.status.setText(message)

    def thread_started(self):
        '''Force the "searching" status message'''
        self.status.setText(self.search_string)

    def threadstart(self):
        self.workThread.start()

    def addlist(self, city):
        logging.debug('Found: ' + str(city))
        if city not in self.lista:
            self.lista.append(city)
            self.errorStatus = False

    def error(self, e):
        self.delay = 2000
        logging.error(e)
        self.status.setText(e)
        self.adjustSize()
        self.errorStatus = True

    def result(self):
        if self.errorStatus:
            return
        if len(self.line_search.text()) < 3:
            self.thread_terminate()
            self.status.clear()
            return
        self.delay = 1000
        # Clear the listWidget elements from an interrupted thread
        self.listWidget.clear()
        self.listWidget.addItems(self.lista)
        number_cities = len(self.lista)
        cities_text = ''
        if number_cities == 0:
            cities_text = self.tr('No results')
        elif number_cities == 1:
            cities_text = self.tr('Found {0} city').format(number_cities)
        elif number_cities > 1:
            cities_text = self.tr('Found {0} cities').format(number_cities)
        self.status.setText(cities_text)
Beispiel #4
0
class RSLWidget(QWidget):
    # Need a signal to send back to RSLCreator when RSLWidget is closed to tell
    # it to close the parent window.
    signal = pyqtSignal()

    def __init__(self, station_list):
        """ Initialize the RSLCreator widget inside the creator main window """
        super().__init__()

        layout = QGridLayout(self)

        # Create a QListWidget to hold the source station list
        self.textbox = QListWidget(self)
        self.textbox.show()
        self.textbox.setDragEnabled(True)
        layout.addWidget(self.textbox, 1, 0, 12, 1)

        # Add a title above the source station list
        lbl = QLabel("Master Station List")
        lbl.setAlignment(Qt.AlignCenter | Qt.AlignCenter)
        layout.addWidget(lbl, 0, 0)

        # If the user cancelled out of selecting a source station list, or
        # requested a non-existent one, capture the error here and warn them.
        # Otherwise, display stations in textbox
        error = re.compile("^ERROR:")
        if isinstance(station_list, str) and error.match(station_list):
            self.textbox.addItem(station_list)
        else:
            self.display_station(station_list)

        # Create a QListWidget to hold the selected stations to be saved to
        # the RSL file.
        self.rslbox = QListWidget(self)
        self.rslbox.setAcceptDrops(True)
        layout.addWidget(self.rslbox, 1, 2, 12, 1)

        # Add a title above the RSL file
        lbl = QLabel("RSL file")
        lbl.setAlignment(Qt.AlignCenter | Qt.AlignCenter)
        layout.addWidget(lbl, 0, 2)

        # Add arrows that move stuff back and forth between boxes
        select = QPushButton('->', self)
        layout.addWidget(select, 1, 1)
        select.clicked.connect(self.select_station)

        remove = QPushButton('<-', self)
        layout.addWidget(remove, 2, 1)
        remove.clicked.connect(self.remove_station)

        # Add a Save button to save RSL
        save = QPushButton('Save', self)
        layout.addWidget(save, 11, 1)
        save.clicked.connect(self.saveRSL)

    def display_station(self, station_list):
        """ Load stations from station_list into textbox """
        for stn in station_list:
            self.textbox.addItem(stn['id'] + "\t" + stn['number'] + " " +
                                 stn['description'] + "\t" + stn['state'] +
                                 " " + stn['country'] + " " + stn['lat'] +
                                 " " + stn['lon'] + " " + stn['elev'])

    def select_station(self):
        """ Transfer a station from the master list to the RSL list """
        for line in self.textbox.selectedItems():
            self.rslbox.addItem(line.text())

    def remove_station(self):
        """ Remove station from RSL list """
        self.rslbox.takeItem(self.rslbox.currentRow())

    def saveRSL(self):
        """ Save the list of values out of the GUI (rslbox) """

        # Call dialog box to select the file to save the RSL list to
        self.loader = FileSelector("saveRsl")
        self.outfile = self.loader.get_file()

        # Write the RSL list to the open file
        fp = open(os.path.relpath(self.outfile, start=os.getcwd()), 'w')
        for item in range(self.rslbox.count()):
            # Only save the beginning of the file until the first space. This
            # will get the id, or if blank, the number.
            fp.write(
                str(self.rslbox.item(item).data(0)).strip().split(' ', 1)[0] +
                "\n")
        fp.close()

        # close window
        self.signal.emit()
        self.close()

    def get_rsl_filename(self):
        return (self.outfile)
Beispiel #5
0
class Bearing(QMainWindow):
    ReadThreadStart_Singal = pyqtSignal(int)
    ManualControlExecute_Signal = pyqtSignal(object, object)
    ManualControlForward_Signal = pyqtSignal(object)
    ManualControlReveres_Signal = pyqtSignal(object)
    ManualControlReturnZeroPoint_Signal = pyqtSignal(object)
    AutoControlCommandExecute_Signal = pyqtSignal(object)

    def __init__(self):
        super(Bearing, self).__init__()

        self.save_path = sys.path[0]
        self._serial1_is_open = 0
        self._serial2_is_open = 0
        self._plot_times = 0
        self._plot_first_data = []
        self._plot_last_data = []
        self._experiment2_data_resistance = []
        self._experiment2_data_current = []

        self.initUi()
        self.set_attribute()
        self.set_layout()
        self.init_conf()

        self._serial_1 = Bearing_Serial(
            self._serial_information1_port_combox.currentText(),
            self._serial_information1_baudrate_combox.currentText(), 'modbus')
        self._serial_2 = Bearing_Serial(
            self._serial_information2_port_combox.currentText(),
            self._serial_information2_baudrate_combox.currentText(), 'serial')
        self._serial_3 = Bearing_Serial(
            self._serial_information3_port_combox.currentText(), 115200,
            'serial')
        self._handle_data = Handle_Data()
        self._read_thread = Read_Thread(self._serial_1, self._serial_2)
        self._motor = Motor(self._serial_3)

        self.signal_connect_slot()
        self.setFixedSize(self.sizeHint())
        self.setWindowTitle('Show Data')
        self.show()

    def initUi(self):
        self.widget = QWidget()
        self.setCentralWidget(self.widget)
        self.statusBar().showMessage(' ')

        self._serial_information1_groupbox = QGroupBox('串口1')
        self._serial_information1_port_combox = QComboBox()
        self._serial_information1_baudrate_combox = QComboBox()
        self._serial_information1_pushbutton = QPushButton('连接')

        self._serial_information2_groupbox = QGroupBox('串口2')
        self._serial_information2_port_combox = QComboBox()
        self._serial_information2_baudrate_combox = QComboBox()
        self._serial_information2_pushbutton = QPushButton('连接')

        self._experiment1_groupbox = QGroupBox('实验一')
        self._experiment1_datasize_label = QLabel('数据采集量')
        self._experiment1_datasize_lineedit = QLineEdit('400')
        self._experiment1_start_pushbutton = QPushButton('开始')
        self._experiment1_stop_pushbutton = QPushButton('停止')
        self._experiment1_clear_pushbutton = QPushButton('清除')

        self._experiment2_groupbox = QGroupBox('实验二')
        self._experiment2_current_label = QLabel('电流大小')
        self._experiment2_current_lineedit = QLineEdit('0.0')
        self._experiment2_readresistance_pushbutton = QPushButton('获取阻值')
        self._experiment2_save_pushbutton = QPushButton('保存电流-电阻')

        self._plot_tabwidget = QTabWidget()
        self._plot_fr_widget = QWidget()
        self._plot_fd_widget = QWidget()
        self._plot_tabwidget.addTab(self._plot_fr_widget, 'F-R')
        self._plot_tabwidget.addTab(self._plot_fd_widget, 'F-L')

        self._control_serial_groupbox = QGroupBox('控制器串口')
        self._serial_information3_port_combox = QComboBox()
        self._serial_information3_pushbutton = QPushButton('连接')

        self._manualcontrol_groupbox = QGroupBox('手动控制')
        self._manualcontrol_step_label = QLabel('步 数')
        self._manualcontrol_step_lineedit = QLineEdit()
        self._manualcontrol_speed_label = QLabel('速 度')
        self._manualcontrol_speed_lineedit = QLineEdit()
        self._manualcontrol_execute_pushbutton = QPushButton('执行')
        self._manualcontrol_returnzeropoint_pushbutton = QPushButton('返回零点')
        self._manualcontrol_forward_pushbutton = QPushButton('正转')
        self._manualcontrol_reverse_pushbutton = QPushButton('反转')
        self._manualcontrol_pause_pushbutton = QPushButton('暂停')
        self._manualcontrol_stop_pushbutton = QPushButton('停止')

        self._autocontrol_groupbox = QGroupBox('自动控制')
        self._autocontrol_command_combox = QComboBox()
        self._autocontrol_command_lineedit = QLineEdit()
        self._autocontrol_command_add_pushbutton = QPushButton('添加')
        self._autocontrol_command_del_pushbutton = QPushButton('删除')
        self._autocontrol_command_execute_pushbutton = QPushButton('执行')
        self._autocontrol_pause_pushbutton = QPushButton('暂停')
        self._autocontrol_stop_pushbutton = QPushButton('停止')
        self._autocontrol_listwidget = QListWidget()

    def set_attribute(self):
        port = []
        baudrate = ['4800', '9600', '19200', '38400', '56000', '115200']
        command = ['速度', '步数', '暂停(秒)', '循环次数']
        left_width = 250
        right_width = 300
        for i in range(1, 9):
            port.append('COM%d' % i)
        self._serial_information1_port_combox.addItems(port)
        self._serial_information1_baudrate_combox.addItems(baudrate)
        self._serial_information2_port_combox.addItems(port)
        self._serial_information2_baudrate_combox.addItems(baudrate)
        self._serial_information1_groupbox.setMaximumWidth(left_width / 2)
        self._serial_information2_groupbox.setMaximumWidth(left_width / 2)
        self._serial_information1_groupbox.setMaximumHeight(left_width * 2 / 3)
        self._serial_information2_groupbox.setMaximumHeight(left_width * 2 / 3)
        self._experiment1_groupbox.setMaximumWidth(left_width)
        self._experiment2_groupbox.setMaximumWidth(left_width)

        self.figure1 = Figure()
        self.canvas1 = FigureCanvas(self.figure1)
        self.ax1 = self.figure1.add_subplot(111)
        self.toolbar1 = NavigationToolbar(self.canvas1, self.widget)
        self.PlotBox1 = QVBoxLayout(self._plot_fr_widget)
        self.PlotBox1.addWidget(self.toolbar1)
        self.PlotBox1.addWidget(self.canvas1)
        self.figure2 = Figure()
        self.canvas2 = FigureCanvas(self.figure2)
        self.ax2 = self.figure2.add_subplot(111)
        self.toolbar2 = NavigationToolbar(self.canvas2, self.widget)
        self.PlotBox2 = QVBoxLayout(self._plot_fd_widget)
        self.PlotBox2.addWidget(self.toolbar2)
        self.PlotBox2.addWidget(self.canvas2)
        self.ax1.set_title('F-R')
        self.ax1.set_xlabel('F/N')
        self.ax1.set_ylabel('R/Ω')
        self.ax2.set_title('F-L')
        self.ax2.set_ylabel('F/N')
        self.ax2.set_xlabel('L/mm')
        self.canvas1.draw()
        self.canvas2.draw()

        self._serial_information3_port_combox.addItems(port)
        self._serial_information3_port_combox.setMaximumHeight(
            self._serial_information3_pushbutton.height())
        self._manualcontrol_step_label.setAlignment(Qt.AlignHCenter)
        self._manualcontrol_speed_label.setAlignment(Qt.AlignHCenter)
        self._manualcontrol_step_lineedit.setPlaceholderText('步数<2000')
        self._manualcontrol_speed_lineedit.setPlaceholderText('速度<300')
        self._manualcontrol_groupbox.setMaximumWidth(right_width)
        self._autocontrol_groupbox.setMaximumWidth(right_width)

        self._autocontrol_command_combox.addItems(command)

    def set_layout(self):
        self._serial1_layout = QVBoxLayout(self._serial_information1_groupbox)
        self._serial1_layout.addWidget(self._serial_information1_port_combox)
        self._serial1_layout.addWidget(
            self._serial_information1_baudrate_combox)
        self._serial1_layout.addWidget(self._serial_information1_pushbutton)

        self._serial2_layout = QVBoxLayout(self._serial_information2_groupbox)
        self._serial2_layout.addWidget(self._serial_information2_port_combox)
        self._serial2_layout.addWidget(
            self._serial_information2_baudrate_combox)
        self._serial2_layout.addWidget(self._serial_information2_pushbutton)

        self._experiment1_layout = QGridLayout(self._experiment1_groupbox)
        self._experiment1_layout.addWidget(self._experiment1_datasize_label)
        self._experiment1_layout.addWidget(self._experiment1_datasize_lineedit,
                                           0, 1)
        self._experiment1_layout.addWidget(self._experiment1_start_pushbutton,
                                           1, 0, 1, -1)
        self._experiment1_layout.addWidget(self._experiment1_stop_pushbutton,
                                           2, 0, 1, -1)
        self._experiment1_layout.addWidget(self._experiment1_clear_pushbutton,
                                           3, 0, 1, -1)

        self._experiment2_layout = QGridLayout(self._experiment2_groupbox)
        self._experiment2_layout.addWidget(self._experiment2_current_label)
        self._experiment2_layout.addWidget(self._experiment2_current_lineedit,
                                           0, 1)
        self._experiment2_layout.addWidget(
            self._experiment2_readresistance_pushbutton, 1, 0, 1, -1)
        self._experiment2_layout.addWidget(self._experiment2_save_pushbutton,
                                           2, 0, 1, -1)

        self._left_layout = QGridLayout()
        self._left_layout.addWidget(self._serial_information1_groupbox)
        self._left_layout.addWidget(self._serial_information2_groupbox, 0, 1)
        self._left_layout.addWidget(self._experiment1_groupbox, 1, 0, 1, -1)
        self._left_layout.addWidget(self._experiment2_groupbox, 2, 0, 1, -1)

        self._control_serial_layout = QHBoxLayout(
            self._control_serial_groupbox)
        self._control_serial_layout.addWidget(
            self._serial_information3_port_combox)
        self._control_serial_layout.addWidget(
            self._serial_information3_pushbutton)

        self._manualcontrol_layout = QGridLayout(self._manualcontrol_groupbox)
        self._manualcontrol_layout.addWidget(self._manualcontrol_step_label)
        self._manualcontrol_layout.addWidget(self._manualcontrol_step_lineedit,
                                             0, 1)
        self._manualcontrol_layout.addWidget(self._manualcontrol_speed_label,
                                             1, 0)
        self._manualcontrol_layout.addWidget(
            self._manualcontrol_speed_lineedit, 1, 1)
        self._manualcontrol_layout.addWidget(
            self._manualcontrol_execute_pushbutton, 2, 0)
        self._manualcontrol_layout.addWidget(
            self._manualcontrol_returnzeropoint_pushbutton, 2, 1)
        self._manualcontrol_layout.addWidget(
            self._manualcontrol_forward_pushbutton, 3, 0)
        self._manualcontrol_layout.addWidget(
            self._manualcontrol_reverse_pushbutton, 3, 1)
        self._manualcontrol_layout.addWidget(
            self._manualcontrol_pause_pushbutton, 4, 0)
        self._manualcontrol_layout.addWidget(
            self._manualcontrol_stop_pushbutton, 4, 1)

        self._autocontrol_layout = QGridLayout(self._autocontrol_groupbox)
        self._autocontrol_layout.addWidget(self._autocontrol_command_combox)
        self._autocontrol_layout.addWidget(self._autocontrol_command_lineedit,
                                           0, 1)
        self._autocontrol_layout.addWidget(
            self._autocontrol_command_add_pushbutton, 1, 0)
        self._autocontrol_layout.addWidget(
            self._autocontrol_command_del_pushbutton, 1, 1)
        self._autocontrol_layout.addWidget(
            self._autocontrol_command_execute_pushbutton, 2, 0, 1, -1)
        self._autocontrol_layout.addWidget(self._autocontrol_pause_pushbutton,
                                           3, 0)
        self._autocontrol_layout.addWidget(self._autocontrol_stop_pushbutton,
                                           3, 1)
        self._autocontrol_layout.addWidget(self._autocontrol_listwidget, 4, 0,
                                           1, -1)

        self._right_layout = QGridLayout()
        self._right_layout.addWidget(self._control_serial_groupbox, 0, 0, 1,
                                     -1)
        self._right_layout.addWidget(self._manualcontrol_groupbox, 1, 0, 1, -1)
        self._right_layout.addWidget(self._autocontrol_groupbox, 2, 0, 1, -1)

        self._main_layout = QGridLayout()
        self._main_layout.addLayout(self._left_layout, 0, 0)
        self._main_layout.addWidget(self._plot_tabwidget, 0, 1)
        self._main_layout.addLayout(self._right_layout, 0, 2)

        self.widget.setLayout(self._main_layout)

    def signal_connect_slot(self):
        self._serial_information1_pushbutton.clicked.connect(
            self.serial_information1_pushbutton_clicked)
        self._serial_information2_pushbutton.clicked.connect(
            self.serial_information2_pushbutton_clicked)
        self._serial_information3_pushbutton.clicked.connect(
            self.serial_information3_pushbutton_clicked)

        self._experiment1_stop_pushbutton.clicked.connect(
            self.experiment1_stop_pushbutton_clicked)
        self._experiment1_start_pushbutton.clicked.connect(
            self.experiment1_start_pushbutton_clicked)
        self._experiment1_clear_pushbutton.clicked.connect(
            self.experiment1_clear_pushbutton_clicked)

        self._experiment2_current_lineedit.returnPressed.connect(
            self.read_resistance)  # 行编辑框的回车事件处理
        self._experiment2_save_pushbutton.clicked.connect(
            self.experiment2_save_pushbutton_clicked)
        self._experiment2_readresistance_pushbutton.clicked.connect(
            self.read_resistance)

        self._manualcontrol_execute_pushbutton.clicked.connect(
            self.manualcontrol_execute_pushbutton_clicked)
        self._manualcontrol_returnzeropoint_pushbutton.clicked.connect(
            self.manualcontrol_returnzeropoint_pushbutton_clicked)
        self._manualcontrol_forward_pushbutton.clicked.connect(
            self.manualcontrol_forward_pushbutton_clicked)
        self._manualcontrol_reverse_pushbutton.clicked.connect(
            self.manualcontrol_reverse_pushbutton_clicked)
        self._manualcontrol_pause_pushbutton.clicked.connect(
            self.manualcontrol_pause_pushbutton_clicked)
        self._manualcontrol_stop_pushbutton.clicked.connect(
            self.manualcontrol_stop_pushbutton_clicked)

        self._autocontrol_command_combox.activated.connect(
            self.autocontrol_command_lineedit_setFocus)
        self._autocontrol_command_add_pushbutton.clicked.connect(
            self.autocontrol_command_add_pushbutton_clicked)
        self._autocontrol_command_del_pushbutton.clicked.connect(
            self.autocontrol_command_del_pushbutton_clicked)
        self._autocontrol_command_execute_pushbutton.clicked.connect(
            self.autocontrol_command_execute_pushbutton_clicked)
        self._autocontrol_pause_pushbutton.clicked.connect(
            self.autocontrol_pause_pushbutton_clicked)
        self._autocontrol_stop_pushbutton.clicked.connect(
            self.autocontrol_stop_pushbutton_clicked)

        self.ReadThreadStart_Singal.connect(self._read_thread.read_data,
                                            type=Qt.QueuedConnection)
        self.ManualControlExecute_Signal.connect(
            self._motor.manualcontrol_execute)
        self.ManualControlForward_Signal.connect(
            self._motor.manualcontrol_forward)
        self.ManualControlReveres_Signal.connect(
            self._motor.manualcontrol_reverse)
        self.ManualControlReturnZeroPoint_Signal.connect(
            self._motor.manualcontrol_returnzeropoint)
        self.AutoControlCommandExecute_Signal.connect(
            self._motor.autocontrol_command_execute)

        self._serial_1.Warning_Signal.connect(self.warning_message)
        self._serial_2.Warning_Signal.connect(self.warning_message)
        self._serial_3.Warning_Signal.connect(self.warning_message)
        self._read_thread.ReadComplete_Signal.connect(self._handle_data.handle,
                                                      type=Qt.QueuedConnection)
        self._read_thread.SaveData_Signal.connect(
            self._handle_data.save_all_data, type=Qt.QueuedConnection)
        self._handle_data.HandleComplete_Signal.connect(
            self.plot, type=Qt.QueuedConnection)
        self._handle_data.TranData_Signal.connect(self.save_data,
                                                  type=Qt.QueuedConnection)
        self._motor.Warning_Signal.connect(self.warning_message)

    def serial_information1_pushbutton_clicked(self):
        self._serial_1.set_setting(
            self._serial_information1_port_combox.currentText(),
            self._serial_information1_baudrate_combox.currentText(), 'modbus')
        if self._serial_1.open_serial():
            self._serial1_is_open = 1

    def serial_information2_pushbutton_clicked(self):
        self._serial_2.set_setting(
            self._serial_information2_port_combox.currentText(),
            self._serial_information2_baudrate_combox.currentText(), 'serial')
        if self._serial_2.open_serial():
            self._serial2_is_open = 1

    def serial_information3_pushbutton_clicked(self):
        self._serial_3.set_setting(
            self._serial_information3_port_combox.currentText(), 115200,
            'serial')
        self._serial_3.open_serial()
        self._motor.set_serial(self._serial_3)
        self._motor._thread.start()

    def experiment1_start_pushbutton_clicked(self):
        self.ax1.clear()
        self.canvas1.draw()
        self.ax2.clear()
        self.canvas2.draw()
        self.ax1.set_title('F-R')
        self.ax1.set_xlabel('F/N')
        self.ax1.set_ylabel('R/Ω')
        self.ax2.set_title('F-L')
        self.ax2.set_ylabel('F/N')
        self.ax2.set_xlabel('L/mm')
        if (self._serial1_is_open == 0) | (self._serial2_is_open == 0):
            self.warning_message('串口未打开!')
            return 0
        QMessageBox().warning(self, 'Warning',
                              u"请确保台式万用表处于Fast Print模式,\n并确保传感器段通电!",
                              QMessageBox.Yes)
        self._plot_times = 0
        self._plot_first_data = []
        self._plot_last_data = []
        self._handle_data.clear_all_data()
        self._read_thread._thread.start()
        self._handle_data._thread.start()
        self._read_thread.set_serial(self._serial_1, self._serial_2)
        self.ReadThreadStart_Singal.emit(
            int(self._experiment1_datasize_lineedit.text()))

    def experiment1_stop_pushbutton_clicked(self):
        self._read_thread.stop_function()

    def experiment1_clear_pushbutton_clicked(self):
        self.ax1.clear()
        self.canvas1.draw()
        self.ax2.clear()
        self.canvas2.draw()
        self.ax1.set_title('F-R')
        self.ax1.set_xlabel('F/N')
        self.ax1.set_ylabel('R/Ω')
        self.ax2.set_title('F-L')
        self.ax2.set_ylabel('F/N')
        self.ax2.set_xlabel('L/mm')

    def experiment2_save_pushbutton_clicked(self):
        self.save_path = QFileDialog.getSaveFileName(self, "选择目录",
                                                     self.save_path)[0]
        if self.save_path == '':
            return 0
        data = pd.DataFrame(columns=['resistance', 'current'])
        length = len(self._experiment2_data_resistance)
        i = 0
        while i < length:
            data = data.append(
                {
                    'resistance': self._experiment2_data_resistance[i],
                    'current': self._experiment2_data_current[i]
                },
                ignore_index=True)
            i += 1
        if not os.path.exists(self.save_path + '.csv'):
            data.to_csv(self.save_path + '.csv')
            self.statusBar().showMessage('Save %s Done' % self.save_path)
        else:
            if QMessageBox().question(self, 'Warning', u"文件已存在!\n是否覆盖?",
                                      QMessageBox.Yes
                                      | QMessageBox.No) == 16384:
                os.remove(self.save_path + '.csv')
                data.to_csv(self.save_path + '.csv')
                self.statusBar().showMessage('Save %s Done' % self.save_path)

    def autocontrol_command_lineedit_setFocus(self):
        self._autocontrol_command_lineedit.selectAll()
        self._autocontrol_command_lineedit.setFocus()

    def manualcontrol_execute_pushbutton_clicked(self):
        self.ManualControlExecute_Signal.emit(
            self._manualcontrol_speed_lineedit.text(),
            self._manualcontrol_step_lineedit.text())

    def manualcontrol_returnzeropoint_pushbutton_clicked(self):
        self.experiment1_start_pushbutton_clicked()
        self.ManualControlReturnZeroPoint_Signal.emit(self._handle_data)

    def manualcontrol_forward_pushbutton_clicked(self):
        self.ManualControlForward_Signal.emit(
            self._manualcontrol_speed_lineedit.text())

    def manualcontrol_reverse_pushbutton_clicked(self):
        self.ManualControlReveres_Signal.emit(
            self._manualcontrol_speed_lineedit.text())

    def manualcontrol_pause_pushbutton_clicked(self):
        if self._manualcontrol_pause_pushbutton.text() == '暂停':
            self._serial_3.send_data(self._motor.pause_command())
            self._manualcontrol_pause_pushbutton.setText('继续')
        elif self._manualcontrol_pause_pushbutton.text() == '继续':
            self._serial_3.send_data(self._motor.continue_command())
            self._manualcontrol_pause_pushbutton.setText('暂停')

    def manualcontrol_stop_pushbutton_clicked(self):
        if self._manualcontrol_pause_pushbutton.text() == '暂停':
            self._serial_3.send_data(self._motor.pause_command())
            self._serial_3.send_data(self._motor.stop_motor_command())
        elif self._manualcontrol_pause_pushbutton.text() == '继续':
            self._serial_3.send_data(self._motor.stop_motor_command())
            self._manualcontrol_pause_pushbutton.setText('暂停')

    def autocontrol_command_add_pushbutton_clicked(self):
        if self._autocontrol_command_lineedit.text() == '':
            self.warning_message('填写正确参数!')
            return 0
        elif self._autocontrol_command_combox.currentText() == '速度':
            self._autocontrol_listwidget.insertItem(
                self._autocontrol_listwidget.currentRow() + 1,
                '设置速度 ' + self._autocontrol_command_lineedit.text())
        elif self._autocontrol_command_combox.currentText() == '步数':
            self._autocontrol_listwidget.insertItem(
                self._autocontrol_listwidget.currentRow() + 1,
                '设置步数 ' + self._autocontrol_command_lineedit.text())
        elif self._autocontrol_command_combox.currentText() == '暂停(秒)':
            self._autocontrol_listwidget.insertItem(
                self._autocontrol_listwidget.currentRow() + 1,
                '设置暂停 ' + self._autocontrol_command_lineedit.text())
        elif self._autocontrol_command_combox.currentText() == '循环次数':
            loop_digtal = self._autocontrol_command_lineedit.text().replace(
                ',', ',')
            [start, times] = loop_digtal.split(',')
            self._autocontrol_listwidget.insertItem(
                self._autocontrol_listwidget.currentRow() + 1,
                '设置循环 %s,%s' % (start, times))
        self._autocontrol_listwidget.setCurrentRow(
            self._autocontrol_listwidget.count() - 1)
        self.autocontrol_command_lineedit_setFocus()

    def autocontrol_command_del_pushbutton_clicked(self):
        self._autocontrol_listwidget.takeItem(
            self._autocontrol_listwidget.currentRow())

    def autocontrol_command_execute_pushbutton_clicked(self):  # 无法嵌套循环
        command1 = []
        command = []
        loopTimes = 0
        times = 1
        for i in range(self._autocontrol_listwidget.count() - 1, -1, -1):
            if '设置循环' in self._autocontrol_listwidget.item(i).text():
                loopTimes += 1
                [start, times] = self._autocontrol_listwidget.item(
                    i).text().split()[1].split(',')
                end = i
        if loopTimes > 1:
            self.warning_message('不能处理嵌套循环!')
            return 0

        for i in range(self._autocontrol_listwidget.count()):
            if '设置步数' in self._autocontrol_listwidget.item(i).text():
                step = self._autocontrol_listwidget.item(i).text().split()[1]
                for j in range(i, -1, -1):
                    if '设置速度' in self._autocontrol_listwidget.item(j).text():
                        speed = self._autocontrol_listwidget.item(
                            j).text().split()[1]
                        break
                    speed = 100
                command1.append(self._motor.appoint_command(speed, step))
            elif '设置暂停' in self._autocontrol_listwidget.item(i).text():
                command1.append(
                    'sleep ' +
                    self._autocontrol_listwidget.item(i).text().split(' ')[1])
            else:
                command1.append(' ')

        if loopTimes > 0:
            for k1 in range(int(times)):
                for k2 in range(int(start) - 1, int(end)):
                    if command1[k2] != ' ':
                        command.append(command1[k2])
        else:
            for k1 in range(command1.count(' ')):
                command1.remove(' ')
            command = command1
        self.AutoControlCommandExecute_Signal.emit(command)

    def autocontrol_pause_pushbutton_clicked(self):
        if self._autocontrol_pause_pushbutton.text() == '暂停':
            self._serial_3.send_data(self._motor.pause_command())
            self._autocontrol_pause_pushbutton.setText('继续')
        elif self._autocontrol_pause_pushbutton.text() == '继续':
            self._serial_3.send_data(self._motor.continue_command())
            self._autocontrol_pause_pushbutton.setText('暂停')

    def autocontrol_stop_pushbutton_clicked(self):
        if self._autocontrol_pause_pushbutton.text() == '暂停':
            self._serial_3.send_data(self._motor.pause_command())
            self._serial_3.send_data(self._motor.stop_motor_command())
        elif self._autocontrol_pause_pushbutton.text() == '继续':
            self._serial_3.send_data(self._motor.stop_motor_command())
            self._autocontrol_pause_pushbutton.setText('暂停')

    def warning_message(self, content):
        QMessageBox().warning(self, 'Warning', content, QMessageBox.Yes)

    def init_conf(self):
        if not os.path.exists(sys.path[0] + "\ZhiZuo.conf"):
            with open(sys.path[0] + '\ZhiZuo.conf', 'w+') as f:
                cf = configparser.ConfigParser()
                cf.add_section('COM1')
                cf.set('COM1', 'port',
                       self._serial_information1_port_combox.currentText())
                cf.set('COM1', 'baudrate',
                       self._serial_information1_baudrate_combox.currentText())
                cf.add_section('COM2')
                cf.set('COM2', 'port',
                       self._serial_information2_port_combox.currentText())
                cf.set('COM2', 'baudrate',
                       self._serial_information2_baudrate_combox.currentText())
                cf.add_section('COM3')
                cf.set('COM3', 'port',
                       self._serial_information3_port_combox.currentText())
                cf.add_section('DATA')
                cf.set('DATA', 'size',
                       self._experiment1_datasize_lineedit.text())
                cf.write(f)
        else:
            cf = configparser.ConfigParser()
            cf.read(sys.path[0] + '\ZhiZuo.conf')
            try:
                self._serial_information1_port_combox.setCurrentText(
                    cf.get('COM1', 'port'))
                self._serial_information1_baudrate_combox.setCurrentText(
                    cf.get('COM1', 'baudrate'))
                self._serial_information2_port_combox.setCurrentText(
                    cf.get('COM2', 'port'))
                self._serial_information2_baudrate_combox.setCurrentText(
                    cf.get('COM2', 'baudrate'))
                self._serial_information3_port_combox.setCurrentText(
                    cf.get('COM3', 'port'))
                self._experiment1_datasize_lineedit.setText(
                    cf.get('DATA', 'size'))
            except:
                os.remove(sys.path[0] + "\ZhiZuo.conf")

    def plot(self, F, R, D):
        if self._plot_times == 0:
            self._plot_first_data = [F, R, D]
            self._plot_last_data = [F, R, D]
        else:
            self.ax1.plot([self._plot_last_data[0], F],
                          [self._plot_last_data[1], R])
            self.ax1.plot([self._plot_last_data[0], F],
                          [self._plot_last_data[1], R], 'k.')
            self.ax2.plot([
                self._plot_last_data[0] - self._plot_first_data[0],
                F - self._plot_first_data[0]
            ], [
                self._plot_last_data[2] - self._plot_first_data[2],
                R - self._plot_first_data[2]
            ])
            self.ax2.plot([
                self._plot_last_data[0] - self._plot_first_data[0],
                F - self._plot_first_data[0]
            ], [
                self._plot_last_data[2] - self._plot_first_data[2],
                R - self._plot_first_data[2]
            ], 'k.')
            self._plot_last_data = [F, R, D]
        self._plot_times += 1
        self.canvas1.draw()
        self.canvas2.draw()
        self.statusBar().showMessage('Now is %d' % self._plot_times)

    def read_resistance(self):
        if self._serial2_is_open == 0:
            self.warning_message('串口未打开!')
            return 0
        self._experiment2_data_current.append(
            self._experiment2_current_lineedit.text())
        self._experiment2_data_resistance.append(
            self._handle_data.handle_resistance(self._serial_2.read_data()))
        self._experiment2_current_lineedit.selectAll()
        self.statusBar().showMessage(
            'Get %s Succed' % (self._experiment2_current_lineedit.text()))

    def save_data(self, alldata):
        self.save_path = QFileDialog.getSaveFileName(self, "选择目录",
                                                     self.save_path)[0]
        if self.save_path == '':
            return 0
        data = pd.DataFrame(columns=[
            'force_original', 'force_switch', 'force_stress', 'resistance',
            'displacement'
        ])
        length = len(alldata.force_original)
        i = 0
        while i < length:
            data = data.append(
                {
                    'force_original': alldata.force_original[i],
                    'force_switch': alldata.force_switch[i],
                    'force_stress': alldata.force_stress[i],
                    'resistance': alldata.resistance[i],
                    'displacement': alldata.displacement[i],
                    'time': alldata.time[i] - alldata.time[0]
                },
                ignore_index=True)
            i += 1
        if not os.path.exists(self.save_path + '.csv'):
            data.to_csv(self.save_path + '.csv')
            self.figure1.savefig(self.save_path + '-FR' + '.jpg', dpi=1000)
            self.figure2.savefig(self.save_path + '-FL' + '.jpg', dpi=1000)
            self.statusBar().showMessage('Save %s Done' % self.save_path)
        else:
            if QMessageBox().question(self, 'Warning', u"文件已存在!\n是否覆盖?",
                                      QMessageBox.Yes
                                      | QMessageBox.No) == 16384:
                os.remove(self.save_path + '.csv')
                os.remove(self.save_path + '-FR' + '.jpg')
                os.remove(self.save_path + '-FL' + '.jpg')
                data.to_csv(self.save_path + '.csv')
                self.figure1.savefig(self.save_path + '-FR' + '.jpg', dpi=1000)
                self.figure2.savefig(self.save_path + '-FL' + '.jpg', dpi=1000)
                self.statusBar().showMessage('Save %s Done' %
                                             (self.save_path + '.csv'))
                self.statusBar().showMessage('Save %s Done' % self.save_path)

    def closeEvent(self, *args, **kwargs):
        cf = configparser.ConfigParser()
        cf.read(sys.path[0] + '\ZhiZuo.conf')
        cf.set('COM1', 'port',
               self._serial_information1_port_combox.currentText())
        cf.set('COM1', 'baudrate',
               self._serial_information1_baudrate_combox.currentText())
        cf.set('COM2', 'port',
               self._serial_information2_port_combox.currentText())
        cf.set('COM2', 'baudrate',
               self._serial_information2_baudrate_combox.currentText())
        cf.set('COM3', 'port',
               self._serial_information3_port_combox.currentText())
        cf.set('DATA', 'size', self._experiment1_datasize_lineedit.text())
        with open(sys.path[0] + '\ZhiZuo.conf', 'w') as f:
            cf.write(f)
Beispiel #6
0
class App(QWidget):
    def __init__(self):
        super(App, self).__init__()
        self.title = 'Soft Shadow Generation'
        self.left = 400
        self.top = 400
        self.width = 1280
        self.height = 480
        self.ibl_counter = 0

        self.template_label = QLabel(self)

        self.cutout_label = QLabel(self)
        self.cutout_label.move(256, 0)

        self.ibl_label = QLabel(self)
        self.ibl_label.move(256 * 2, 0)

        self.next_btn = QPushButton("next")
        self.next_btn.move(256 * 4, 0)
        self.next_btn.clicked.connect(self.next_scene)

        # light list
        self.light_list = QListWidget()
        self.light_list.itemClicked.connect(self.light_item_clicked)

        # size slider
        self.size_slider_label = QLabel(self)
        self.size_slider_label.setText('light size')
        self.size_slider = QSlider(Qt.Horizontal)
        self.size_slider.setSingleStep(1)
        self.size_slider.valueChanged.connect(self.size_change)

        # scale slider
        self.scale_slider_label = QLabel(self)
        self.scale_slider_label.setText('scale size')
        self.scale_slider = QSlider(Qt.Horizontal)
        self.scale_slider.setSingleStep(1)
        self.scale_slider.valueChanged.connect(self.scale_change)

        label_group = QGroupBox("Composite")
        self.hlayout = QtWidgets.QHBoxLayout()
        self.hlayout.addWidget(self.template_label)
        self.hlayout.addWidget(self.cutout_label)
        self.hlayout.addWidget(self.ibl_label)
        label_group.setLayout(self.hlayout)

        button_group = QGroupBox("Light control")
        self.vlayout = QtWidgets.QVBoxLayout()
        self.vlayout.addWidget(self.light_list)
        self.vlayout.addWidget(self.size_slider_label)
        self.vlayout.addWidget(self.size_slider)
        self.vlayout.addWidget(self.scale_slider_label)
        self.vlayout.addWidget(self.scale_slider)
        self.vlayout.addWidget(self.next_btn)
        button_group.setLayout(self.vlayout)

        grid = QGridLayout()
        grid.addWidget(label_group, 0, 0)
        grid.addWidget(button_group, 0, 1)
        self.setLayout(grid)

        self.cur_ibl = np.zeros((256, 512, 3))
        self.cur_size_min, self.cur_size_max = 0.03, 0.2
        self.cur_scale_min, self.cur_scale_max = 0.0, 3.0

        self.cur_ibl_cmd = -1
        self.ibl_cmds = []
        self.init_ibl_state()
        self.add_cur_ibl_state()

        # test imgs
        self.num_test = 4
        self.final_imgs = [
            join('imgs', '{:03d}_final.png'.format(i))
            for i in range(1, self.num_test + 1)
        ]
        self.mask_imgs = [
            join('imgs', '{:03d}_mask.png'.format(i))
            for i in range(1, self.num_test + 1)
        ]
        self.cutout_imgs = [
            join('imgs', '{:03d}_cutout.png'.format(i))
            for i in range(1, self.num_test + 1)
        ]
        self.cur_exp = 0

        # output folder
        self.cache_folder = 'us_cache'
        os.makedirs(self.cache_folder, exist_ok=True)
        self.initUI()

    def init_ibl_state(self):
        self.cur_x, self.cur_y, self.cur_size, self.cur_scale = 0.0, 0.0, 0.04, 1.0
        self.update_slider()

    def update_slider(self):
        size_factor = (self.cur_size - self.cur_size_min) / (
            self.cur_size_max - self.cur_size_min)
        scale_factor = (self.cur_scale - self.cur_scale_min) / (
            self.cur_scale_max - self.cur_scale_min)

        self.scale_slider.setValue(scale_factor * 99.0)
        self.size_slider.setValue(size_factor * 99.0)

    def reset_ibl_cmds(self):
        self.ibl_cmds = []
        self.init_ibl_state()
        self.cur_ibl_cmd = -1
        self.light_list.clear()

    def add_cur_ibl_state(self):
        self.cur_ibl_cmd = len(self.ibl_cmds)
        self.ibl_counter += 1
        self.light_list.addItem('{}'.format(self.ibl_counter))
        self.light_list.setCurrentItem(self.light_list.item(self.cur_ibl_cmd))

        self.ibl_cmds.append(
            (self.cur_x, self.cur_y, self.cur_size, self.cur_scale))

    def light_item_clicked(self, item):
        # self.cur_ibl_cmd = int(item.text())
        self.cur_ibl_cmd = self.light_list.currentRow()
        print('select current item: ', self.cur_ibl_cmd)

        # update current status
        self.cur_x, self.cur_y, self.cur_size, self.cur_scale = self.ibl_cmds[
            self.cur_ibl_cmd]

        # update slider
        self.update_slider()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.update_cur_labels()
        self.setFixedSize(self.width, self.height)

        self.show()

    def set_image(self, label, img_path):
        # Create widget
        pixmap = QPixmap(img_path)
        label.setPixmap(pixmap)

    def set_np_img(self, label, img_np):
        if len(img_np.shape) == 2:
            img_np = np.repeat(img_np[:, :, np.newaxis], 3, axis=2)

        pixmap = QPixmap(self.to_qt_img(img_np))
        label.setPixmap(pixmap)

    def to_qt_img(self, np_img):
        if np_img.dtype != np.uint8:
            np_img = np.clip(np_img, 0.0, 1.0)
            np_img = np_img * 255.0
            np_img = np_img.astype(np.uint8)

        h, w, c = np_img.shape
        # bytesPerLine = 3 * w
        return QImage(np_img.data, w, h, QImage.Format_RGB888)

    def update_cur_labels(self):
        self.cur_final, self.cur_cutout, self.cur_mask = self.load_cur_np(
            self.cur_exp)
        self.set_np_img(self.template_label, self.cur_final)
        self.set_np_img(self.cutout_label, self.cur_cutout)
        self.set_np_img(self.ibl_label, self.cur_ibl)

        self.cur_user_comopsite = np.copy(self.cur_cutout)
        self.cur_user_ibl = np.copy(self.cur_ibl)
        self.ibl_label.setFocus()

    def save_current(self):
        cutout_pix, ibl_pix = self.cur_user_comopsite, self.cur_user_ibl
        cur_time = datetime.now().strftime('%Y_%m_%d_%H_%M_%S')

        print('cutout path: ',
              join(self.cache_folder, '{}_cutout.png'.format(cur_time)))
        cv2.imwrite(join(self.cache_folder, '{}_cutout.png'.format(cur_time)),
                    cutout_pix * 255.0)
        cv2.imwrite(join(self.cache_folder, '{}_ibl.png'.format(cur_time)),
                    ibl_pix * 255.0)

    def next_scene(self):
        self.save_current()

        print('go to next scene')
        self.cur_exp += 1
        self.cur_exp = self.cur_exp % self.num_test
        print(self.cur_exp)
        self.update_cur_labels()
        # self.ibl_cmds = []
        # self.init_ibl_state()
        self.reset_ibl_cmds()

    def load_cur_np(self, cur_exp):
        final, cutout, mask = cv2.imread(self.final_imgs[cur_exp]), cv2.imread(
            self.cutout_imgs[cur_exp]), cv2.imread(self.mask_imgs[cur_exp])
        return final, cutout, mask

    def lerp(self, a, b, f):
        return (1.0 - f) * a + f * b

    def size_change(self):
        fract = self.size_slider.value() / 99.0
        self.cur_size = self.lerp(self.cur_size_min, self.cur_size_max, fract)

        # print('size value: ', )
        # self.cur_size = np.clip(self.cur_size, 0.001, 1.0)
        if len(self.ibl_cmds) != 0:
            self.ibl_cmds[self.cur_ibl_cmd] = (self.cur_x, self.cur_y,
                                               self.cur_size, self.cur_scale)
            self.update_composite()

        self.ibl_label.setFocus()

    def scale_change(self):
        fract = self.scale_slider.value() / 99.0
        self.cur_scale = self.lerp(self.cur_scale_min, self.cur_scale_max,
                                   fract)

        if len(self.ibl_cmds) != 0:
            self.ibl_cmds[self.cur_ibl_cmd] = (self.cur_x, self.cur_y,
                                               self.cur_size, self.cur_scale)
            self.update_composite()

        self.ibl_label.setFocus()

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_A:
            print('add an area light')
            self.add_cur_ibl_state()
            self.init_ibl_state()
            self.update_composite()

        if event.key() == QtCore.Qt.Key_Q:
            self.cur_size += 0.01
            self.cur_size = np.clip(self.cur_size, self.cur_size_min,
                                    self.cur_size_max)

            if len(self.ibl_cmds) != 0:
                self.ibl_cmds[self.cur_ibl_cmd] = (self.cur_x, self.cur_y,
                                                   self.cur_size,
                                                   self.cur_scale)
                self.update_composite()

        if event.key() == QtCore.Qt.Key_W:
            self.cur_size -= 0.01
            self.cur_size = np.clip(self.cur_size, self.cur_size_min,
                                    self.cur_size_max)
            print(self.cur_size)

            if len(self.ibl_cmds) != 0:
                self.ibl_cmds[self.cur_ibl_cmd] = (self.cur_x, self.cur_y,
                                                   self.cur_size,
                                                   self.cur_scale)
                self.update_composite()

    def check_mouse(self, x, y):
        if x > 1.0 or x <= 0 or y > 1.0 or y <= 0:
            return False

        # dis = np.sqrt((x-self.cur_x) ** 2 + (y-self.cur_y) ** 2)
        # print('cur dis', dis)
        # if dis > 0.1:
        #     return False

        return True

    def mouseMoveEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton:
            x, y = self.get_relative_pos(event)
            if not self.check_mouse(x, y):
                return

            self.cur_x, self.cur_y = x, y
            if len(self.ibl_cmds) != 0:
                self.ibl_cmds[self.cur_ibl_cmd] = (self.cur_x, self.cur_y,
                                                   self.cur_size,
                                                   self.cur_scale)
            self.update_composite()

        self.ibl_label.setFocus()
        super(App, self).mouseMoveEvent(event)

    def set_ibl(self, ibl_np):
        self.set_np_img(self.ibl_label, ibl_np)

    def composite(self, final, mask, shadow):
        shadow = np.clip(shadow, 0.0, 1.0)
        composite = mask * final + (1.0 - mask) * (1.0 - shadow)
        return composite

    def update_composite(self):
        # if len(self.ibl_cmds) != 0:
        #     self.ibl_cmds[-1] = (self.cur_x, self.cur_y, self.cur_size)

        cur_ibl = self.get_cur_ibl()
        self.cur_user_ibl = cur_ibl

        self.set_ibl(cur_ibl)
        if np.sum(cur_ibl) < 1e-3:
            shadow_result = np.zeros((256, 256, 3))
        else:
            shadow_result = evaluation.net_render_np(self.cur_mask, cur_ibl)
            shadow_result = np.repeat(shadow_result[:, :, np.newaxis],
                                      3,
                                      axis=2)
        final_composite = self.composite(self.cur_final / 255.0,
                                         self.cur_mask / 255.0, shadow_result)
        self.cur_user_comopsite = final_composite
        self.set_np_img(self.cutout_label, final_composite)

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            x, y = self.get_relative_pos(event)
            # print("Press! {}, {}".format(x, y))

            if not self.check_mouse(x, y):
                return

            self.cur_x, self.cur_y = x, y
            if len(self.ibl_cmds) != 0:
                self.ibl_cmds[self.cur_ibl_cmd] = (self.cur_x, self.cur_y,
                                                   self.cur_size,
                                                   self.cur_scale)
                self.update_composite()

        if event.button() == QtCore.Qt.RightButton:
            print('Delete last IBL')
            if len(self.ibl_cmds) != 0:
                # self.ibl_cmds = self.ibl_cmds[:-1]
                del self.ibl_cmds[self.cur_ibl_cmd]
                self.light_list.takeItem(self.cur_ibl_cmd)

                self.cur_ibl_cmd = self.cur_ibl_cmd - 1
                self.update_composite()

        super(App, self).mousePressEvent(event)

    def get_relative_pos(self, mouse_event):
        ibl_pos = self.ibl_label.pos()
        mouse_pos = mouse_event.pos()
        ibl_w, ibl_h = 512, 256
        relative = mouse_pos - ibl_pos
        x, y = relative.x() / ibl_w, 1.0 - (relative.y() - 100) / ibl_h
        print('x: {}, y: {}'.format(x, y))
        return x, y

    def get_cur_ibl(self):
        num = len(self.ibl_cmds)
        if num == 0:
            return np.zeros((256, 512, 3))
        gs = ig.Composite(operator=np.add,
                          generators=[
                              ig.Gaussian(
                                  size=self.ibl_cmds[i][2],
                                  scale=self.ibl_cmds[i][3],
                                  x=self.ibl_cmds[i][0] - 0.5,
                                  y=self.ibl_cmds[i][1] - 0.5,
                                  aspect_ratio=1.0,
                              ) for i in range(num)
                          ],
                          xdensity=512)
        return np.repeat(gs()[:, :, np.newaxis], 3, axis=2)
Beispiel #7
0
class PyQtSamplerMainAxis(QMainWindow):
    def __init__(self, keyword="all", for_test=False):
        super(PyQtSamplerMainAxis, self).__init__()
        self.setMouseTracking(True)

        if for_test:
            self.base_dir = os.path.join('../samples', 'food_spanet_rgb')
        else:
            self.base_dir = "../data/skewering_positions_%s" % (keyword)

        self.img_dir = os.path.join(self.base_dir, 'cropped_images')
        self.img_filename_list = list()
        self.img_idx = 0

        self.ann_dir = os.path.join(self.base_dir, 'annotations')

        self.cur_category = None

        self.label = None
        self.pixmap = None

        self.org_img_size = None
        self.label_size = 450
        self.pixmap_offset = None

        self.margin = 10
        self.btn_width = 80
        self.btn_height = 30
        self.statusbar_height = 20

        self.shift_pressed = False
        self.ctrl_pressed = False

        self.title = 'Main Axis Sampler'
        self.left = 150
        self.top = 100
        self.width = 840
        self.height = self.label_size + self.margin * 3 + \
            self.btn_height + self.statusbar_height

        self.init_data()
        self.init_ui()

    def init_data(self):
        print('init data')
        filename_list = os.listdir(self.img_dir)

        for item in sorted(filename_list):
            if not item.endswith('.png'):
                continue

            item_name = item.split('.')[0]

            self.img_filename_list.append(item_name)
        print('loaded {} cropped images'.format(len(self.img_filename_list)))

        if not os.path.exists(self.ann_dir):
            os.makedirs(self.ann_dir)

    def init_ui(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.setMinimumSize(350, 350)

        self.setAutoFillBackground(True)
        p = self.palette()
        p.setColor(self.backgroundRole(), QColor(238, 239, 236))
        self.setPalette(p)

        self.statusbar = QStatusBar()
        self.setStatusBar(self.statusbar)

        self.init_buttons()
        self.show_image()

        self.list_widget = QListWidget(self)
        self.list_widget.itemDoubleClicked.connect(self.on_item_double_click)
        self.update_list()
        self.update_list_data()

        self.show()

    def init_buttons(self):
        self.btn_prev_text = '<< Prev'
        self.btn_prev = QPushButton(self.btn_prev_text, self)
        self.btn_prev.clicked.connect(self.on_btn_prev_click)

        self.btn_save_text = 'Save'
        self.btn_save = QPushButton(self.btn_save_text, self)
        self.btn_save.clicked.connect(self.on_btn_save_click)

        self.btn_clear_text = 'Clear'
        self.btn_clear = QPushButton(self.btn_clear_text, self)
        self.btn_clear.clicked.connect(self.on_btn_clear_click)

        self.btn_next_text = 'Next >>'
        self.btn_next = QPushButton(self.btn_next_text, self)
        self.btn_next.clicked.connect(self.on_btn_next_click)

        self.adjust_buttons()

    def adjust_buttons(self):
        self.btn_width = self.label_size * 0.23

        self.btn_prev.move(self.margin, self.label_size + self.margin * 2)
        self.btn_prev.resize(self.btn_width, self.btn_height)

        self.btn_save.move(
            self.margin + self.label_size * 0.37 - self.btn_width * 0.5,
            self.label_size + self.margin * 2)
        self.btn_save.resize(self.btn_width, self.btn_height)

        self.btn_clear.move(
            self.margin + self.label_size * 0.63 - self.btn_width * 0.5,
            self.label_size + self.margin * 2)
        self.btn_clear.resize(self.btn_width, self.btn_height)

        self.btn_next.move(self.label_size - self.btn_width + self.margin,
                           self.label_size + self.margin * 2)
        self.btn_next.resize(self.btn_width, self.btn_height)

    @pyqtSlot()
    def on_btn_prev_click(self):
        self.show_prev_image()

    @pyqtSlot()
    def on_btn_save_click(self):
        self.save_ann()

    @pyqtSlot()
    def on_btn_clear_click(self):
        self.clear_ann()

    @pyqtSlot()
    def on_btn_next_click(self):
        self.show_next_image()

    def show_prev_image(self):
        if self.img_idx > 0:
            self.img_idx -= 1
            self.list_widget.setCurrentRow(self.img_idx)
            self.show_image()
        else:
            print('This is the first image in the dataset')

    def show_next_image(self):
        if self.img_idx < len(self.img_filename_list) - 1:
            self.img_idx += 1
            self.list_widget.setCurrentRow(self.img_idx)
            self.show_image()
        else:
            print('This is the last image in the dataset')

    def update_list_data(self):
        self.list_widget.addItems(self.img_filename_list)

    def update_list(self):
        self.list_widget.move(self.label_size + self.margin * 2, self.margin)
        self.list_widget.resize(
            self.width - (self.label_size + self.margin * 3),
            self.height - (self.margin * 2 + self.statusbar_height))

    def on_item_double_click(self, item):
        self.img_idx = self.list_widget.currentRow()
        self.list_widget.clearFocus()
        self.show_image()

    def set_statusbar(self, optional=None):
        msg = 'Recording masks:  {} / {}   |   Image Directory: "{}"'.format(
            self.img_idx + 1, len(self.img_filename_list), self.img_dir)
        if optional is not None:
            msg += '  | {}'.format(optional)

        self.statusbar.showMessage(msg)

    def show_image(self):
        img_filename = self.img_filename_list[self.img_idx]
        self.cur_category = img_filename.split('_')[-2]
        filepath = os.path.join(self.img_dir, img_filename + '.png')

        self.pixmap = QPixmap(filepath)
        self.org_img_size = np.array(
            [self.pixmap.width(), self.pixmap.height()])
        self.rescale_image()

        if self.label is None:
            self.label = OverlayLabel(self)
            self.label.setAlignment(Qt.AlignCenter)
            self.label.setFrameShape(QFrame.Panel)
            self.label.setFrameShadow(QFrame.Sunken)
            self.label.setLineWidth(1)

        self.label.setPixmap(self.pixmap)
        self.label.move(self.margin, self.margin)
        self.label.resize(self.label_size, self.label_size)

        self.load_ann_from_file()

        self.set_statusbar()
        self.update()

    def resize_image(self):
        self.rescale_image()
        self.label.setPixmap(self.pixmap)
        self.label.move(self.margin, self.margin)
        self.label.resize(self.label_size, self.label_size)
        self.update_list()
        self.update()

    def rescale_image(self):
        ratio = self.label_size / float(np.max(self.org_img_size))
        new_size = self.org_img_size * ratio
        self.pixmap = self.pixmap.scaled(new_size[0], new_size[1])
        self.pixmap_offset = (self.label_size - new_size) * 0.5 + self.margin

    def calculate_angle(self, sp=None, ep=None):
        if sp is None:
            sp = self.label.axis_sp
        if ep is None:
            ep = self.label.axis_ep
        pdiff = sp - ep
        return np.degrees(np.arctan2(pdiff[0], pdiff[1])) % 180

    def get_center_of_highlight(self):
        targets = list()
        highlight = self.label.highlight
        for ci in range(highlight.shape[0]):
            for ri in range(highlight.shape[1]):
                if highlight[ri, ci] == 1:
                    targets.append([ri, ci])
        targets = np.asarray(targets)
        return np.sum(targets / targets.shape[0], axis=0)

    def add_group_item(self, x, y):
        if x < 0 or x >= self.grid_size[0] or y < 0 or y >= self.grid_size[1]:
            return False
        if self.label.grid[x, y] == -1:
            return False

        if self.cur_group is None:
            self.cur_group = dict()

        hv = x * self.grid_size[0] + y

        if hv in self.cur_group:
            return False
        self.cur_group[hv] = True
        return True

    def clear_ann(self):
        self.label.clear_ann()
        self.update()

    def save_ann(self):
        if (self.label.axis_sp is None or len(self.label.axis_sp) == 0
                or self.label.axis_ep is None or len(self.label.axis_ep) == 0):
            return

        if not os.path.exists(self.ann_dir):
            os.makedirs(self.ann_dir)
        img_name = self.img_filename_list[self.img_idx]
        ann_filename = os.path.join(self.ann_dir, img_name) + '.txt'

        with open(ann_filename, 'w') as f:
            this_sp = (np.array(self.label.axis_sp) -
                       self.margin) / self.label_size
            this_ep = (np.array(self.label.axis_ep) -
                       self.margin) / self.label_size

            f.write('{0:.3f} {1:.3f} {2:.3f} {3:.3f}\n'.format(
                *this_sp, *this_ep))
            f.close()
            print('saved: {}'.format(ann_filename))

    def load_ann_from_file(self):
        img_name = self.img_filename_list[self.img_idx]
        ann_filename = os.path.join(self.ann_dir, img_name) + '.txt'

        self.clear_ann()
        if not os.path.exists(ann_filename):
            return

        with open(ann_filename, 'r') as f:
            points = np.array(list(map(float, f.read().strip().split())))
            self.label.axis_sp = points[:2] * self.label_size + self.margin
            self.label.axis_ep = points[2:] * self.label_size + self.margin
            f.close()

    def show_shortcuts(self, show=True):
        if show:
            self.btn_prev.setText('<< A')
            self.btn_save.setText('S')
            self.btn_clear.setText('C')
            self.btn_next.setText('D >>')
        else:
            self.btn_prev.setText(self.btn_prev_text)
            self.btn_save.setText(self.btn_save_text)
            self.btn_clear.setText(self.btn_clear_text)
            self.btn_next.setText(self.btn_next_text)

    # override
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_A:
            self.show_prev_image()
        elif event.key() == Qt.Key_S:
            self.save_ann()
        elif event.key() == Qt.Key_C:
            self.clear_ann()
        elif event.key() == Qt.Key_D:
            self.save_ann()
            self.show_next_image()
        elif event.key() == Qt.Key_Alt:
            self.show_shortcuts()
        elif event.key() == Qt.Key_Shift:
            self.shift_pressed = True
        elif event.key() == Qt.Key_Control:
            self.ctrl_pressed = True

    # override
    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Alt:
            self.show_shortcuts(show=False)
        elif event.key() == Qt.Key_Shift:
            self.shift_pressed = False
        elif event.key() == Qt.Key_Control:
            self.ctrl_pressed = False

    # override
    def resizeEvent(self, event):
        self.width, self.height = event.size().width(), event.size().height()

        self.label_size = (
            self.height -
            (self.margin * 3 + self.btn_height + self.statusbar_height))
        self.resize_image()

        self.adjust_buttons()

    # override
    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            self.label.set_axis_ep(event.pos().x() - self.margin,
                                   event.pos().y() - self.margin)
            self.update()
        elif event.buttons() == Qt.RightButton:
            self.label.set_axis_ep(event.pos().x() - self.margin,
                                   event.pos().y() - self.margin)
            self.update()

    # override
    def mousePressEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            self.label.set_axis_sp(event.pos().x() - self.margin,
                                   event.pos().y() - self.margin)
        elif event.buttons() == Qt.RightButton:
            self.label.set_axis_sp(event.pos().x() - self.margin,
                                   event.pos().y() - self.margin)
        self.update()

    # override
    def mouseReleaseEvent(self, event):
        self.label.set_axis_ep(event.pos().x() - self.margin,
                               event.pos().y() - self.margin)
        self.update()
class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setWindowTitle("application main window")

        self.file_menu = QtWidgets.QMenu('&File', self)
        self.file_menu.addAction('&Quit', self.fileQuit,
                                 QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
        self.menuBar().addMenu(self.file_menu)

        self.help_menu = QtWidgets.QMenu('&Help', self)
        self.menuBar().addSeparator()
        self.menuBar().addMenu(self.help_menu)

        self.help_menu.addAction('&About', self.about)

        self.main_widget = QtWidgets.QWidget(self)

        l = QtWidgets.QGridLayout(self.main_widget)
        # sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100)
        self.dc = MyDynamicMplCanvas(self.main_widget,
                                     width=10,
                                     height=8,
                                     dpi=100)
        self.list = QListWidget(self)
        self.list.setFont(QFont('Courier'))
        self.list.doubleClicked.connect(self.listItemDoubleClicked)
        # l.addWidget(sc)
        self.xEdit = QLineEdit()
        self.yEdit = QLineEdit()
        self.xEdit2 = QLineEdit()
        self.yEdit2 = QLineEdit()
        self.xEdit3 = QLineEdit()
        self.yEdit3 = QLineEdit()
        #self.altitudeEdit = QLineEdit()

        self.xEdit.setText('0')
        self.yEdit.setText('0')
        self.xEdit2.setText('0')
        self.yEdit2.setText('0')
        self.xEdit3.setText('0')
        self.yEdit3.setText('0')
        #self.altitudeEdit.setText('5')

        self.xLabel = QLabel('ISU #1 Longitude')
        self.yLabel = QLabel('ISU #1 Latitude')
        self.xLabel2 = QLabel('ISU #2 Longitude')
        self.yLabel2 = QLabel('ISU #2 Latitude')
        self.xLabel3 = QLabel('Ground Station Longitude')
        self.yLabel3 = QLabel('Ground Station Latitude')
        #self.altitudeLabel = QLabel('Altitude in Feet')
        self.gpsLabel = QLabel('      Use GPS as position')

        self.addButton = QPushButton("Add Point")
        self.addButton.clicked.connect(self.addPointButtonClicked)
        self.startButton = QPushButton("Begin")
        self.startButton.clicked.connect(self.beginButtonClicked)
        self.loadCSVButton = QPushButton("Load from CSV")
        self.loadCSVButton.clicked.connect(self.loadCSVButtonClicked)
        self.gpsCheckBox = QCheckBox()

        self.altitudeCheckBox = QCheckBox()
        self.altitudeCheckBoxLabel = QLabel('      Show altitude annotations')
        self.altitudeCheckBox.stateChanged.connect(
            self.altitudeCheckBoxClicked)
        self.altitudeCheckBox.toggle()

        l.addWidget(self.xLabel, 0, 0, 1, 1)
        l.addWidget(self.yLabel, 0, 1, 1, 1)
        l.addWidget(self.xLabel2, 0, 2, 1, 1)
        l.addWidget(self.yLabel2, 0, 3, 1, 1)

        l.addWidget(self.xLabel3, 0, 4, 1, 1)
        l.addWidget(self.yLabel3, 0, 5, 1, 1)
        #l.addWidget(self.altitudeLabel, 0, 6, 1, 1)
        l.addWidget(self.gpsLabel, 1, 8, 1, 1)

        l.addWidget(self.xEdit, 1, 0, 1, 1)
        l.addWidget(self.yEdit, 1, 1, 1, 1)
        l.addWidget(self.xEdit2, 1, 2, 1, 1)
        l.addWidget(self.yEdit2, 1, 3, 1, 1)
        l.addWidget(self.xEdit3, 1, 4, 1, 1)
        l.addWidget(self.yEdit3, 1, 5, 1, 1)
        #l.addWidget(self.altitudeEdit, 1, 6, 1, 1)
        l.addWidget(self.addButton, 1, 7, 1, 1)
        l.addWidget(self.gpsCheckBox, 1, 8, 1, 1)
        l.addWidget(self.startButton, 3, 8, 1, 1)
        l.addWidget(self.loadCSVButton, 3, 7, 1, 1)
        #l.addWidget(self.altitudeCheckBoxLabel, 0, 8, 1, 1)
        #l.addWidget(self.altitudeCheckBox, 0, 8, 1, 1)

        l.addWidget(self.dc, 2, 0, 1, 7)
        l.addWidget(self.list, 2, 7, 1, 2)

        # self.addButton.clicked.connect(dc.addPoint(str(self.xEdit.text()), str(self.yEdit.text()), str(self.altitudeEdit.text())))

        lenCoords = len(dataPoints[0])
        for i in range(0, lenCoords):
            self.list.addItem(
                str(i + 1) + ".  " + "(" + str(dataPoints[0][i]) + ", " +
                str(dataPoints[1][i]) + ")  Alt: " + str(dataPoints[2][i]))

        # lenCoords = len(exampleData[0])
        # for i in range(0,lenCoords):
        # self.list.addItem("(" + str(exampleData[0][i]) + ", " + str(exampleData[1][i]) + ")  Altitude: " + str(exampleData[2][i]) + "   Temp: " + str(exampleData[3][i]) + " °C")
        #	self.list.addItem('{:16s} {:18s} {:18s}'.format("(" + str(exampleData[0][i]) + ", " + str(exampleData[1][i]) + ")", "Altitude: " + str(exampleData[2][i]), "Temp: " + str(exampleData[3][i]) + " °C"))

        self.main_widget.setFocus()
        self.setCentralWidget(self.main_widget)

    # def buttonClicked(self, dc):
    #	dc.addPoint(self, str(self.xEdit.text()), str(self.yEdit.text()), str(self.altitudeEdit.text()))

    def fileQuit(self):
        self.close()

    def closeEvent(self, ce):
        self.fileQuit()

    def altitudeCheckBoxClicked(self):
        self.dc.axes.cla()
        self.dc.axes.plot(dataPoints[0],
                          dataPoints[1],
                          c='c',
                          linestyle='dashed',
                          marker='o')
        self.dc.axes.set_xlabel('Longitude in Decimal Degrees')
        self.dc.axes.set_ylabel('Latitude in Decimal Degrees')
        self.dc.axes.set_title('ISU & Ground Station Locations')
        self.dc.axes.set_xlim(28, 38)
        self.dc.axes.set_ylim(82, 92)
        # tuscaloosa GPS coords roughly
        # 33.209561 lat, 33 deg 12 min 34.412 sec N
        # -87.567526 long, 87 deg 34 min 3.092 sec W

        if self.altitudeCheckBox.isChecked():

            for i in range(0, len(dataPoints[0])):
                self.dc.axes.annotate(
                    str(dataPoints[2][i]) + ' m',
                    (dataPoints[0][i] - 10, dataPoints[1][i] + 20))

                self.dc.axes.annotate(
                    str(i + 1), (dataPoints[0][i] - 2.5 - 3 * len(str(i + 1)),
                                 dataPoints[1][i] - 8),
                    size=8)

            self.dc.draw()
        else:
            for i in range(0, len(dataPoints[0])):
                self.dc.axes.annotate(
                    str(i + 1), (dataPoints[0][i] - 2.5 - 3 * len(str(i + 1)),
                                 dataPoints[1][i] - 8),
                    size=8)

            self.dc.draw()

    def listItemDoubleClicked(self):
        editedRow = self.list.currentRow()
        print(editedRow)
        s = str(dataPoints[editedRow * 2]) + ', ' + str(
            dataPoints[2 * editedRow + 1])
        t, okPressed = QInputDialog.getText(
            self, "Edit waypoint", "Format: X.X, Y.Y without brackets",
            QLineEdit.Normal, s)
        if okPressed:
            if re.match('[-+]?[0-9]*\.?[0-9]+, [-+]?[0-9]*\.?[0-9]+', t):
                editX, editY = t.split(', ')
                dataPoints[editedRow * 2] = float(editX)
                dataPoints[editedRow * 2 + 1] = float(editY)
                if (editedRow == 0):
                    self.list.currentItem().setText(
                        str("ISU #1: ") + "(" +
                        str(dataPoints[editedRow * 2]) + ", " +
                        str(dataPoints[editedRow * 2 + 1]) + ")")
                elif (editedRow == 1):
                    self.list.currentItem().setText(
                        str("ISU #2: ") + "(" +
                        str(dataPoints[editedRow * 2]) + ", " +
                        str(dataPoints[editedRow * 2 + 1]) + ")")
                elif (editedRow == 2):
                    self.list.currentItem().setText(
                        str("Ground Station: ") + "(" +
                        str(dataPoints[editedRow * 2]) + ", " +
                        str(dataPoints[editedRow * 2 + 1]) + ")")
                else:
                    print("Some error with ")
                    return None
            else:
                QtWidgets.QMessageBox.about(self, "Unsaved edit",
                                            "Invalid format")

    def addPointButtonClicked(self):
        lenCoords = len(dataPoints[0])

        newX = self.xEdit.text()
        newY = self.yEdit.text()
        newX2 = self.xEdit2.text()
        newY2 = self.yEdit2.text()
        newX3 = self.xEdit3.text()
        newY3 = self.yEdit3.text()
        #newAlt = self.altitudeEdit.text()

        if isinstance(float(newX), numbers.Number) and isinstance(
                float(newY), numbers.Number) and isinstance(
                    float(newX2), numbers.Number) and isinstance(
                        float(newY2), numbers.Number) and isinstance(
                            float(newX3), numbers.Number) and isinstance(
                                float(newY3), numbers.Number):
            dataPoints[0].append(float(newX))
            dataPoints[1].append(float(newY))
            #print("reached here")
            dataPoints[2].append(float(newX2))
            dataPoints[3].append(float(newY2))
            #print("reached here")
            dataPoints[4].append(float(newX3))
            dataPoints[5].append(float(newY3))
            #dataPoints[2].append(float(newAlt))

            print("added a point at (" + str(dataPoints[0][lenCoords]) + ", " +
                  str(dataPoints[1][lenCoords]) + ")  ")
            print("added a point at (" + str(dataPoints[2][lenCoords]) + ", " +
                  str(dataPoints[3][lenCoords]) + ")  ")
            print("added a point at (" + str(dataPoints[4][lenCoords]) + ", " +
                  str(dataPoints[5][lenCoords]) + ")  ")
            self.list.addItem("ISU #1: " + "(" +
                              str(dataPoints[0][lenCoords]) + ", " +
                              str(dataPoints[1][lenCoords]) + ") ")
            self.list.addItem("ISU #2: " + "(" +
                              str(dataPoints[2][lenCoords]) + ", " +
                              str(dataPoints[3][lenCoords]) + ") ")
            self.list.addItem("Ground Station: " + "(" +
                              str(dataPoints[4][lenCoords]) + ", " +
                              str(dataPoints[5][lenCoords]) + ") ")

            self.dc.axes.cla()
            self.dc.axes.plot(dataPoints[0],
                              dataPoints[1],
                              c='c',
                              linestyle='dashed',
                              marker='o')
            self.dc.axes.plot(dataPoints[2],
                              dataPoints[3],
                              c='c',
                              linestyle='dashed',
                              marker='o')
            self.dc.axes.plot(dataPoints[4],
                              dataPoints[5],
                              c='c',
                              linestyle='dashed',
                              marker='o')
            self.dc.axes.set_xlabel('Longitude in Decimal Degrees')
            self.dc.axes.set_ylabel('Latitude in Decimal Degrees')
            self.dc.axes.set_title('ISU & Ground Station Locations')
            self.dc.axes.set_xlim(28, 38)
            self.dc.axes.set_ylim(82, 92)

            if self.altitudeCheckBox.isChecked():

                for i in range(0, len(dataPoints[0])):
                    self.dc.axes.annotate(
                        str(dataPoints[2][i]) + ' m',
                        (dataPoints[0][i] - 10, dataPoints[1][i] + 20))

                    self.dc.axes.annotate(
                        str(i + 1),
                        (dataPoints[0][i] - 2.5 - 3 * len(str(i + 1)),
                         dataPoints[1][i] - 8),
                        size=8)

            else:
                for i in range(0, len(dataPoints[0])):
                    self.dc.axes.annotate(
                        str(i + 1),
                        (dataPoints[0][i] - 2.5 - 3 * len(str(i + 1)),
                         dataPoints[1][i] - 8),
                        size=8)

            self.dc.draw()

    def beginButtonClicked(self):

        msg = QtWidgets.QMessageBox()
        reply = msg.question(
            self, 'Begin Flight?',
            "Flight path will be saved to flightPath.csv. Are you sure you want to begin flight?",
            QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)

        if reply == QtWidgets.QMessageBox.Yes:
            with open('flightPath.csv', 'w') as csvfile:
                fieldnames = ['x', 'y']
                writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
                writer.writeheader()

                for i in range(0, 3):

                    writer.writerow({
                        'x': str(dataPoints[2 * i]),
                        'y': str(dataPoints[2 * i + 1])
                    })

            dictList = []
            for i in range(0, 3):
                currentDict = {
                    'x': dataPoints[2 * i],
                    'y': dataPoints[2 * i + 1]
                }
                dictList.append(currentDict)

            #uncomment below when ready to start testing data, as well as uncommenting line 30
            #com.send(dictList)
            for i in range(0, 3):
                #send x
                com.send(dataPoints[2 * i])
                #space for seperation
                com.send(" ")
                #send y
                com.send(dataPoints[2 * i + 1])
                com.send(" ")

            com.send("EndOfFile")

    # msg.exec_()

    def threeDButtonClicked(self):
        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')

        ax.scatter(dataPoints[0],
                   dataPoints[1],
                   dataPoints[2],
                   c='r',
                   linestyle='dashed',
                   marker='o')
        ax.plot(dataPoints[0], dataPoints[1], dataPoints[2], color='r')

        ax.set_xlabel('Relative Position, West/East (m)')
        ax.set_ylabel('Relative Position, South/North (m)')
        ax.set_zlabel('Relative Altitude (m)')

        ax.set_xlim(28, 38)
        ax.set_ylim(82, 92)
        ax.set_zlim(0, 25)

        plt.show()

    def loadCSVButtonClicked(self):
        msg = QtWidgets.QMessageBox()
        reply = msg.question(
            self, 'Load waypoints from CSV?',
            "Are you sure? This will overwrite your existing waypoints.",
            QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)

        if reply == QtWidgets.QMessageBox.Yes:
            del dataPoints[0][:]
            del dataPoints[1][:]
            del dataPoints[2][:]
            del dataPoints[3][:]
            del dataPoints[4][:]
            del dataPoints[5][:]
            aw.list.clear()
            with open('flightPath.csv', 'r') as csvfile:
                csvReader = csv.reader(csvfile)
                for row in csvReader:
                    if len(row) == 3:
                        try:
                            self.dc.addPoint(int(row[0]), int(row[1]),
                                             int(row[2]))
                            print(row)
                        except ValueError:
                            True

    # Make sure to check if any row is selected here to avoid crash
    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Delete:
            deletedRow = self.list.currentRow()
            if deletedRow >= 0:
                print('deleted ' + str(deletedRow))
                self.list.takeItem(deletedRow)
                self.dc.removePoint(deletedRow)

        # Left and Right key to increase or decrease altitude
        if event.key() == QtCore.Qt.Key_Right:
            self.altitudeEdit.setText(str(int(self.altitudeEdit.text()) + 1))
        elif event.key() == QtCore.Qt.Key_Left:
            self.altitudeEdit.setText(str(int(self.altitudeEdit.text()) - 1))

        if event.modifiers() & QtCore.Qt.ControlModifier and event.key(
        ) == QtCore.Qt.Key_Z:
            rowToDelete = len(dataPoints[0]) - 1
            if rowToDelete != 0:
                print('deleted ' + str(rowToDelete))
                self.list.takeItem(rowToDelete)
                self.dc.removePoint(rowToDelete)

    def about(self):
        QtWidgets.QMessageBox.about(
            self, "About",
            """A program to plot a flight path for an autonomous UAV.""")
Beispiel #9
0
class Movie(QWidget):
    def __init__(self, files):
        super().__init__()

        self.previousRow = 0

        #used for testing
        #		self.pictures = [
        #			"images/movie_test/1.png",
        #			"images/movie_test/2.png",
        #			"images/movie_test/3.png",
        #			"images/movie_test/4.png",
        #			"images/movie_test/5.png",
        #			"images/movie_test/6.png",
        #			"images/movie_test/7.png",
        #			"images/movie_test/8.png",
        #			"images/movie_test/9.png",
        #			]
        self.pictures = files
        self.buttonMessage = ["Create Slideshow", "Update Picture Order"]
        self.initWindow()
        self.createPicIcons()

        #set all signals
        self.picList.itemClicked.connect(self.updateLeftView)
        self.rightButton.clicked.connect(self.updateList)
        self.leftButton.clicked.connect(self.destroyTimer)

    def updateFiles(self, ls):
        self.pictures = ls

    def initWindow(self):
        #created layout and pushed necessary skeleton
        self.setGeometry(100, 100, 2000, 1000)

        self.layout = QBoxLayout(QBoxLayout.RightToLeft)

        self.rightLayout = QBoxLayout(QBoxLayout.TopToBottom)

        self.picList = QListWidget(self)
        self.picList.setIconSize(QSize(100, 100))

        self.picList.setDragDropMode(QListWidget.InternalMove)
        self.picList.setDropIndicatorShown(True)
        self.picList.setDragEnabled(True)
        self.setAcceptDrops(True)

        self.rightButton = QPushButton(self.buttonMessage[0], self)
        self.rightLayout.addWidget(self.picList)
        self.rightLayout.addWidget(self.rightButton)

        self.layout.addLayout(self.rightLayout)
        self.setLayout(self.layout)

    def createPicIcons(self):
        #loop through paths and create icon
        for p in self.pictures:
            pic = QPixmap(p)
            icon = QIcon(pic)
            item = QListWidgetItem(p, self.picList)
            item.setStatusTip(p)
            item.setIcon(icon)
#create list of icons with paths
        self.leftLayout = QBoxLayout(QBoxLayout.TopToBottom)
        self.leftLabel = QLabel()
        self.leftLayout.addWidget(self.leftLabel)
        self.leftButton = QPushButton(self.buttonMessage[1], self)
        self.leftButton.setHidden(not self.leftButton.isHidden())
        self.leftLayout.addWidget(self.leftButton)
        self.layout.addLayout(self.leftLayout)

    def updateLeftView(self):
        #creates preview of current active image on left side
        self.previousRow = self.picList.currentRow()
        current = QPixmap(self.picList.currentItem().text())
        self.leftLabel.setPixmap(current)

    def updateList(self):

        # Does not work for Ubuntu
        # Tried to make a video file that could be saved from given images

        #		firstFrame = cv2.imread(self.picList.item(0).text())
        #		fourcc = cv2.VideoWriter_fourcc(*'MJPG')
        #		out = cv2.VideoWriter("createdVideo", fourcc, 1.0, (firstFrame.shape[0], firstFrame.shape[1]))
        #
        #		for i in range(self.picList.count()):
        #			frame = cv2.imread(self.picList.item(i).text())
        #			out.write(frame)
        #
        #		out.release()

        self.itemID = 0

        self.toggleRightWidget()
        self.toggleLeftWidget()
        self.playSlideshow()

    def playSlideshow(self):
        #updates image for slideshow every 1 second
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update)
        self.timer.start(1000)

    def update(self):
        try:
            #updates image on left side else-->
            self.leftLabel.setPixmap(
                QPixmap(self.picList.item(self.itemID).text()))
            self.itemID += 1
        except:
            #else stops timer and brings up list again
            self.timer.stop()
            self.toggleRightWidget()

    def toggleRightWidget(self):
        self.picList.setHidden(not self.picList.isHidden())
        self.rightButton.setHidden(not self.rightButton.isHidden())

    def toggleLeftWidget(self):
        self.leftButton.setHidden(not self.leftButton.isHidden())

    def destroyTimer(self):
        self.timer.stop()
        self.toggleRightWidget()
        self.toggleLeftWidget()
Beispiel #10
0
class ServerSelectDialog(QDialog):
    def __init__(self, parent=None):
        super(ServerSelectDialog, self).__init__(parent)
        # out
        self.selected_server = None
        self.selected_comp_name = ''
        #
        self.layout = QVBoxLayout()
        self.layout1 = QHBoxLayout()
        self.layout2 = QHBoxLayout()
        self.layout3 = QHBoxLayout()
        # 1
        self.labelCompName = QLabel('Computer name (empty for localhost):', self)
        self.editCompName = QLineEdit(self)
        self.btnRefresh = QPushButton('List OPC servers', self)
        self.layout1.addWidget(self.labelCompName)
        self.layout1.addWidget(self.editCompName)
        self.layout1.addWidget(self.btnRefresh)
        # 2
        self.serversList = QListWidget(self)
        self.layout2.addWidget(self.serversList)
        # 3
        self.btnOK = QPushButton('OK', self)
        self.btnCancel = QPushButton('Cancel', self)
        self.layout3.addStretch()
        self.layout3.addWidget(self.btnOK)
        self.layout3.addWidget(self.btnCancel)
        self.layout3.addStretch()
        # final
        self.layout.addLayout(self.layout1)
        self.layout.addLayout(self.layout2)
        self.layout.addLayout(self.layout3)
        self.setLayout(self.layout)
        self.setWindowTitle('Select OPC server to connect to')
        #
        self.btnOK.clicked.connect(self.onClickedOK)
        self.btnCancel.clicked.connect(self.reject)
        self.btnRefresh.clicked.connect(self.onClickedRefresh)
        self.serversList.doubleClicked.connect(self.onListDoubleClick)
        #
        self.servers_list = []
        #
        self.onClickedRefresh()

    @pyqtSlot()
    def onClickedOK(self):
        sel_item = self.serversList.currentRow()
        if sel_item < 0:
            return
        self.selected_server = self.servers_list[sel_item]
        self.selected_comp_name = self.editCompName.text()
        self.accept()

    @pyqtSlot()
    def onClickedRefresh(self):
        self.serversList.clear()
        comp_name = self.editCompName.text()
        try:
            self.servers_list = opc_helper.opc_enum_query(comp_name)
        except OSError as ose:
            sys.stderr.write('OSError: ' + str(ose))
        for srv in self.servers_list:
            s = '{0} ({1})'.format(srv['desc'], srv['progid'])
            self.serversList.addItem(s)

    @pyqtSlot(QModelIndex)
    def onListDoubleClick(self, idx: QModelIndex):
        print('onListDoubleClick')
        self.onClickedOK()
Beispiel #11
0
class CityListDlg(QDialog):
    citieslist_signal = pyqtSignal([list])
    citiesdict_signal = pyqtSignal([dict])

    def __init__(
        self, citylist, accurate_url, appid, trans_cities_dict, parent=None
    ):
        super(CityListDlg, self).__init__(parent)
        self.settings = QSettings()
        self.citylist = citylist
        self.trans_cities_dict = trans_cities_dict
        self.accurate_url = accurate_url
        self.appid = appid
        self.listWidget = QListWidget()
        self.listWidget.itemDoubleClicked.connect(self.translate)
        cities_list = []
        for i in self.citylist:
            cities_list.append(self.trans_cities_dict.get(i, i))
        self.listWidget.addItems(cities_list)
        buttonLayout = QVBoxLayout()
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )
        self.buttonBox.rejected.connect(self.reject)
        self.buttonBox.accepted.connect(self.accept)
        layoutT = QVBoxLayout()
        layout = QHBoxLayout()
        layout.addWidget(self.listWidget)
        layout.addLayout(buttonLayout)
        for text, slot in ((self.tr("&Add..."), self.add),
                           (self.tr("&Remove..."), self.remove),
                           (self.tr("&Up"), self.up),
                           (self.tr("&Down"), self.down),
                           (self.tr("De&fault"), self.default),
                           (self.tr("&Sort"), self.listWidget.sortItems)):
            button = QPushButton(text)
            buttonLayout.addWidget(button)
            button.clicked.connect(slot)
        self.translate_button = QPushButton(
            QCoreApplication.translate(
                'Button',
                '&Translate',
                'Edit cities name'
            )
        )
        buttonLayout.addWidget(self.translate_button)
        self.translate_button.clicked.connect(self.translate)
        buttonLayout.addWidget(self.buttonBox)
        self.status = QLabel()
        layoutT.addLayout(layout)
        layoutT.addWidget(self.status)
        self.setLayout(layoutT)
        self.setWindowTitle(
            QCoreApplication.translate(
                'Window title',
                'Cities',
                'Cities list dialogue'
            )
        )
        self.checklength()

    def add(self):
        self.status.setText('')
        lista = []
        newitem = ''
        self.citytoadd = ''
        self.countrytoadd = ''
        self._idtoadd = ''
        dialog = searchcity.SearchCity(self.accurate_url, self.appid, self)
        dialog.id_signal.connect(self.addcity)
        dialog.city_signal.connect(self.addcity)
        dialog.country_signal.connect(self.addcity)
        if dialog.exec_() == 1:
            newitem = (
                self.citytoadd + '_' + self.countrytoadd
                + '_' + self._idtoadd
            )
            for row in range(self.listWidget.count()):
                lista.append(self.listWidget.item(row).text())
            if newitem in lista:
                self.status.setText(
                    QCoreApplication.translate(
                        'Status bar message',
                        'The city already exists in the list',
                        'Cities list dialogue'
                    )
                )
                return
            else:
                self.listWidget.addItem(newitem)
                self.checklength()
                self.status.setText(
                    'ℹ ' + QCoreApplication.translate(
                        'Status bar message',
                        'Toggle cities with mouse scroll on the weather window',
                        'Cities list dialogue'
                    )
                )

    def addcity(self, what):
        self.status.setText('')
        if what[0] == 'ID':
            self._idtoadd = what[1]
        elif what[0] == 'City':
            self.citytoadd = what[1]
        elif what[0] == 'Country':
            self.countrytoadd = what[1]

    def remove(self):
        self.status.setText('')
        if self.listWidget.count() == 1:
            self.status.setText(
                QCoreApplication.translate(
                    'Message when trying to remove the'
                    'last and unique city in the list',
                    'This is the default city !',
                    'Cities list dialogue'
                )
            )
            return
        row = self.listWidget.currentRow()
        item = self.listWidget.item(row)
        if item is None:
            return
        message = self.tr('The city "{0}" has been removed').format(
            self.listWidget.item(row).text())
        item = self.listWidget.takeItem(row)
        del item
        self.status.setText(message)

    def up(self):
        self.status.setText('')
        row = self.listWidget.currentRow()
        if row >= 1:
            item = self.listWidget.takeItem(row)
            self.listWidget.insertItem(row - 1, item)
            self.listWidget.setCurrentItem(item)

    def down(self):
        self.status.setText('')
        row = self.listWidget.currentRow()
        if row < self.listWidget.count() - 1:
            item = self.listWidget.takeItem(row)
            self.listWidget.insertItem(row + 1, item)
            self.listWidget.setCurrentItem(item)

    def default(self):
        self.status.setText('')
        row = self.listWidget.currentRow()
        if row >= 1:
            item = self.listWidget.takeItem(row)
            self.listWidget.insertItem(0, item)
            self.listWidget.setCurrentItem(item)

    def checklength(self):
        if self.listWidget.count() == 1:
            # After adding the first city the entry is not activated
            self.listWidget.setCurrentRow(0)
        if self.listWidget.count() > 0:
            self.translate_button.setEnabled(True)
            self.listWidget.setMinimumWidth(
                self.listWidget.sizeHintForColumn(0)
            )
        else:
            self.translate_button.setEnabled(False)

    def translate(self):
        city = self.listWidget.currentItem().text()
        dialog = citytranslate.CityTranslate(
            city, self.trans_cities_dict, self
        )
        dialog.city_signal.connect(self.current_translation)
        if dialog.exec_() == 1:
            row = self.listWidget.currentRow()
            item = self.listWidget.takeItem(row)
            del item
            self.listWidget.insertItem(row, self.current_translated_city)
            self.listWidget.setCurrentRow(row)

    def current_translation(self, translated_city):
        for city, translated in translated_city.items():
            if translated == '':
                translated = city
            self.trans_cities_dict[city] = translated
            self.current_translated_city = translated

    def accept(self):
        listtosend = []
        for row in range(self.listWidget.count()):
            city = self.find_city_key(self.listWidget.item(row).text())
            listtosend.append(city)
        if self.listWidget.count() == 0:
            return
        self.citieslist_signal[list].emit(listtosend)
        self.citiesdict_signal[dict].emit(self.trans_cities_dict)
        QDialog.accept(self)

    def find_city_key(self, city):
        for key, value in self.trans_cities_dict.items():
            if value == city:
                return key
        return city
class GstPipeEdit(QWidget):

    def __init__(self, pipe, app_mode=False, **kwargs):
        super().__init__(**kwargs)
        self.setLayout(QGridLayout())
        self.layout().setAlignment(Qt.AlignTop)

        self._app_mode = app_mode

        # Input selection
        self.inputBox = QComboBox(self)
        self.layout().addWidget(self.inputBox, 0, 0, 1, 3)
        self.__init_inputs()

        # Current plugins list
        self.currentList = QListWidget(self)
        self.currentList.setDragEnabled(True)
        self.currentList.setDragDropMode(QAbstractItemView.InternalMove)
        self.layout().addWidget(self.currentList, 1, 0)

        # Available plugins list
        self.availableList = QListWidget(self)
        self.layout().addWidget(self.availableList, 1, 2)

        # Output selection
        self.outputBox = QComboBox(self)
        self.layout().addWidget(self.outputBox, 4, 0, 1, 3)
        self.__init_outputs()

        # Add/Remove plugins buttons
        self.buttonsLayout = QVBoxLayout()
        self.layout().addLayout(self.buttonsLayout, 1, 1)
        self.layout().setAlignment(self.buttonsLayout, Qt.AlignHCenter)

        self.addButton = QPushButton(self)
        self.addButton.setIcon(QIcon.fromTheme('go-previous'))
        self.addButton.clicked.connect(self.__add_plugin)
        self.buttonsLayout.addWidget(self.addButton)
        self.buttonsLayout.setAlignment(self.addButton, Qt.AlignHCenter)

        self.delButton = QPushButton(self)
        self.delButton.setIcon(QIcon.fromTheme('go-next'))
        self.delButton.clicked.connect(self.__remove_plugin)
        self.buttonsLayout.addWidget(self.delButton)
        self.buttonsLayout.setAlignment(self.delButton, Qt.AlignHCenter)

        # Load the pipeline
        self.set_pipe(pipe)

    def set_pipe(self, pipe):
        if pipe:
            if not self._app_mode:
                inputs = sorted(elements.inputs())
                self.inputBox.setCurrentIndex(inputs.index(pipe[0]))

            outputs = sorted(elements.outputs())
            self.outputBox.setCurrentIndex(outputs.index(pipe[-1]))

        self.__init_current_plugins(pipe)
        self.__init_available_plugins(pipe)

    def get_pipe(self):
        pipe = [] if self._app_mode else [self.inputBox.currentText()]
        for n in range(self.currentList.count()):
            pipe.append(self.currentList.item(n).text())
        pipe.append(self.outputBox.currentText())

        return tuple(pipe)

    def __init_inputs(self):
        if self._app_mode:
            self.inputBox.setEnabled(False)
        else:
            inputs = sorted(elements.inputs())
            self.inputBox.addItems(inputs)
            self.inputBox.setEnabled(len(inputs) > 1)

    def __init_outputs(self):
        outputs = sorted(elements.outputs())
        self.outputBox.addItems(outputs)
        self.outputBox.setEnabled(len(outputs) > 1)

    def __init_current_plugins(self, pipe):
        self.currentList.clear()

        start = 0 if self._app_mode else 1
        for plugin in pipe[start:-1]:
            self.currentList.addItem(plugin)

    def __init_available_plugins(self, pipe):
        self.availableList.clear()

        for plugin in elements.plugins().values():
            if plugin.Name not in pipe:
                self.availableList.addItem(plugin.Name)

    def __add_plugin(self):
        item = self.availableList.takeItem(self.availableList.currentRow())
        self.currentList.addItem(item)

    def __remove_plugin(self):
        item = self.currentList.takeItem(self.currentList.currentRow())
        self.availableList.addItem(item)
class TriggersSettings(SettingsPage):

    Name = 'Triggers'
    PluginInstance = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.setLayout(QVBoxLayout(self))

        self.triggersWidget = QListWidget(self)
        self.triggersWidget.setAlternatingRowColors(True)
        self.layout().addWidget(self.triggersWidget)

        # Buttons
        self.dialogButtons = QDialogButtonBox(self)
        self.dialogButtons.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.layout().addWidget(self.dialogButtons)

        self.addButton = self.dialogButtons.addButton('Add', QDialogButtonBox.ActionRole)
        self.addButton.clicked.connect(self._add_trigger_dialog)

        self.delButton = self.dialogButtons.addButton('Remove', QDialogButtonBox.ActionRole)
        self.delButton.clicked.connect(self._remove_trigger)

        self.cue_dialog = CueListDialog(cues=Application().cue_model)

    def _add_new_trigger(self, tr_action, target, ta_action):
        item = QListWidgetItem()
        item.setSizeHint(QSize(200, 30))

        widget = TriggerWidget(tr_action, target, ta_action, self.cue_dialog)

        self.triggersWidget.addItem(item)
        self.triggersWidget.setItemWidget(item, widget)

    def _add_trigger_dialog(self):
        if self.cue_dialog.exec_() == QDialog.Accepted:
            target = self.cue_dialog.selected_cues()[0]
            self._add_new_trigger(CueTriggers.Ended.value, target,
                                  CueAction.Start.value)

    def _remove_trigger(self):
        self.triggersWidget.takeItem(self.triggersWidget.currentRow())

    def load_settings(self, settings):
        settings = settings.get('triggers', {})

        for trigger_action, targets in settings.items():
            for target, target_action in targets:
                target = Application().cue_model.get(target)
                if target is not None:
                    self._add_new_trigger(trigger_action, target, target_action)

    def get_settings(self):
        triggers = {}

        for n in range(self.triggersWidget.count()):
            widget = self.triggersWidget.itemWidget(self.triggersWidget.item(n))
            tr_action, target, ta_action = widget.get_trigger()

            if tr_action not in triggers:
                triggers[tr_action] = []

            # Avoid duplicate
            if (target, ta_action) not in triggers[tr_action]:
                triggers[tr_action].append((target, ta_action))

        return {'triggers': triggers}
class AutoComplete(PopupWidget):


    def init_popup(self):
        self.list = QListWidget(self)
        self.list.itemClicked.connect(self.insertItem)
        self.layout().addWidget(self.list)
        self.items = []


    def insertItem(self, item):
        self.insert()


    def insert(self):
        completition = self.items[self.list.currentRow()].value
        cursor = self.textedit.textCursor()
        col = cursor.columnNumber()
        line = unicode(cursor.block().text())
        i = self.cursor_start_col
        while i > 0:
            #print(`line[i:col]`)
            if completition.startswith(line[i:col]):
                #print("break")
                break
            i -= 1
        #print(col,i)
        cursor.insertText(completition[col-i:])
        self.hide()


    def setItems(self, proposals):
        proposals = sorted(proposals, cmp=lambda p1,p2:cmp(p1.name,p2.name))
        del self.items[:]
        self.list.clear()
        for entry in proposals:
            i = AutoCompleteItem(entry)
            self.list.addItem(i)
            self.items.append(i)


    def keyPressEvent(self, event):
        self.list.keyPressEvent(event)
        key = event.key()
        text = event.text()
        if key in [Qt.Key_Right, Qt.Key_Enter, Qt.Key_Return]:
            text = ""
        cursor = self.textedit.textCursor()
        line = unicode(cursor.block().text())
        col = cursor.columnNumber()
        prefix = line[self.cursor_start_col:col] + unicode(text)

        found = False
        for row, item in enumerate(self.items):
            if item.value.startswith(prefix):
                current = self.items[self.list.currentRow()].value
                if not current.startswith(prefix):
                    self.list.setCurrentRow(row)
                found = True
                break
        if not found:
            self.hide()
            return

        if key in [Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown]:
            return True
        elif key in [Qt.Key_Tab, Qt.Key_Right, Qt.Key_Enter, Qt.Key_Return]:
            self.insert()
            return True
        elif not text:
            self.hide()
class SinkSelectorSettings(SettingsSection):

    NAME = 'Multi-Sink Selector'
    ELEMENT = SinkSelector

    Sinks = elements.outputs()

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

        self.id = Id
        self._sinks = []

        self.groupBox = QGroupBox(self)
        self.groupBox.setTitle('Add/Remove outputs')
        self.groupBox.resize(self.width(), self.height())

        self.vlayout = QVBoxLayout(self.groupBox)
        self.vlayout.setContentsMargins(0, 5, 0, 0)

        self.sinksList = QListWidget(self.groupBox)
        self.sinksList.setAlternatingRowColors(True)
        self.vlayout.addWidget(self.sinksList)

        self.buttons = QDialogButtonBox(self.groupBox)
        self.vlayout.addWidget(self.buttons)

        self.add = QPushButton('Add')
        self.remove = QPushButton('Remove')
        self.buttons.addButton(self.add, QDialogButtonBox.YesRole)
        self.buttons.addButton(self.remove, QDialogButtonBox.NoRole)

        self.vlayout.activate()

        self.add.clicked.connect(self._new_sink)
        self.remove.clicked.connect(self.__remove_sink)

    def set_configuration(self, conf):
        if conf is not None and self.id in conf:
            conf = deepcopy(conf)
            for key in conf[self.id]:
                self.__add_sink(conf[self.id][key], key)
        else:
            self.__add_sink({'name': 'AutoSink'}, 'sink0')

    def get_configuration(self):
        conf = {}

        if not (self.groupBox.isCheckable() and not self.groupBox.isChecked()):
            conf[self.id] = {}
            for widget, name in self._sinks:
                conf[self.id][name] = widget.get_settings()

        return conf

    def enable_check(self, enable):
        self.groupBox.setCheckable(enable)
        self.groupBox.setChecked(False)

    def _new_sink(self):
        sinks = sorted(self.Sinks.keys())
        sinks.remove('SinkSelector')
        name, ok = QInputDialog.getItem(self, "Output", "Select output device",
                                        sinks, editable=False)
        if ok:
            self.__add_sink({'name': name})

    def __new_name(self):
        suff = 0

        for _, name in sorted(self._sinks, key=itemgetter(1)):
            if 'sink' + str(suff) != name:
                break
            suff += 1

        return 'sink' + str(suff)

    def __add_sink(self, properties, name=None):
        widget = OutputWidget(properties, self.sinksList)
        widget.resize(self.sinksList.width() - 5, 80)

        item = QListWidgetItem()
        item.setSizeHint(widget.size())

        if name is None:
            name = self.__new_name()

        self._sinks.append((widget, name))
        self.sinksList.addItem(item)
        self.sinksList.setItemWidget(item, widget)

        self.remove.setEnabled(len(self._sinks) > 1)

    def __remove_sink(self):
        self._sinks.pop(self.sinksList.currentRow())
        self.sinksList.removeItemWidget(self.sinksList.currentItem())
        self.sinksList.takeItem(self.sinksList.currentRow())

        self.remove.setEnabled(len(self._sinks) > 1)
class RSSReaderWidget(QWidget):

    def __init__(self, config_dir):
        super(RSSReaderWidget, self).__init__()

        self.feed_file_path = os.path.join(config_dir, "rss-reader", "feeds.json")

        self.feed_area = QWidget()
        self.feed_list = QListWidget()
        self.feed_list.setStyleSheet( """QListView {background: #4D5250; show-decoration-selected: 1; selection-background-color: #464646;}""")
        panel_layout = QVBoxLayout()
        panel_layout.setSpacing(0)
        panel_layout.setContentsMargins(0, 0, 0, 0)
        panel_layout.addWidget(self.feed_list)
        self.feed_area.setLayout(panel_layout)

        self.article_area = QWidget()
        self.article_list = QListWidget()
        self.article_list.setStyleSheet( """QListView {background: #FFF; show-decoration-selected: 1; selection-background-color: #EEE;}""")
        self.article_list.verticalScrollBar().setStyleSheet("QScrollBar {width:0px;}");
        article_layout = QVBoxLayout()
        article_layout.setSpacing(0)
        article_layout.setContentsMargins(0, 0, 0, 0)

        self.browser = BrowserView(config_dir)

        article_layout.addWidget(self.article_list)
        article_layout.addWidget(self.browser)

        article_layout.setStretchFactor(self.article_list, 1)
        article_layout.setStretchFactor(self.browser, 3)

        self.article_area.setLayout(article_layout)

        self.welcome_page = QWidget()
        self.welcome_page_box = QVBoxLayout()
        self.welcome_page_box.setSpacing(10)
        self.welcome_page_box.setContentsMargins(0, 0, 0, 0)

        welcome_title_label = QLabel("Welcome to EAF RSS Reader!")
        welcome_title_label.setFont(QFont('Arial', 24))
        welcome_title_label.setStyleSheet("QLabel {color: #333; font-weight: bold; margin: 20px;}")
        welcome_title_label.setAlignment(Qt.AlignHCenter)

        add_subscription_label = QLabel("Press 'a' to subscribe to a feed!")
        add_subscription_label.setFont(QFont('Arial', 20))
        add_subscription_label.setStyleSheet("QLabel {color: #grey;}")
        add_subscription_label.setAlignment(Qt.AlignHCenter)

        self.welcome_page_box.addStretch(1)
        self.welcome_page_box.addWidget(welcome_title_label)
        self.welcome_page_box.addWidget(add_subscription_label)
        self.welcome_page_box.addStretch(1)

        self.welcome_page.setLayout(self.welcome_page_box)

        self.right_area = QStackedWidget()
        self.right_area.addWidget(self.welcome_page)
        self.right_area.addWidget(self.article_area)

        if self.has_feed():
            self.right_area.setCurrentIndex(1)
        else:
            self.right_area.setCurrentIndex(0)

        hbox = QHBoxLayout()
        hbox.setSpacing(0)
        hbox.setContentsMargins(0, 0, 0, 0)

        hbox.addWidget(self.feed_area)
        hbox.addWidget(self.right_area)

        hbox.setStretchFactor(self.feed_area, 1)
        hbox.setStretchFactor(self.right_area, 3)

        self.setLayout(hbox)

        self.feed_list.itemActivated.connect(self.handle_feed)
        self.article_list.itemActivated.connect(self.handle_article)

        self.feed_object_dict = {}

        self.init_select_line = False

        self.fetch_feeds()

    def has_feed(self):
        if os.path.exists(self.feed_file_path):
            try:
                with open(self.feed_file_path, "r") as feed_file:
                    feed_dict = json.loads(feed_file.read())
                    return len(feed_dict.keys()) > 0
            except Exception:
                return False

        return False

    def fetch_feeds(self):
        if os.path.exists(self.feed_file_path):
            try:
                with open(self.feed_file_path, "r") as feed_file:
                    feed_dict = json.loads(feed_file.read())
                    for index, feed_link in enumerate(feed_dict):
                        self.fetch_feed(feed_link, index == 0)
            except Exception:
                pass

    def handle_feed(self, feed_item):
        if feed_item.feed_link in self.feed_object_dict:
            self.init_article_area(self.feed_object_dict[feed_item.feed_link])

    def handle_article(self, article_item):
        article_item.mark_as_read()

        self.browser.setUrl(QUrl(article_item.post_link))

    def fetch_feed(self, feed_link, refresh_ui):
        fetchThread = FetchRSSThread(feed_link)
        fetchThread.fetch_rss.connect(lambda f_object, f_link, f_title: self.handle_rss(f_object, f_link, f_title, refresh_ui))
        fetchThread.invalid_rss.connect(self.handle_invalid_rss)

        object_name = "feed_thread_" + feed_link
        setattr(self, object_name, fetchThread)
        getattr(self, object_name).start()

    def add_subscription(self, feed_link):
        if not self.feed_exists(feed_link):
            self.fetch_feed(feed_link, True)
        else:
            self.buffer.message_to_emacs.emit("Feed already exists: " + feed_link)

    def delete_subscription(self):
        feed_count = self.feed_list.count()
        current_row = self.feed_list.currentRow()
        feed_link = self.feed_list.currentItem().feed_link
        feed_title = self.feed_list.currentItem().feed_title

        self.feed_list.takeItem(current_row)

        with open(self.feed_file_path, "r") as feed_file:
            feed_dict = json.loads(feed_file.read())
            if feed_link in feed_dict:
                del feed_dict[feed_link]

                with open(self.feed_file_path, "w") as f:
                    f.write(json.dumps(feed_dict))

        if feed_count <= 1:
            self.feed_list.clear()
            self.article_list.clear()
            self.browser.setUrl(QUrl(""))
            self.right_area.setCurrentIndex(0)
        else:
            if current_row < feed_count - 1:
                self.feed_list.setCurrentRow(current_row)
            else:
                self.feed_list.setCurrentRow(feed_count - 2)
            self.handle_feed(self.feed_list.currentItem())
            self.buffer.message_to_emacs.emit("Removed feed: " + feed_title)

    def feed_exists(self, feed_link):
        if not os.path.exists(self.feed_file_path):
            return False

        try:
            with open(self.feed_file_path, "r") as feed_file:
                feed_dict = json.loads(feed_file.read())
                return feed_link in feed_dict
        except Exception:
            import traceback
            traceback.print_exc()

            return False

    def save_feed(self, feed_object, feed_link, feed_title):
        touch(self.feed_file_path)

        article_ids = list(map(lambda post: post.id if hasattr(post, 'id')  else post.link, feed_object.entries))

        try:
            with open(self.feed_file_path, "r") as feed_file:
                feed_dict = json.loads(feed_file.read())
                if feed_link not in feed_dict:
                    feed_dict[feed_link] = {
                        "title": feed_title,
                        "unread_articles": article_ids
                    }

                    self.save_feed_dict(feed_dict)
                    self.buffer.message_to_emacs.emit("Add feed: " + feed_link)
        except Exception:
            import traceback
            traceback.print_exc()

            self.save_feed_dict({feed_link : {
                "title": feed_title,
                "unread_articles": article_ids
            }})
            self.buffer.message_to_emacs.emit("Add feed: " + feed_link)

    def save_feed_dict(self, feed_dict):
        with open(self.feed_file_path, "w") as f:
            f.write(json.dumps(feed_dict))

    def handle_rss(self, feed_object, feed_link, feed_title, refresh_ui):
        feed_object.feed_link = feed_link
        self.feed_object_dict[feed_link] = feed_object

        self.save_feed(feed_object, feed_link, feed_title)

        self.right_area.setCurrentIndex(1)

        feed_item = QListWidgetItem(self.feed_list)
        feed_item.feed_link = feed_link
        feed_item.feed_title = feed_title
        feed_item_widget = RSSFeedItem(feed_object, len(feed_object.entries))
        feed_item.update_article_num = feed_item_widget.update_article_num
        feed_item.setSizeHint(feed_item_widget.sizeHint())
        self.feed_list.addItem(feed_item)
        self.feed_list.setItemWidget(feed_item, feed_item_widget)

        unread_articles = []
        with open(self.feed_file_path, "r") as feed_file:
            feed_dict = json.loads(feed_file.read())
            if feed_object.feed_link in feed_dict:
                if "unread_articles" in feed_dict[feed_object.feed_link]:
                    unread_articles = ["unread_articles"]

                for index in range(self.feed_list.count()):
                    feed_item = self.feed_list.item(index)
                    if feed_item.feed_link == feed_object.feed_link:
                        feed_item.update_article_num(len(unread_articles))
                        break

        if refresh_ui:
            self.init_article_area(feed_object)

    def init_article_area(self, feed_object):
        self.browser.setUrl(QUrl(feed_object.entries[0].link))

        self.article_list.clear()

        unread_articles = []
        with open(self.feed_file_path, "r") as feed_file:
            feed_dict = json.loads(feed_file.read())
            if feed_object.feed_link in feed_dict and "unread_articles" in feed_dict[feed_object.feed_link]:
                unread_articles = feed_dict[feed_object.feed_link]["unread_articles"]

        for index, post in enumerate(feed_object.entries):
            item_widget = RSSArticleItemWidget(feed_object, post, unread_articles)
            item = QListWidgetItem(self.article_list)
            item.mark_as_read = item_widget.mark_as_read
            item.post_link = item_widget.post_link
            item.setSizeHint(item_widget.sizeHint())
            item_widget.mark_article_read.connect(self.mark_article_read)
            self.article_list.addItem(item)
            self.article_list.setItemWidget(item, item_widget)

            if index == 0:
                item.mark_as_read()

        self.article_list.setCurrentRow(0)

        if not self.init_select_line:
            self.init_select_line = True
            self.feed_list.setCurrentRow(0)

    def handle_invalid_rss(self, feed_link):
        self.buffer.message_to_emacs.emit("Invalid feed link: " + feed_link)

    def mark_article_read(self, feed_link, post_link):
        if os.path.exists(self.feed_file_path):
            try:
                with open(self.feed_file_path, "r") as feed_file:
                    feed_dict = json.loads(feed_file.read())
                    if feed_link in feed_dict:
                        unread_articles = feed_dict[feed_link]["unread_articles"]

                        if post_link in unread_articles:
                            unread_articles.remove(post_link)
                            feed_dict[feed_link]["unread_articles"] = unread_articles

                            with open(self.feed_file_path, "w") as f:
                                f.write(json.dumps(feed_dict))

                        for index in range(self.feed_list.count()):
                            feed_item = self.feed_list.item(index)
                            if feed_item.feed_link == feed_link:
                                feed_item.update_article_num(len(unread_articles))
                                break
            except Exception:
                pass

    def next_subscription(self):
        feed_count = self.feed_list.count()
        current_row = self.feed_list.currentRow()

        if current_row < feed_count - 1:
            self.feed_list.setCurrentRow(current_row + 1)
            self.feed_list.scrollToItem(self.feed_list.currentItem())
            self.handle_feed(self.feed_list.currentItem())
        else:
            self.buffer.message_to_emacs.emit("End of subscribed feeds")

    def prev_subscription(self):
        current_row = self.feed_list.currentRow()

        if current_row > 0:
            self.feed_list.setCurrentRow(current_row - 1)
            self.feed_list.scrollToItem(self.feed_list.currentItem())
            self.handle_feed(self.feed_list.currentItem())
        else:
            self.buffer.message_to_emacs.emit("Beginning of subscribed feeds")

    def first_subscription(self):
        self.feed_list.setCurrentRow(0)
        self.feed_list.scrollToItem(self.feed_list.currentItem())
        self.handle_feed(self.feed_list.currentItem())

    def last_subscription(self):
        feed_count = self.feed_list.count()

        self.feed_list.setCurrentRow(feed_count - 1)
        self.feed_list.scrollToItem(self.feed_list.currentItem())
        self.handle_feed(self.feed_list.currentItem())

    def next_article(self):
        article_count = self.article_list.count()
        current_row = self.article_list.currentRow()

        if current_row < article_count - 1:
            self.article_list.setCurrentRow(current_row + 1)
            self.article_list.scrollToItem(self.article_list.currentItem())
            self.handle_article(self.article_list.currentItem())
        else:
            self.buffer.message_to_emacs.emit("End of articles")

    def prev_article(self):
        current_row = self.article_list.currentRow()

        if current_row > 0:
            self.article_list.setCurrentRow(current_row - 1)
            self.article_list.scrollToItem(self.article_list.currentItem())
            self.handle_article(self.article_list.currentItem())
        else:
            self.buffer.message_to_emacs.emit("Beginning of articles")

    def first_article(self):
        self.article_list.setCurrentRow(0)
        self.article_list.scrollToItem(self.article_list.currentItem())
        self.handle_article(self.article_list.currentItem())

    def last_article(self):
        article_count = self.article_list.count()

        self.article_list.setCurrentRow(article_count - 1)
        self.article_list.scrollToItem(self.article_list.currentItem())
        self.handle_article(self.article_list.currentItem())
class CollectionCueSettings(SettingsPage):
    Name = 'Edit Collection'

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.setLayout(QVBoxLayout(self))

        self.cuesWidget = QListWidget(self)
        self.cuesWidget.setAlternatingRowColors(True)
        self.layout().addWidget(self.cuesWidget)

        # Buttons
        self.dialogButtons = QDialogButtonBox(self)
        self.dialogButtons.setSizePolicy(QSizePolicy.Minimum,
                                         QSizePolicy.Minimum)
        self.layout().addWidget(self.dialogButtons)

        self.addButton = self.dialogButtons.addButton('Add', QDialogButtonBox.ActionRole)
        self.addButton.clicked.connect(self._add_dialog)

        self.delButton = self.dialogButtons.addButton('Remove', QDialogButtonBox.ActionRole)
        self.delButton.clicked.connect(self._remove_selected)

        self.cue_dialog = CueListDialog(cues=Application().cue_model)
        self.cue_dialog.list.setSelectionMode(QAbstractItemView.ExtendedSelection)

    def load_settings(self, settings):
        for target_id, action in settings.get('targets', []):
            target = Application().cue_model.get(target_id)
            if target is not None:
                self._add_cue(target, action)

    def get_settings(self):
        targets = []
        for n in range(self.cuesWidget.count()):
            widget = self.cuesWidget.itemWidget(self.cuesWidget.item(n))
            target_id, action = widget.get_target()
            targets.append((target_id, action))

        return {'targets': targets}

    def _add_cue(self, cue, action):
        item = QListWidgetItem()
        item.setSizeHint(QSize(200, 30))

        widget = CueItemWidget(cue, action, self.cue_dialog)

        self.cuesWidget.addItem(item)
        self.cuesWidget.setItemWidget(item, widget)
        self.cue_dialog.remove_cue(cue)

    def _add_dialog(self):
        if self.cue_dialog.exec_() == QDialog.Accepted:
            for target in self.cue_dialog.selected_cues():
                self._add_cue(target, tuple(target.CueActions)[0].name)

    def _remove_selected(self):
        cue = self.cuesWidget.itemWidget(self.cuesWidget.currentItem()).target

        self.cuesWidget.takeItem(self.cuesWidget.currentRow())
        self.cue_dialog.add_cue(cue)
Beispiel #18
0
class Gui(QMainWindow):
    mainText = None
    mast = None
    tenants = None
    inc = None
    rowStore = None
    Gen = None
    gen = None
    mastInput = None
    keyNum = 1

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        app_icon = QIcon()
        app_icon.addFile("key.png", QSize(256, 256))
        self.setWindowIcon(app_icon)
        # open
        openFile = QAction('Open', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('Open new File')
        openFile.triggered.connect(self.fileOpen)
        # save
        saveFile = QAction('Save', self)
        saveFile.setShortcut('Ctrl+S')
        saveFile.setStatusTip('Save new File')
        saveFile.triggered.connect(self.fileSave)
        printAction = QAction("Print", self)
        printAction.triggered.connect(self.printSetup)
        # exit
        exitAction = QAction('Exit', self)
        exitAction.triggered.connect(self.closeEvent)
        # menu object
        menubar = self.menuBar()
        # file drop down
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(openFile)
        fileMenu.addAction(saveFile)
        fileMenu.addAction(printAction)
        fileMenu.addAction(exitAction)
        # widgets
        grid = QGridLayout()
        horiz = QVBoxLayout()
        bigHoriz = QHBoxLayout()
        horizLayout = QHBoxLayout()
        window = QWidget()
        window.setLayout(bigHoriz)
        leftPane = QFormLayout()
        bigHoriz.addLayout(leftPane)
        bigHoriz.addLayout(horiz)
        self.setCentralWidget(window)
        btn = QPushButton('Generate', self)
        btn.clicked.connect(lambda: self.runGen())
        clearBtn = QPushButton("Clear", self)
        clearBtn.clicked.connect(self.clearList)
        self.mainText = QListWidget(self)
        self.mainText.itemSelectionChanged.connect(self.listItemClicked)
        self.mainText.setFont(
            QFontDatabase.systemFont(QFontDatabase.FixedFont))
        self.mastInput = []
        i = 0
        while i < 6:
            t = QLineEdit()
            t.setMaxLength(1)
            t.setAlignment(Qt.AlignHCenter)
            t.textChanged.connect(self.textInputed)
            self.mastInput.append(t)
            i = i + 1
        for e in self.mastInput:
            horizLayout.addWidget(e)
        self.mast = QLineEdit()
        self.tenants = QLineEdit()
        self.inc = QLineEdit()
        self.title = QLineEdit()
        self.title.setMinimumWidth(200)
        self.desc = QLineEdit()
        self.address = QLineEdit()
        self.contact = QLineEdit()
        self.phone = QLineEdit()
        self.email = QLineEdit()
        self.notes = QTextEdit()
        self.keyway = QLineEdit()
        label = QLabel("Master Cuts")
        incLabel = QLabel("Increment")
        tenantLabel = QLabel("Tenants")
        # add widgets to layouts
        leftPane.addRow(QLabel("Title"), self.title)
        leftPane.addRow(QLabel("Description"), self.desc)
        leftPane.addRow(QLabel("Keyway"), self.keyway)
        leftPane.addRow(QLabel("Address"), self.address)
        leftPane.addRow(QLabel("contact"), self.contact)
        leftPane.addRow(QLabel("Phone"), self.phone)
        leftPane.addRow(QLabel("Email"), self.email)
        leftPane.addRow(QLabel("Notes"), self.notes)
        grid.addWidget(incLabel, 3, 0)
        grid.addWidget(tenantLabel, 2, 0)
        grid.addWidget(label, 1, 0)
        grid.addWidget(btn, 0, 0)
        horiz.addWidget(self.mainText)
        horiz.addLayout(grid)
        # horiz.addLayout(horizLayout)
        grid.addWidget(clearBtn, 0, 1)
        grid.addWidget(self.tenants, 2, 1)
        grid.addWidget(self.inc, 3, 1)
        grid.addLayout(horizLayout, 1, 1)
        # window properties
        self.setGeometry(300, 300, 500, 425)
        self.setWindowTitle('PySchlageGen')
        self.show()

    def textInputed(self, string):
        if len(string) == 1:
            self.focusNextChild()

    def getGen(self):
        return self.gen

    def runGen(self):
        self.mainText.clear()
        self.keyNum = 1
        text = self.mast.text()
        mastCuts = []
        try:
            for e in self.mastInput:
                if e.text():
                    mastCuts.append(int(e.text()))
            tenants = int(self.tenants.text())
            inc = int(self.inc.text())
            mastCuts = list(map(int, mastCuts))
            self.gen = schlageGen()
            self.gen.addMasterKey(mastCuts)
            output = self.gen.genSystem(tenants, inc)
            self.displayKeys(output)
        except:
            pass

    def displayKeys(self, output):
        i = 0
        for o in output:
            if self.keyNum < 10:
                f = str(self.keyNum) + ":     "
            elif self.keyNum < 100:
                f = str(self.keyNum) + ":   "
            elif self.keyNum < 1000:
                f = str(self.keyNum) + ": "
            else:
                f = str(self.keyNum) + ":"
            for e in o:
                f = f + str(e) + " "
            item = QListWidgetItem(f)
            self.mainText.insertItem(i, item)
            i = i + 1
            self.keyNum = self.keyNum + 1

    def formatText(self, flist, space=True, inj=" "):
        out = ""
        for e in flist[:-1]:
            out = out + str(e)
            if space:
                out = out + inj
        out = out + str(flist[-1])
        return out

    def printSetup(self):
        if self.gen != None:
            printSetup = PrintSetup(self)
            printSetup.exec()
        else:
            QMessageBox.about(self, "Error","Please generate a system before printing.")

    def clearList(self):
        self.title.clear()
        self.desc.clear()
        self.address.clear()
        self.keyway.clear()
        self.contact.clear()
        self.phone.clear()
        self.email.clear()
        self.keyNum = 1
        self.notes.clear()
        self.mainText.clear()
        self.tenants.clear()
        self.inc.clear()
        for e in self.mastInput:
            e.clear()

    def listItemClicked(self):
        item = self.mainText.currentItem()
        flags = item.flags()
        if flags & Qt.ItemIsEnabled:
            if self.rowStore != None:
                self.mainText.takeItem(self.rowStore + 1)
                self.mainText.takeItem(self.rowStore + 1)
            tenCuts = self.gen.getSystem()[int(item.text().split(":")[0]) - 1]
            tenCuts = list(map(int, tenCuts))
            output = self.gen.bittingCalc(tenCuts)
            row = self.mainText.currentRow()
            self.rowStore = row
            flags = item.flags()
            flags ^= Qt.ItemIsEnabled
            item = QListWidgetItem("        " + self.formatText(output[0]))
            item.setFlags(flags)
            item2 = QListWidgetItem("        " + self.formatText(output[1]))
            item2.setFlags(flags)
            self.mainText.insertItem(row + 1, item)
            self.mainText.insertItem(row + 2, item2)

    def fileOpen(self):
        self.clearList()
        home = expanduser("~")
        fname = QFileDialog.getOpenFileName(self, 'Open file', home, "*.mks")
        data = None
        if fname[0] != '':
            with open(fname[0], 'r') as infile:
                data = infile.read()
            sys = data.split("`")
            self.gen = schlageGen()
            master = list(map(int, sys[0]))
            self.gen.addMasterKey(master)
            del sys[0]
            self.inc.setText(str(sys[0]))
            del sys[0]
            self.title.setText(str(sys[0]))
            del sys[0]
            self.desc.setText(str(sys[0]))
            del sys[0]
            self.keyway.setText(str(sys[0]))
            del sys[0]
            self.address.setText(str(sys[0]))
            del sys[0]
            self.contact.setText(str(sys[0]))
            del sys[0]
            self.phone.setText(str(sys[0]))
            del sys[0]
            self.email.setText(str(sys[0]))
            del sys[0]
            self.notes.setPlainText(str(sys[0]))
            del sys[0]
            self.gen.setTenants(sys)
            self.displayKeys(sys)
            i = 0
            while i < len(master):
                self.mastInput[i].setText(str(master[i]))
                i = i + 1
            self.tenants.setText(str(len(sys)))

    def fileSave(self):
        if self.gen != None:
            home = expanduser("~")
            fname = QFileDialog.getSaveFileName(self, 'Open file', home, "*.mks")
            if fname[0]:
                with open(fname[0], "w") as thefile:
                    thefile.write("%s`" % self.formatText(
                        self.gen.getMasterKey(), False))
                    thefile.write("%s`" % self.inc.text())
                    thefile.write("%s`" % self.title.text())
                    thefile.write("%s`" % self.desc.text())
                    thefile.write("%s`" % self.keyway.text())
                    thefile.write("%s`" % self.address.text())
                    thefile.write("%s`" % self.contact.text())
                    thefile.write("%s`" % self.phone.text())
                    thefile.write("%s`" % self.email.text())
                    thefile.write("%s`" % self.notes.toPlainText())
                    for e in self.gen.getSystem()[:-1]:
                        thefile.write("%s`" % self.formatText(e, False))
                    thefile.write("%s" % self.formatText(
                        self.gen.getSystem()[-1], False))
        else:
            QMessageBox.about(self, "Error","Please generate a system before saving.")

    def closeEvent(self, event):
        reply = QMessageBox.question(self, 'Message',
                                     "Are you sure to quit?", QMessageBox.Yes |
                                     QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            sys.exit()
        else:
            if event:
                event.ignore()
Beispiel #19
0
class EventsPage(QWidget):
    events_valid = pyqtSignal(int, bool)

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

        self.event_list = QListWidget()
        info_layout = QGridLayout()
        event_btn_layout = QVBoxLayout()

        self.up_btn = QPushButton(QIcon(":/icons/up.png"), "")
        self.add_btn = QPushButton(QIcon(":/icons/add.png"), "")
        self.rem_btn = QPushButton(QIcon(":/icons/remove.png"), "")
        self.down_btn = QPushButton(QIcon(":/icons/down.png"), "")
        self.rem_btn.setEnabled(False)
        self.up_btn.setEnabled(False)
        self.down_btn.setEnabled(False)
        self.rem_btn.clicked.connect(self.rem_event)
        self.add_btn.clicked.connect(self.add_event)
        self.up_btn.clicked.connect(self.move_up)
        self.down_btn.clicked.connect(self.move_down)
        event_btn_layout.addWidget(self.up_btn)
        event_btn_layout.addWidget(self.add_btn)
        event_btn_layout.addWidget(self.rem_btn)
        event_btn_layout.addWidget(self.down_btn)
        field_title = QLabel("<font color=\"red\">*</font> Events:")

        self.event_list.itemClicked.connect(self.item_selected)
        info_layout.addWidget(field_title, 0, 0, 1, -1)
        info_layout.addWidget(self.event_list, 1, 0, 4, 3)
        info_layout.addLayout(event_btn_layout, 1, 3, 4, 1)
        self.setLayout(info_layout)

    @pyqtSlot(list)
    def load_from_file(self, events):
        self.event_list.clear()
        for event in events:
            self.event_list.addItem(event)
        self.validate()

    @pyqtSlot(QListWidgetItem)
    def item_selected(self, item):
        self.cur_item = item
        self.rem_btn.setEnabled(True)
        self.up_btn.setEnabled(True)
        self.down_btn.setEnabled(True)
        if self.event_list.currentRow() == 0:
            self.up_btn.setEnabled(False)
        if self.event_list.currentRow() == self.event_list.count() - 1:
            self.down_btn.setEnabled(False)

    @pyqtSlot()
    def rem_event(self):
        self.event_list.takeItem(self.event_list.currentRow())
        if not self.event_list.count():
            self.rem_btn.setEnabled(False)
            self.up_btn.setEnabled(False)
            self.down_btn.setEnabled(False)
            self.events_valid.emit(2, False)
        if self.event_list.currentRow() == 0:
            self.up_btn.setEnabled(False)
        if self.event_list.currentRow() == self.event_list.count() - 1:
            self.down_btn.setEnabled(False)

    @pyqtSlot()
    def add_event(self):
        valid = False
        new_event = None
        while not valid:
            new_event = QInputDialog.getText(self, 'Add New Event', 'Event:')
            if not new_event[1]:
                return
            if not str.strip(new_event[0]):
                QMessageBox.critical(
                    self,
                    "Error",
                    "Event cannot be blank",
                )
            elif new_event[0] in [
                    self.event_list.item(i).text()
                    for i in range(self.event_list.count())
            ]:
                QMessageBox.critical(
                    self,
                    "Error",
                    "Event already exists",
                )
            else:
                valid = True

        self.event_list.addItem(new_event[0])
        self.event_list.setCurrentRow(self.event_list.count() - 1)
        self.events_valid.emit(2, True)

    @pyqtSlot()
    def move_up(self):
        self.down_btn.setEnabled(True)
        cur_index = self.event_list.currentRow()
        cur_item = self.event_list.takeItem(cur_index)
        self.event_list.insertItem(cur_index - 1, cur_item)
        self.event_list.setCurrentRow(cur_index - 1)
        if self.event_list.currentRow() == 0:
            self.up_btn.setEnabled(False)

    @pyqtSlot()
    def move_down(self):
        self.up_btn.setEnabled(True)
        cur_index = self.event_list.currentRow()
        cur_item = self.event_list.takeItem(cur_index)
        self.event_list.insertItem(cur_index + 1, cur_item)
        self.event_list.setCurrentRow(cur_index + 1)
        if self.event_list.currentRow() == self.event_list.count() - 1:
            self.down_btn.setEnabled(False)

    def validate(self):
        self.events_valid.emit(2, self.event_list.count() > 0)
class GstPipeEdit(QDialog):

    def __init__(self, pipe, preferences_mode=False, **kwargs):
        super().__init__(**kwargs)

        self.setWindowTitle('Edit Pipeline')
        self.setWindowModality(Qt.ApplicationModal)
        self.setLayout(QGridLayout())
        self.setMaximumSize(500, 400)
        self.setMinimumSize(500, 400)
        self.resize(500, 400)

        self._preferences_mode = preferences_mode

        # Input selection
        self.inputBox = QComboBox(self)
        self.layout().addWidget(self.inputBox, 0, 0, 1, 3)
        self.init_inputs(pipe)

        # Current plugins list
        self.currentList = QListWidget(self)
        self.currentList.setDragEnabled(True)
        self.currentList.setDragDropMode(QAbstractItemView.InternalMove)
        self.layout().addWidget(self.currentList, 1, 0, 3, 1)
        self.init_current_plugins(pipe)

        # Add/Remove plugins buttons
        self.pluginsLayout = QVBoxLayout(self)
        self.layout().addLayout(self.pluginsLayout, 2, 1)

        self.addButton = QPushButton(self)
        self.addButton.setIcon(QIcon.fromTheme('go-previous'))
        self.addButton.clicked.connect(self.__add_plugin)
        self.pluginsLayout.addWidget(self.addButton)
        self.pluginsLayout.setAlignment(self.addButton, Qt.AlignHCenter)

        self.delButton = QPushButton(self)
        self.delButton.setIcon(QIcon.fromTheme('go-next'))
        self.delButton.clicked.connect(self.__remove_plugin)
        self.pluginsLayout.addWidget(self.delButton)
        self.pluginsLayout.setAlignment(self.delButton, Qt.AlignHCenter)

        # Available plugins list
        self.availableList = QListWidget(self)
        self.layout().addWidget(self.availableList, 1, 2, 3, 1)
        self.init_available_plugins(pipe)

        # Output selection
        self.outputBox = QComboBox(self)
        self.layout().addWidget(self.outputBox, 4, 0, 1, 3)
        self.init_outputs(pipe)

        # Confirm/Cancel buttons
        self.dialogButtons = QDialogButtonBox(self)
        self.dialogButtons.setStandardButtons(QDialogButtonBox.Cancel |
                                              QDialogButtonBox.Ok)
        self.layout().addWidget(self.dialogButtons, 5, 0, 1, 3)

        self.dialogButtons.accepted.connect(self.accept)
        self.dialogButtons.rejected.connect(self.reject)

    def init_inputs(self, pipe):
        if self._preferences_mode:
            self.inputBox.setEnabled(False)
        else:
            inputs = sorted(elements.inputs())
            self.inputBox.addItems(inputs)
            self.inputBox.setEnabled(len(inputs) > 1)
            if pipe != '':
                self.inputBox.setCurrentIndex(inputs.index(pipe.split('!')[0]))

    def init_outputs(self, pipe):
        outputs = sorted(elements.outputs())
        self.outputBox.addItems(outputs)
        self.outputBox.setEnabled(len(outputs) > 1)
        if pipe != '':
            self.outputBox.setCurrentIndex(outputs.index(pipe.split('!')[-1]))

    def init_current_plugins(self, pipe):
        start = 0 if self._preferences_mode else 1
        for plugin in pipe.split('!')[start:-1]:
            self.currentList.addItem(plugin)

    def init_available_plugins(self, pipe):
        currents = pipe.split('!')
        for plugin in elements.plugins().values():
            if plugin.Name not in currents:
                self.availableList.addItem(plugin.Name)

    def get_pipe(self):
        pipe = [] if self._preferences_mode else [self.inputBox.currentText()]
        for n in range(self.currentList.count()):
            pipe.append(self.currentList.item(n).text())
        pipe.append(self.outputBox.currentText())

        return '!'.join(pipe)

    def __add_plugin(self):
        item = self.availableList.takeItem(self.availableList.currentRow())
        self.currentList.addItem(item)

    def __remove_plugin(self):
        item = self.currentList.takeItem(self.currentList.currentRow())
        self.availableList.addItem(item)
Beispiel #21
0
class EditTasksOptionsGUI(QDialog):
    def __init__(self, parent, iBacklogMgr):
        super().__init__(parent)

        self.backlogMgr = iBacklogMgr

        self.currTaskSelected = Task()

        self.tempoTask = Task()

        self.currRowSelected = 0

        self.newItemEnum = 0

        self.listState = State.SELECT_TASK

        self.tasksList = self.backlogMgr.getTasksFromGUI()

        self.currTaskErrors = list()

        self.initUI()

    def initUI(self):

        self.setWindowTitle("Edit tasks")
        self.resize(1000, 400)

        mainLayout = QVBoxLayout(self)
        listViewTaskViewLayout = QHBoxLayout()
        self.listViewLayout = QVBoxLayout()

        self.listTasksView = QListWidget(self)

        self.layoutCurrTask = QFormLayout()

        self.initCurrTaskLineEdits()

        self.addTaskButton = QPushButton(self)
        self.addTaskButton.setIcon(QIcon("Images//plus_icon.png"))
        self.addTaskButton.clicked.connect(self.manageAddTaskClickedButton)

        self.deleteTaskButton = QPushButton(self)
        self.deleteTaskButton.setIcon(QIcon("Images//minus_icon.png"))
        self.deleteTaskButton.clicked.connect(
            self.manageDeleteTaskClickedButton)

        self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok
                                          | QDialogButtonBox.Cancel)

        self.buttonbox.accepted.connect(self.accept)
        self.buttonbox.rejected.connect(self.reject)

        self.listViewLayout.addWidget(self.listTasksView)
        self.listViewLayout.addWidget(self.addTaskButton)
        self.listViewLayout.addWidget(self.deleteTaskButton)

        listViewTaskViewLayout.addLayout(self.listViewLayout)
        listViewTaskViewLayout.addLayout(self.layoutCurrTask)

        mainLayout.addLayout(listViewTaskViewLayout)
        mainLayout.addWidget(self.buttonbox)

        self.invalidTaskErrMsg = QMessageBox()
        self.invalidTaskErrMsg.setText("The current task is invalid.")
        self.invalidTaskErrMsg.setIcon(QMessageBox.Critical)

        self.loadTasksOptions()

        self.updateCurrTaskSelected()

    def exec_(self):

        self.tasksList = self.backlogMgr.getTasksFromGUI()

        for currRow in range(0, len(self.tasksList)):
            self.updateTextRowListView(currRow, self.tasksList[currRow].title)

        self.updateCurrTaskSelected()

        self.updateCurrTaskLineEdits()

        self.titleInputErr.setVisible(False)
        self.prjCodeInputErr.setVisible(False)

        self.exec()

    def initCurrTaskLineEdits(self):

        self.titleInputErr = QLabel(self)

        self.titleInput = QLineEdit(self)
        self.titleInput.editingFinished.connect(self.manageTitleEntered)

        self.prjCodeInputErr = QLabel(self)
        self.prjCodeInput = QLineEdit(self)
        self.completedTime = QLineEdit(self)
        self.estimatedTime = QLineEdit(self)
        self.percAccomplished = QLineEdit(self)
        self.percAccomplished.setEnabled(False)

        self.redPalette = QPalette()
        redColor = QColor(255, 0, 0)
        self.redPalette.setColor(QPalette.WindowText, redColor)

        self.titleInputErr.setText("* A title is required.")
        self.layoutCurrTask.addWidget(self.titleInputErr)
        self.titleInputErr.setVisible(False)
        self.titleInputErr.setPalette(self.redPalette)

        self.layoutCurrTask.addRow("Title:", self.titleInput)

        self.prjCodeInputErr.setText("* A project code is required.")
        self.layoutCurrTask.addWidget(self.prjCodeInputErr)
        self.prjCodeInputErr.setVisible(False)
        self.prjCodeInputErr.setPalette(self.redPalette)

        self.layoutCurrTask.addRow("Project code:", self.prjCodeInput)
        self.layoutCurrTask.addRow("Completed time:", self.completedTime)
        self.layoutCurrTask.addRow("Estimated time:", self.estimatedTime)
        self.layoutCurrTask.addRow("Percentage accomplished:",
                                   self.percAccomplished)

    def manageTitleEntered(self):

        self.updateTextRowListView(self.currRowSelected,
                                   self.titleInput.text())

    def manageCurrRowChangeList(self):

        prevRowSelected = self.currRowSelected
        self.currRowSelected = self.listTasksView.currentRow()

        if self.currRowSelected == self.backlogMgr.backlogData.currTaskIndex:
            self.deleteTaskButton.setEnabled(False)
        else:
            self.deleteTaskButton.setEnabled(True)

        if self.listState == State.ADDING_TASK:

            self.tasksList.append(Task())
            self.updateCurrTaskSelected()

        elif self.listState == State.DELETE_TASK:

            countListItems = self.listTasksView.count()

            if countListItems > 0 and prevRowSelected >= 0:
                self.tasksList.pop(prevRowSelected)

                if self.currRowSelected > prevRowSelected:
                    self.currRowSelected = prevRowSelected
            else:
                pass

            self.updateCurrTaskSelected()

        else:

            self.updateTaskFromLineEdits(self.tempoTask)

            isTaskNotValid = self.backlogMgr.validateTask(self.tempoTask)

            if len(isTaskNotValid):
                self.titleInputErr.setVisible(True)
                self.prjCodeInputErr.setVisible(True)
                self.currRowSelected = prevRowSelected
                self.listTasksView.setCurrentRow(self.currRowSelected)

            else:
                self.titleInputErr.setVisible(False)
                self.prjCodeInputErr.setVisible(False)

                self.updateTaskFromLineEdits(self.currTaskSelected)

                self.updateTextRowListView(prevRowSelected,
                                           self.tempoTask.title)

                self.updateCurrTaskSelected()

    def updateCurrTaskSelected(self):

        if self.currRowSelected >= 0 and \
           self.currRowSelected < len(self.tasksList):

            self.currTaskSelected = self.tasksList[self.currRowSelected]
        else:
            self.currTaskSelected = Task()

        self.updateCurrTaskLineEdits()

    def updateCurrTaskLineEdits(self):

        self.titleInput.setText(self.currTaskSelected.title)
        self.prjCodeInput.setText(self.currTaskSelected.prjCode)

        compTime = str(
            datetime.timedelta(seconds=self.currTaskSelected.completedTime))
        self.completedTime.setText(compTime)

        estimatedTime = str(
            datetime.timedelta(seconds=self.currTaskSelected.estimatedTime))
        self.estimatedTime.setText(estimatedTime)

        self.percAccomplished = str(self.currTaskSelected.completionRatio)

    def loadTasksOptions(self):

        self.initListViewOfTasks()

    def initListViewOfTasks(self):

        for currIndex, currTask in enumerate(self.tasksList):

            self.listTasksView.addItem("")

            self.updateTextRowListView(currIndex, currTask.title)

        self.currRowSelected = 0
        self.listTasksView.setCurrentRow(self.currRowSelected)

        self.listTasksView.itemSelectionChanged.connect(
            self.manageCurrRowChangeList)

    def manageAddTaskClickedButton(self):

        self.listState = State.ADDING_TASK

        self.newItemEnum += 1

        self.listTasksView.addItem('New Item {}'.format(str(self.newItemEnum)))

        lastRowListView = self.listTasksView.count() - 1
        self.listTasksView.setCurrentRow(lastRowListView)

        self.listState = State.SELECT_TASK

    def manageDeleteTaskClickedButton(self):

        self.newItemEnum -= 1

        self.listState = State.DELETE_TASK

        self.listTasksView.takeItem(self.currRowSelected)

        self.listState = State.SELECT_TASK

    def updateTaskFromLineEdits(self, iTask):

        iTask.title = self.titleInput.text()
        iTask.prjCode = self.prjCodeInput.text()

        iTask.setCompletedTime(self.completedTime.text())
        iTask.setEstimatedTime(self.estimatedTime.text())

    def closeEvent(self, event):

        self.reject()

    def accept(self):

        self.updateTaskFromLineEdits(self.tempoTask)

        isTaskNotValid = self.backlogMgr.validateTask(self.tempoTask)

        if len(isTaskNotValid):

            self.invalidTaskErrMsg.exec()
            self.titleInputErr.setVisible(True)
            self.prjCodeInputErr.setVisible(True)
            return

        else:
            self.updateTaskFromLineEdits(self.currTaskSelected)
            self.backlogMgr.replaceTasksBacklogData(self.tasksList)

        self.close()

    def updateTextRowListView(self, iRow, iTaskTitle):

        item = self.listTasksView.item(iRow)

        item.setText(iTaskTitle)
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)
Beispiel #23
0
class OptionWidget(QWidget):
    def __init__(self, *args, **kwargs):
        super(OptionWidget, self).__init__(*args, **kwargs)
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(QMargins(0, 0, 0, 0))

        title_layout = QHBoxLayout()
        title_layout.addWidget(QLabel("标题:", self))
        self.title_edit = QLineEdit(self)
        self.title_edit.setPlaceholderText("图形标题(保存配置时必填)")
        title_layout.addWidget(self.title_edit)
        title_layout.addWidget(QLabel("大小:", self))
        self.title_fontsize = QSpinBox(self)
        self.title_fontsize.setMinimum(10)
        self.title_fontsize.setMaximum(100)
        self.title_fontsize.setValue(22)
        title_layout.addWidget(self.title_fontsize)
        main_layout.addLayout(title_layout)

        x_axis = QHBoxLayout()
        x_axis.addWidget(QLabel("横轴:", self))
        self.x_axis_combobox = QComboBox(self)
        self.x_axis_combobox.setMinimumWidth(150)
        x_axis.addWidget(self.x_axis_combobox)
        x_axis.addStretch()
        main_layout.addLayout(x_axis)

        x_format = QHBoxLayout()
        x_format.addWidget(QLabel("格式:", self))
        self.date_length = QComboBox(self)
        self.date_length.addItem('年-月-日', 10)
        self.date_length.addItem('年-月', 7)
        self.date_length.addItem('年', 4)
        self.date_length.setMinimumWidth(150)
        x_format.addWidget(self.date_length)
        x_format.addStretch()
        main_layout.addLayout(x_format)

        add_indicator = QGroupBox("点击选择添加指标", self)
        self.indicator_list = QListWidget(self)
        add_indicator_layout = QVBoxLayout()
        add_indicator.setLayout(add_indicator_layout)
        add_indicator_layout.addWidget(self.indicator_list)

        # 添加指标的按钮
        button_layout = QGridLayout()
        indicator_button1 = QPushButton("左轴线型", self)
        setattr(indicator_button1, "axis_index", 0)
        setattr(indicator_button1, "chart", "line")
        indicator_button1.clicked.connect(self.selected_indicator)
        button_layout.addWidget(indicator_button1, 0, 0)
        indicator_button2 = QPushButton("左轴柱型", self)
        setattr(indicator_button2, "axis_index", 0)
        setattr(indicator_button2, "chart", "bar")
        indicator_button2.clicked.connect(self.selected_indicator)
        button_layout.addWidget(indicator_button2, 1, 0)
        indicator_button3 = QPushButton("右轴线型", self)
        setattr(indicator_button3, "axis_index", 1)
        setattr(indicator_button3, "chart", "line")
        indicator_button3.clicked.connect(self.selected_indicator)
        button_layout.addWidget(indicator_button3, 0, 1)
        indicator_button4 = QPushButton("右轴柱型", self)
        setattr(indicator_button4, "axis_index", 1)
        setattr(indicator_button4, "chart", "bar")
        indicator_button4.clicked.connect(self.selected_indicator)
        button_layout.addWidget(indicator_button4, 1, 1)
        add_indicator_layout.addLayout(button_layout)
        main_layout.addWidget(add_indicator)

        current_indicator = QGroupBox("双击删除已选指标", self)
        current_indicator_layout = QVBoxLayout()
        current_indicator.setLayout(current_indicator_layout)
        self.current_indicator_list = QListWidget(self)
        self.current_indicator_list.itemDoubleClicked.connect(self.remove_selected_indicator)  # 移除已选指标
        self.current_indicator_list.itemClicked.connect(self.change_check_state)               # 修改去0选项
        current_indicator_layout.addWidget(self.current_indicator_list)
        main_layout.addWidget(current_indicator)

        # 水印
        graphic_layout = QHBoxLayout()
        graphic_layout.setSpacing(1)
        self.has_graphic = QCheckBox(self)
        self.has_graphic.setText("添加水印")
        self.water_graphic = QLineEdit(self)
        self.water_graphic.setText("瑞达期货研究院")
        graphic_layout.addWidget(self.has_graphic)
        graphic_layout.addWidget(self.water_graphic)
        graphic_layout.addStretch()
        main_layout.addLayout(graphic_layout)
        # 取数范围
        range_layout = QHBoxLayout()
        range_layout.setSpacing(1)
        self.start_year = QComboBox(self)
        self.end_year = QComboBox(self)
        self.range_check = QCheckBox(self)
        self.range_check.setText("取数范围")
        range_layout.addWidget(self.range_check)
        range_layout.addWidget(self.start_year)
        middle_label = QLabel("至", self)
        middle_label.setContentsMargins(QMargins(2, 0, 2, 0))
        range_layout.addWidget(middle_label)
        range_layout.addWidget(self.end_year)
        range_layout.addStretch()
        main_layout.addLayout(range_layout)

        other_layout = QHBoxLayout()
        tip_label = QLabel("勾选时,范围0表示不限制.", self)
        tip_label.setStyleSheet("color:rgb(180,60,60);max-height:15px")
        other_layout.addWidget(tip_label)
        self.more_dispose_button = QPushButton("更多配置", self)
        other_layout.addWidget(self.more_dispose_button, alignment=Qt.AlignRight)
        main_layout.addLayout(other_layout)

        draw_layout = QHBoxLayout()
        self.chart_drawer = QPushButton('确认绘图', self)
        setattr(self.chart_drawer, "chart_type", "normal")
        draw_layout.addWidget(self.chart_drawer)
        self.season_drawer = QPushButton('季节图表', self)
        setattr(self.season_drawer, "chart_type", "season")
        draw_layout.addWidget(self.season_drawer)
        main_layout.addLayout(draw_layout)

        self.setLayout(main_layout)

    def selected_indicator(self):
        """ 选择指标准备绘图 """
        current_indicator = self.indicator_list.currentItem()
        sender_button = self.sender()
        if not current_indicator:
            return
        indicator_column = current_indicator.data(Qt.UserRole)
        indicator_text = current_indicator.text()
        axis_index = getattr(sender_button, "axis_index")
        chart_type = getattr(sender_button, "chart")
        button_text = sender_button.text()
        indicator_params = {
            "column_index": indicator_column,
            "axis_index": axis_index,
            "chart_type": chart_type,
            "chart_name": indicator_text,
            "contain_zero": 1
        }
        # 重复指标和类型不再添加了
        for item_at in range(self.current_indicator_list.count()):
            item = self.current_indicator_list.item(item_at)
            exist_list = item.data(Qt.UserRole)
            if exist_list == indicator_params:
                return
        # 已选指标内添加指标
        selected_indicator_item = QListWidgetItem("(数据含0) " + indicator_text + " = " + button_text)
        selected_indicator_item.setCheckState(Qt.Unchecked)
        selected_indicator_item.setData(Qt.UserRole, indicator_params)
        self.current_indicator_list.addItem(selected_indicator_item)

    def remove_selected_indicator(self, _):
        """ 删除已选的指标 """
        current_row = self.current_indicator_list.currentRow()
        item = self.current_indicator_list.takeItem(current_row)
        del item

    @staticmethod
    def change_check_state(item):
        """ 修改item的去0选项"""
        text = item.text()
        if item.checkState():
            suffix_text = text[:6].replace("去", "含")
            indicator_params = item.data(Qt.UserRole)
            indicator_params["contain_zero"] = 1
            item.setCheckState(Qt.Unchecked)
        else:
            suffix_text = text[:6].replace("含", "去")
            indicator_params = item.data(Qt.UserRole)
            indicator_params["contain_zero"] = 0
            item.setCheckState(Qt.Checked)
        item.setData(Qt.UserRole, indicator_params)     # 重新设置item的Data
        text = suffix_text + text[6:]
        item.setText(text)

    def get_base_option(self):
        """ 图形的基本配置 """
        # typec表示图形的类型,single单表绘制,compose组合表,calculate计算绘制
        option = dict()
        # 标题
        option["typec"] = "single"
        option["title"] = {
            "text": self.title_edit.text().strip(),
            "font_size": self.title_fontsize.value()
        }
        # x轴
        option["x_axis"] = {
            "column_index": self.x_axis_combobox.currentData(),
            "date_length": self.date_length.currentData()
        }
        # y轴
        option["y_axis"] = [
            {"type": "value", "name": ""}
        ]
        # 数据序列
        series_data = []
        for item_at in range(self.current_indicator_list.count()):
            item = self.current_indicator_list.item(item_at)
            item_data = item.data(Qt.UserRole)
            if item_data["axis_index"] == 1 and len(option["y_axis"]) == 1:  # 如果有右轴添加右轴
                option["y_axis"].append({"type": "value", "name": ""})
            series_data.append(item_data)
        option["series_data"] = series_data

        option["watermark"] = ""
        if self.has_graphic.isChecked():
            option["watermark"] = self.water_graphic.text().strip()
        option["start_year"] = "0"
        option["end_year"] = "0"
        if self.range_check.isChecked():
            option["start_year"] = self.start_year.currentText()
            option["end_year"] = self.end_year.currentText()
        return option
Beispiel #24
0
class ProfilesLoader(QDialog):

    def __init__(self, load_func, create_func, save_func,
                 profiles, parent=None):
        super(ProfilesLoader, self).__init__(self, parent, Qt.Dialog)
        self.setWindowTitle(_translate("ProfilesLoader", "Profile Manager"))
        self.setMinimumWidth(400)
        self._profiles = profiles
        self.load_function = load_func
        self.create_function = create_func
        self.save_function = save_func
        self.ide = parent
        vbox = QVBoxLayout(self)
        vbox.addWidget(QLabel(_translate("ProfilesLoader", "Save your opened files and projects "
                                      "into a profile and change really"
                                      "quick between projects and"
                                      "files sessions.\n This allows you to "
                                      "save your working environment, "
                                      "keep working in another\n"
                                      "project and then go back "
                                      "exactly where you left.")))
        self.profileList = QListWidget()
        self.profileList.addItems([key for key in profiles])
        self.profileList.setCurrentRow(0)
        self.contentList = QListWidget()
        self.btnDelete = QPushButton(_translate("ProfilesLoader", "Delete Profile"))
        self.btnDelete.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.btnUpdate = QPushButton(_translate("ProfilesLoader", "Update Profile"))
        self.btnUpdate.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.btnCreate = QPushButton(_translate("ProfilesLoader", "Create New Profile"))
        self.btnCreate.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.btnOpen = QPushButton(_translate("ProfilesLoader", "Open Profile"))
        self.btnOpen.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.btnOpen.setDefault(True)
        hbox = QHBoxLayout()
        hbox.addWidget(self.btnDelete)
        hbox.addWidget(self.btnUpdate)
        hbox.addWidget(self.btnCreate)
        hbox.addWidget(self.btnOpen)

        vbox.addWidget(self.profileList)
        vbox.addWidget(self.contentList)
        vbox.addLayout(hbox)

        self.profileList.itemSelectionChanged.connect(self.load_profile_content)
        self.btnOpen.clicked['bool'].connect(self.open_profile)
        self.btnUpdate.clicked['bool'].connect(self.save_profile)
        self.btnCreate.clicked['bool'].connect(self.create_profile)
        self.btnDelete.clicked['bool'].connect(self.delete_profile)

    def load_profile_content(self):
        item = self.profileList.currentItem()
        self.contentList.clear()
        if item is not None:
            key = item.text()
            files = [_translate("ProfilesLoader", 'Files:')] + \
                [file[0] for file in self._profiles[key][0]]
            projects = [_translate("ProfilesLoader", 'Projects:')] + self._profiles[key][1]
            content = files + projects
            self.contentList.addItems(content)

    def create_profile(self):
        profileName = self.create_function()
        self.ide.Profile = profileName
        self.close()

    def save_profile(self):
        if self.profileList.currentItem():
            profileName = self.profileList.currentItem().text()
            self.save_function(profileName)
            self.ide.show_status_message(_translate("ProfilesLoader", "Profile %s Updated!") %
                                         profileName)
            self.load_profile_content()

    def open_profile(self):
        if self.profileList.currentItem():
            key = self.profileList.currentItem().text()
            self.load_function(key)
            self.ide.Profile = key
            self.close()

    def delete_profile(self):
        if self.profileList.currentItem():
            key = self.profileList.currentItem().text()
            self._profiles.pop(key)
            self.profileList.takeItem(self.profileList.currentRow())
            self.contentList.clear()
class CodeCompletionWidget(QFrame):

    def __init__(self, editor):
        super(CodeCompletionWidget, self).__init__(
            None, Qt.FramelessWindowHint | Qt.ToolTip)
        self._editor = editor
        self._revision = 0
        self._block = 0
        self.stack_layout = QStackedLayout(self)
        self.stack_layout.setContentsMargins(0, 0, 0, 0)
        self.stack_layout.setSpacing(0)
        self.completion_list = QListWidget()
        self.completion_list.setMinimumHeight(200)
        self.completion_list.setAlternatingRowColors(True)
        self._list_index = self.stack_layout.addWidget(self.completion_list)

        self._icons = {'a': resources.IMAGES['attribute'],
                       'f': resources.IMAGES['function'],
                       'c': resources.IMAGES['class'],
                       'm': resources.IMAGES['module']}

        self.cc = code_completion.CodeCompletion()
        self._completion_results = {}
        self._prefix = ''
        self.setVisible(False)
        self.source = ''
        self._key_operations = {
            Qt.Key_Up: self._select_previous_row,
            Qt.Key_Down: self._select_next_row,
            Qt.Key_PageUp: (lambda: self._select_previous_row(6)),
            Qt.Key_PageDown: (lambda: self._select_next_row(6)),
            Qt.Key_Right: lambda: None,
            Qt.Key_Left: lambda: None,
            Qt.Key_Enter: self.pre_key_insert_completion,
            Qt.Key_Return: self.pre_key_insert_completion,
            Qt.Key_Tab: self.pre_key_insert_completion,
            Qt.Key_Space: self.hide_completer,
            Qt.Key_Escape: self.hide_completer,
            Qt.Key_Backtab: self.hide_completer,
            Qt.NoModifier: self.hide_completer,
            Qt.ShiftModifier: self.hide_completer,
        }

        self.desktop = QApplication.instance().desktop()

        self.completion_list.itemClicked['QListWidgetItem*'].connect(self.pre_key_insert_completion)
        self._editor.document().cursorPositionChanged['const QTextCursor &'].connect(self.update_metadata)

    def _select_next_row(self, move=1):
        new_row = self.completion_list.currentRow() + move
        if new_row < self.completion_list.count():
            self.completion_list.setCurrentRow(new_row)
        else:
            self.completion_list.setCurrentRow(0)
        return True

    def _select_previous_row(self, move=1):
        new_row = self.completion_list.currentRow() - move
        if new_row >= 0:
            self.completion_list.setCurrentRow(new_row)
        else:
            self.completion_list.setCurrentRow(
                self.completion_list.count() - move)
        return True

    def update_metadata(self, cursor):
        if settings.CODE_COMPLETION:
            if self._editor.document().revision() != self._revision and \
               cursor.block().blockNumber() != self._block:
                source = self._editor.get_text()
                source = source.encode(self._editor.encoding)
                self.cc.analyze_file(self._editor.ID, source,
                                     self._editor.indent, self._editor.useTabs)
                self._revision = self._editor.document().revision()
                self._block = cursor.block().blockNumber()

    def insert_completion(self, insert, type_=ord('a')):
        if insert != self._prefix:
            closing = ''
            if type_ in (ord('f'), ord('c')):
                closing = '()'
            extra = len(self._prefix) - len(insert)
            insertion = '%s%s' % (insert[extra:], closing)
            self._editor.textCursor().insertText(insertion)
        self.hide_completer()

    def _get_geometry(self):
        cr = self._editor.cursorRect()
        desktop_geometry = self.desktop.availableGeometry(self._editor)
        point = self._editor.mapToGlobal(cr.topLeft())
        cr.moveTopLeft(point)
        #Check new position according desktop geometry
        width = (self.completion_list.sizeHintForColumn(0) +
                 self.completion_list.verticalScrollBar().sizeHint().width() +
                 10)
        height = 200
        orientation = (point.y() + height) < desktop_geometry.height()
        if orientation:
            cr.moveTop(cr.bottom())
        cr.setWidth(width)
        cr.setHeight(height)
        if not orientation:
            cr.moveBottom(cr.top())
        xpos = desktop_geometry.width() - (point.x() + width)
        if xpos < 0:
            cr.moveLeft(cr.left() + xpos)
        return cr

    def complete(self, results):
        self.add_list_items(results)
        self.completion_list.setCurrentRow(0)
        cr = self._get_geometry()
        self.setGeometry(cr)
        self.completion_list.updateGeometries()
        self.show()

    def add_list_items(self, proposals):
        self.completion_list.clear()
        for p in proposals:
            self.completion_list.addItem(
                QListWidgetItem(
                    QIcon(
                        self._icons.get(p[0], resources.IMAGES['attribute'])),
                    p[1], type=ord(p[0])))

    def set_completion_prefix(self, prefix, valid=True):
        self._prefix = prefix
        proposals = []
        proposals += [('m', item)
                      for item in self._completion_results.get('modules', [])
                      if item.startswith(prefix)]
        proposals += [('c', item)
                      for item in self._completion_results.get('classes', [])
                      if item.startswith(prefix)]
        proposals += [('a', item)
                      for item in self._completion_results.get(
                          'attributes', [])
                      if item.startswith(prefix)]
        proposals += [('f', item)
                      for item in self._completion_results.get('functions', [])
                      if item.startswith(prefix)]
        if proposals and valid:
            self.complete(proposals)
        else:
            self.hide_completer()

    def _invalid_completion_position(self):
        result = False
        cursor = self._editor.textCursor()
        cursor.movePosition(QTextCursor.StartOfLine,
                            QTextCursor.KeepAnchor)
        selection = cursor.selectedText()[:-1].split(' ')
        if len(selection) == 0 or selection[-1] == '' or \
           selection[-1].isdigit():
            result = True
        return result

    def fill_completer(self, force_completion=False):
        if not force_completion and (self._editor.cursor_inside_string() or
           self._editor.cursor_inside_comment() or
           self._invalid_completion_position()):
            return
        source = self._editor.get_text()
        source = source.encode(self._editor.encoding)
        offset = self._editor.textCursor().position()
        results = self.cc.get_completion(source, offset)
        self._completion_results = results
        if force_completion:
            cursor = self._editor.textCursor()
            cursor.movePosition(QTextCursor.StartOfWord,
                                QTextCursor.KeepAnchor)
            prefix = cursor.selectedText()
        else:
            prefix = self._editor._text_under_cursor()
        self.set_completion_prefix(prefix)

    def hide_completer(self):
        self._prefix = ''
        self.hide()

    def pre_key_insert_completion(self):
        type_ = ord('a')
        current = self.completion_list.currentItem()
        insert = current.text()
        if not insert.endswith(')'):
            type_ = current.type()
        self.insert_completion(insert, type_)
        self.hide_completer()
        return True

    def process_pre_key_event(self, event):
        if not self.isVisible() or self._editor.lang != "python":
            return False
        skip = self._key_operations.get(event.key(), lambda: False)()
        self._key_operations.get(event.modifiers(), lambda: False)()
        if skip is None:
            skip = False
        return skip

    def process_post_key_event(self, event):
        if not settings.CODE_COMPLETION or self._editor.lang != "python":
            return
        if self.isVisible():
            source = self._editor.get_text()
            source = source.encode(self._editor.encoding)
            offset = self._editor.textCursor().position()
            prefix, valid = self.cc.get_prefix(source, offset)
            self.set_completion_prefix(prefix, valid)
            self.completion_list.setCurrentRow(0)
        force_completion = (event.key() == Qt.Key_Space and
                            event.modifiers() == Qt.ControlModifier)
        if event.key() == Qt.Key_Period or force_completion:
            self.fill_completer(force_completion)
Beispiel #26
0
class StringListDlg(QDialog):

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

        self.name = name

        self.listWidget = QListWidget()
        if stringlist is not None:
            self.listWidget.addItems(stringlist)
            self.listWidget.setCurrentRow(0)
        buttonLayout = QVBoxLayout()
        for text, slot in (("&Add...", self.add),
                           ("&Edit...", self.edit),
                           ("&Remove...", self.remove),
                           ("&Up", self.up),
                           ("&Down", self.down),
                           ("&Sort", self.listWidget.sortItems),
                           ("Close", self.accept)):
            button = QPushButton(text)
            if not MAC:
                button.setFocusPolicy(Qt.NoFocus)
            if text == "Close":
                buttonLayout.addStretch()
            buttonLayout.addWidget(button)
            button.clicked.connect(slot)
#            self.connect(button, SIGNAL("clicked()"), slot)
        layout = QHBoxLayout()
        layout.addWidget(self.listWidget)
        layout.addLayout(buttonLayout)
        self.setLayout(layout)
        self.setWindowTitle("Edit {0} List".format(self.name))


    def add(self):
        row = self.listWidget.currentRow()
        title = "Add {0}".format(self.name)
        string, ok = QInputDialog.getText(self, title, title)
        if ok and string is not None:
            self.listWidget.insertItem(row, string)


    def edit(self):
        row = self.listWidget.currentRow()
        item = self.listWidget.item(row)
        if item is not None:
            title = "Edit {0}".format(self.name)
            string, ok = QInputDialog.getText(self, title, title,
                    QLineEdit.Normal, item.text())
            if ok and string is not None:
                item.setText(string)


    def remove(self):
        row = self.listWidget.currentRow()
        item = self.listWidget.item(row)
        if item is None:
            return
        reply = QMessageBox.question(self, "Remove {0}".format(
                self.name), "Remove {0} `{1}'?".format(
                self.name, unicode(item.text())),
                QMessageBox.Yes|QMessageBox.No)
        if reply == QMessageBox.Yes:
            item = self.listWidget.takeItem(row)
            del item


    def up(self):
        row = self.listWidget.currentRow()
        if row >= 1:
            item = self.listWidget.takeItem(row)
            self.listWidget.insertItem(row - 1, item)
            self.listWidget.setCurrentItem(item)


    def down(self):
        row = self.listWidget.currentRow()
        if row < self.listWidget.count() - 1:
            item = self.listWidget.takeItem(row)
            self.listWidget.insertItem(row + 1, item)
            self.listWidget.setCurrentItem(item)


    def reject(self):
        self.accept()


    def accept(self):
        self.stringlist = QStringListModel().stringList()
        for row in range(self.listWidget.count()):
            self.stringlist.append(self.listWidget.item(row).text())
        #self.stringlist.acceptedList.emit(self.stringlist)
        QDialog.accept(self)
Beispiel #27
0
class ContentGuiDCM2Nii(QWidget):
    def __init__(self, parent=None):
        super(QWidget, self).__init__(parent)

        # Load configuration files and general settings
        self.cfg = Configuration.load_config(ROOTDIR)
        if os.path.isdir(self.cfg['folders']['dicom']):
            self.dicomdir = self.cfg['folders']['dicom']
        else:
            self.dicomdir = FileOperations.set_wdir_in_config(self.cfg, foldername='dicom', init=True)

        self.cfg['folders']['dicom'] = self.dicomdir
        self.cfg['folders']['rootdir'] = ROOTDIR
        Configuration.save_config(ROOTDIR, self.cfg)

        # Create general layout
        self.tot_layout = QVBoxLayout(self)
        self.mid_layout = QHBoxLayout(self)

        # ============================    Create upper of  GUI, i.e. working directory   ============================
        self.folderboxDcm2nii = QGroupBox("Directory (DICOM-files)")
        self.HBoxUpperDcm2nii = QVBoxLayout(self.folderboxDcm2nii)
        self.label_dicomdir = QLabel('dicom DIR: {}'.format(self.dicomdir))
        self.HBoxUpperDcm2nii.addWidget(self.label_dicomdir)

        self.btn_dicomdir = QPushButton('Change working \ndirectory')
        self.btn_dicomdir.setFixedSize(150, 40)
        self.btn_dicomdir.clicked.connect(self.change_dicomdir)
        self.btn_savedir = QPushButton('Save directory \nto config file')
        self.btn_savedir.setFixedSize(150, 40)
        self.btn_savedir.setToolTip(Output.split_lines(setToolTips.saveDirButton()))
        self.btn_savedir.clicked.connect(self.save_cfg)

        hlay_upper = QHBoxLayout()
        hlay_upper.addWidget(self.btn_dicomdir)
        hlay_upper.addWidget(self.btn_savedir)
        hlay_upper.addStretch(1)
        self.HBoxUpperDcm2nii.addLayout(hlay_upper)

        # ====================    Create Content for Lists, i.e. input/output      ====================
        self.listboxInputDcm2nii = QGroupBox('Available subjects in working directory')
        self.listboxInput = QVBoxLayout(self.listboxInputDcm2nii)
        self.mInput = QListWidget()
        self.listboxInput.addWidget(self.mInput)

        self.mButtonToAvailable = QPushButton("<<")
        self.mBtnMoveToAvailable = QPushButton(">")
        self.mBtnMoveToSelected = QPushButton("<")
        self.mButtonToSelected = QPushButton(">>")
        self.mBtnUp = QPushButton("Up")
        self.mBtnDown = QPushButton("Down")

        self.listboxOutputDcm2nii = QGroupBox('Subjects to process')
        self.listboxOutput = QVBoxLayout(self.listboxOutputDcm2nii)
        self.mOutput = QListWidget()
        self.listboxOutput.addWidget(self.mOutput)

        # First column on the left side
        vlay = QVBoxLayout()
        vlay.addStretch()
        vlay.addWidget(self.mBtnMoveToAvailable)
        vlay.addWidget(self.mBtnMoveToSelected)
        vlay.addStretch()
        vlay.addWidget(self.mButtonToAvailable)
        vlay.addWidget(self.mButtonToSelected)
        vlay.addStretch()

        # Second column on the right side
        vlay2 = QVBoxLayout()
        vlay2.addStretch()
        vlay2.addWidget(self.mBtnUp)
        vlay2.addWidget(self.mBtnDown)
        vlay2.addStretch()

        # ====================    Lower part of GUI, i.e. Preferences/Start estimation      ====================
        self.btn_preferences = QPushButton("Preferences")
        self.btn_preferences.clicked.connect(self.settings_show)
        self.btn_run_dcm2niix = QPushButton("Run dcm2niix")
        self.btn_run_dcm2niix.setToolTip(setToolTips.run_dcm2niix())
        self.btn_run_dcm2niix.clicked.connect(self.start_converting)

        hlay_bottom = QHBoxLayout()
        hlay_bottom.addStretch(1)
        hlay_bottom.addWidget(self.btn_preferences)
        hlay_bottom.addWidget(self.btn_run_dcm2niix)
        hlay_bottom.addStretch()

        # ====================    Set all contents to general Layout     =======================
        self.mid_layout.addWidget(self.listboxInputDcm2nii)
        self.mid_layout.addLayout(vlay)
        self.mid_layout.addWidget(self.listboxOutputDcm2nii)
        self.mid_layout.addLayout(vlay2)

        self.tot_layout.addWidget(self.folderboxDcm2nii)
        self.tot_layout.addLayout(self.mid_layout)
        self.tot_layout.addLayout(hlay_bottom)

        try:
            self.mInput.clear()
            items = FileOperations.list_folders(inputdir=self.cfg['folders']['dicom'], prefix='', files2lookfor='')
            items = self.check_for_complete_input(list(items))
            self.add_available_subj(items)
        except FileExistsError:
            print('{} without any valid files/folders, continuing ...'.format(self.dicomdir))

        self.update_buttons_status()
        self.connections()

    def check_for_complete_input(self, item_list, modalities = ['CT', 'MRI']):
        """this function ensures, that only those subjects are displayed where MRI and CT data is available in the
        correct folders (surname_nameMRI or surname_nameCT)"""

        available_subjects = set([re.split(r'CT|MRI', x)[0] for x in list(item_list)])
        item_list_complete = []
        [item_list_complete.append(subj) for subj in list(available_subjects) if
         all([os.path.isdir(os.path.join(self.dicomdir, subj + y)) for y in modalities])]

        if len(available_subjects) != len(item_list_complete):
            incomplete = list(set(available_subjects) - set(item_list_complete))
            Output.msg_box(text="There is incomplete data or directories have unknown names. Please ensure the presence"
                                " of two folders (surname_nameCT) and (surname_nameMRI) for:"
                                "\n{}".format(''.join(' -> {}\n'.format(c) for c in incomplete)),
                           title="Incomplete data")
        return set(item_list_complete)

    def add_available_subj(self, items):
        """adds available subjects in the cwd to item list; PyQT5 dialog is opened if none available"""

        if len(items) == 0:
            buttonReply = QMessageBox.question(self, 'No files in the DICOM directory',
                                               'No subjects available in the CWD: {}. '
                                               'Do you want to change to different one?'.format(self.dicomdir),
                                               QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
            if buttonReply == QMessageBox.Yes:
                self.change_dicomdir()
        else:
            self.mInput.addItems(items)

    def change_dicomdir(self):
        """A new window appears in which the working directory can be set; besides, data is stored in the preferences
        file so that they will be loaded automatically next time"""

        self.dicomdir = FileOperations.set_wdir_in_config(self.cfg, foldername='dicom')
        self.label_dicomdir.setText('dicomDIR: {}'.format(self.dicomdir))
        self.cfg['folders']['dicom'] = self.dicomdir

        self.mInput.clear()
        items = FileOperations.list_folders(inputdir=self.cfg['folders']['dicom'], prefix='', files2lookfor='')
        self.add_available_subj(items)

    def save_cfg(self):
        """Function intended to save the DICOM directory once button is pressed"""

        self.cfg['folders']['dicom'] = self.dicomdir
        Configuration.save_config(self.cfg['folders']['rootdir'], self.cfg)
        Output.msg_box(text="Folder changed in configuration to {}".format(self.dicomdir), title="Changed folder")

    def settings_show(self):
        """Opens a new GUI in which the settings for the tansformation con be changed and saved to config file"""
        self.settingsGUI = GuiSettingsDCM2NII()
        self.settingsGUI.show()

    def start_converting(self):
        folderlist = []
        [folderlist.append(self.mOutput.item(x).text()) for x in range(self.mOutput.count())]
        print('in total, {} folders were selected'.format(len(folderlist)))

        if not folderlist:
            Output.msg_box(text="At least one folder with DICOM data needed!", title="No directory selected")
        else:
            preprocDCM2NII.PreprocessDCM(folderlist)

    # ====================    Actions when buttons are pressed      ====================
    @QtCore.pyqtSlot()
    def update_buttons_status(self):
        self.mBtnUp.setDisabled(not bool(self.mOutput.selectedItems()) or self.mOutput.currentRow() == 0)
        self.mBtnDown.setDisabled(not bool(self.mOutput.selectedItems()) or
                                  self.mOutput.currentRow() == (self.mOutput.count() - 1))
        self.mBtnMoveToAvailable.setDisabled(not bool(self.mInput.selectedItems()) or self.mOutput.currentRow() == 0)
        self.mBtnMoveToSelected.setDisabled(not bool(self.mOutput.selectedItems()))
        self.mButtonToAvailable.setDisabled(not bool(self.mOutput.selectedItems()))
        self.mButtonToSelected.setDisabled(not bool(self.mInput.selectedItems()))

    def connections(self):
        self.mInput.itemSelectionChanged.connect(self.update_buttons_status)
        self.mOutput.itemSelectionChanged.connect(self.update_buttons_status)
        self.mBtnMoveToAvailable.clicked.connect(self.on_mBtnMoveToAvailable_clicked)
        self.mBtnMoveToSelected.clicked.connect(self.on_mBtnMoveToSelected_clicked)
        self.mButtonToAvailable.clicked.connect(self.on_mButtonToAvailable_clicked)
        self.mButtonToSelected.clicked.connect(self.on_mButtonToSelected_clicked)
        self.mBtnUp.clicked.connect(self.on_mBtnUp_clicked)
        self.mBtnDown.clicked.connect(self.on_mBtnDown_clicked)

    @QtCore.pyqtSlot()
    def on_mBtnMoveToAvailable_clicked(self):
        self.mOutput.addItem(self.mInput.takeItem(self.mInput.currentRow()))

    @QtCore.pyqtSlot()
    def on_mBtnMoveToSelected_clicked(self):
        self.mInput.addItem(self.mOutput.takeItem(self.mOutput.currentRow()))

    @QtCore.pyqtSlot()
    def on_mButtonToAvailable_clicked(self):
        while self.mOutput.count() > 0:
            self.mInput.addItem(self.mOutput.takeItem(0))

    @QtCore.pyqtSlot()
    def on_mButtonToSelected_clicked(self):
        while self.mInput.count() > 0:
            self.mOutput.addItem(self.mInput.takeItem(0))

    @QtCore.pyqtSlot()
    def on_mBtnUp_clicked(self):
        row = self.mOutput.currentRow()
        currentItem = self.mOutput.takeItem(row)
        self.mOutput.insertItem(row - 1, currentItem)
        self.mOutput.setCurrentRow(row - 1)

    @QtCore.pyqtSlot()
    def on_mBtnDown_clicked(self):
        row = self.mOutput.currentRow()
        currentItem = self.mOutput.takeItem(row)
        self.mOutput.insertItem(row + 1, currentItem)
        self.mOutput.setCurrentRow(row + 1)

    def addSelectedItems(self, items):
        self.mOutput.addItems(items)
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)
Beispiel #29
0
class SettingsDialog(QDialog):
    worker = None
    config = None
    configfile = None

    saved = QtCore.pyqtSignal()

    def __init__(self, parent, worker, config, configfile):
        QDialog.__init__(self, parent)

        self.worker = worker
        self.config = config
        self.configfile = configfile
        self.setStyleSheet("QGroupBox { font-weight: bold; } ")
        self.setWindowTitle('Settings')
        layout = QGridLayout()

        # Categories
        self.category_list = QListWidget()
        category_media = QListWidgetItem(getIcon('media-playback-start'), 'Media', self.category_list)
        category_sync = QListWidgetItem(getIcon('view-refresh'), 'Sync', self.category_list)
        category_ui = QListWidgetItem(getIcon('window-new'), 'User Interface', self.category_list)
        category_theme = QListWidgetItem(getIcon('applications-graphics'), 'Theme', self.category_list)
        self.category_list.setSelectionMode(QAbstractItemView.SingleSelection)
        self.category_list.setCurrentRow(0)
        self.category_list.setMaximumWidth(self.category_list.sizeHintForColumn(0) + 15)
        self.category_list.setFocus()
        self.category_list.currentItemChanged.connect(self.s_switch_page)

        # Media tab
        page_media = QWidget()
        page_media_layout = QVBoxLayout()
        page_media_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Media settings
        g_media = QGroupBox('Media settings')
        g_media.setFlat(True)
        g_media_layout = QFormLayout()
        self.tracker_enabled = QCheckBox()
        self.tracker_enabled.toggled.connect(self.tracker_type_change)

        self.tracker_type = QComboBox()
        for (n, label) in utils.available_trackers:
            self.tracker_type.addItem(label, n)
        self.tracker_type.currentIndexChanged.connect(self.tracker_type_change)

        self.tracker_interval = QSpinBox()
        self.tracker_interval.setRange(5, 1000)
        self.tracker_interval.setMaximumWidth(60)
        self.tracker_process = QLineEdit()
        self.tracker_update_wait = QSpinBox()
        self.tracker_update_wait.setRange(0, 1000)
        self.tracker_update_wait.setMaximumWidth(60)
        self.tracker_update_close = QCheckBox()
        self.tracker_update_prompt = QCheckBox()
        self.tracker_not_found_prompt = QCheckBox()

        g_media_layout.addRow('Enable tracker', self.tracker_enabled)
        g_media_layout.addRow('Tracker type', self.tracker_type)
        g_media_layout.addRow('Tracker interval (seconds)', self.tracker_interval)
        g_media_layout.addRow('Process name (regex)', self.tracker_process)
        g_media_layout.addRow('Wait before updating (seconds)', self.tracker_update_wait)
        g_media_layout.addRow('Wait until the player is closed', self.tracker_update_close)
        g_media_layout.addRow('Ask before updating', self.tracker_update_prompt)
        g_media_layout.addRow('Ask to add new shows', self.tracker_not_found_prompt)

        g_media.setLayout(g_media_layout)

        # Group: Plex settings
        g_plex = QGroupBox('Plex Media Server')
        g_plex.setFlat(True)
        self.plex_host = QLineEdit()
        self.plex_port = QLineEdit()
        self.plex_user = QLineEdit()
        self.plex_passw = QLineEdit()
        self.plex_passw.setEchoMode(QLineEdit.Password)
        self.plex_obey_wait = QCheckBox()

        g_plex_layout = QGridLayout()
        g_plex_layout.addWidget(QLabel('Host and Port'),                   0, 0, 1, 1)
        g_plex_layout.addWidget(self.plex_host,                            0, 1, 1, 1)
        g_plex_layout.addWidget(self.plex_port,                            0, 2, 1, 2)
        g_plex_layout.addWidget(QLabel('Use "wait before updating" time'), 1, 0, 1, 1)
        g_plex_layout.addWidget(self.plex_obey_wait,                       1, 2, 1, 1)
        g_plex_layout.addWidget(QLabel('myPlex login (claimed server)'),   2, 0, 1, 1)
        g_plex_layout.addWidget(self.plex_user,                            2, 1, 1, 1)
        g_plex_layout.addWidget(self.plex_passw,                           2, 2, 1, 2)

        g_plex.setLayout(g_plex_layout)

        # Group: Library
        g_playnext = QGroupBox('Library')
        g_playnext.setFlat(True)
        self.player = QLineEdit()
        self.player_browse = QPushButton('Browse...')
        self.player_browse.clicked.connect(self.s_player_browse)
        lbl_searchdirs = QLabel('Media directories')
        lbl_searchdirs.setAlignment(QtCore.Qt.AlignTop)
        self.searchdirs = QListWidget()
        self.searchdirs_add = QPushButton('Add...')
        self.searchdirs_add.clicked.connect(self.s_searchdirs_add)
        self.searchdirs_remove = QPushButton('Remove')
        self.searchdirs_remove.clicked.connect(self.s_searchdirs_remove)
        self.searchdirs_buttons = QVBoxLayout()
        self.searchdirs_buttons.setAlignment(QtCore.Qt.AlignTop)
        self.searchdirs_buttons.addWidget(self.searchdirs_add)
        self.searchdirs_buttons.addWidget(self.searchdirs_remove)
        self.searchdirs_buttons.addWidget(QSplitter())
        self.library_autoscan = QCheckBox()
        self.scan_whole_list = QCheckBox()
        self.library_full_path = QCheckBox()


        g_playnext_layout = QGridLayout()
        g_playnext_layout.addWidget(QLabel('Player'),                    0, 0, 1, 1)
        g_playnext_layout.addWidget(self.player,                         0, 1, 1, 1)
        g_playnext_layout.addWidget(self.player_browse,                  0, 2, 1, 1)
        g_playnext_layout.addWidget(lbl_searchdirs,                      1, 0, 1, 1)
        g_playnext_layout.addWidget(self.searchdirs,                     1, 1, 1, 1)
        g_playnext_layout.addLayout(self.searchdirs_buttons,             1, 2, 1, 1)
        g_playnext_layout.addWidget(QLabel('Rescan Library at startup'), 2, 0, 1, 2)
        g_playnext_layout.addWidget(self.library_autoscan,               2, 2, 1, 1)
        g_playnext_layout.addWidget(QLabel('Scan through whole list'),   3, 0, 1, 2)
        g_playnext_layout.addWidget(self.scan_whole_list,                3, 2, 1, 1)
        g_playnext_layout.addWidget(QLabel('Take subdirectory name into account'), 4, 0, 1, 2)
        g_playnext_layout.addWidget(self.library_full_path,              4, 2, 1, 1)

        g_playnext.setLayout(g_playnext_layout)

        # Media form
        page_media_layout.addWidget(g_media)
        page_media_layout.addWidget(g_plex)
        page_media_layout.addWidget(g_playnext)
        page_media.setLayout(page_media_layout)

        # Sync tab
        page_sync = QWidget()
        page_sync_layout = QVBoxLayout()
        page_sync_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Autoretrieve
        g_autoretrieve = QGroupBox('Autoretrieve')
        g_autoretrieve.setFlat(True)
        self.autoretrieve_off = QRadioButton('Disabled')
        self.autoretrieve_always = QRadioButton('Always at start')
        self.autoretrieve_days = QRadioButton('After n days')
        self.autoretrieve_days.toggled.connect(self.s_autoretrieve_days)
        self.autoretrieve_days_n = QSpinBox()
        self.autoretrieve_days_n.setRange(1, 100)
        g_autoretrieve_layout = QGridLayout()
        g_autoretrieve_layout.setColumnStretch(0, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_off,    0, 0, 1, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_always, 1, 0, 1, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_days,   2, 0, 1, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_days_n, 2, 1, 1, 1)
        g_autoretrieve.setLayout(g_autoretrieve_layout)

        # Group: Autosend
        g_autosend = QGroupBox('Autosend')
        g_autosend.setFlat(True)
        self.autosend_off = QRadioButton('Disabled')
        self.autosend_always = QRadioButton('Immediately after every change')
        self.autosend_minutes = QRadioButton('After n minutes')
        self.autosend_minutes.toggled.connect(self.s_autosend_minutes)
        self.autosend_minutes_n = QSpinBox()
        self.autosend_minutes_n.setRange(1, 1000)
        self.autosend_size = QRadioButton('After the queue reaches n items')
        self.autosend_size.toggled.connect(self.s_autosend_size)
        self.autosend_size_n = QSpinBox()
        self.autosend_size_n.setRange(2, 20)
        self.autosend_at_exit = QCheckBox('At exit')
        g_autosend_layout = QGridLayout()
        g_autosend_layout.setColumnStretch(0, 1)
        g_autosend_layout.addWidget(self.autosend_off,      0, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_always,   1, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_minutes,    2, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_minutes_n,  2, 1, 1, 1)
        g_autosend_layout.addWidget(self.autosend_size,     3, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_size_n,   3, 1, 1, 1)
        g_autosend_layout.addWidget(self.autosend_at_exit,  4, 0, 1, 1)
        g_autosend.setLayout(g_autosend_layout)

        # Group: Extra
        g_extra = QGroupBox('Additional options')
        g_extra.setFlat(True)
        self.auto_status_change = QCheckBox('Change status automatically')
        self.auto_status_change.toggled.connect(self.s_auto_status_change)
        self.auto_status_change_if_scored = QCheckBox('Change status automatically only if scored')
        self.auto_date_change = QCheckBox('Change start and finish dates automatically')
        g_extra_layout = QVBoxLayout()
        g_extra_layout.addWidget(self.auto_status_change)
        g_extra_layout.addWidget(self.auto_status_change_if_scored)
        g_extra_layout.addWidget(self.auto_date_change)
        g_extra.setLayout(g_extra_layout)

        # Sync layout
        page_sync_layout.addWidget(g_autoretrieve)
        page_sync_layout.addWidget(g_autosend)
        page_sync_layout.addWidget(g_extra)
        page_sync.setLayout(page_sync_layout)

        # UI tab
        page_ui = QWidget()
        page_ui_layout = QFormLayout()
        page_ui_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Icon
        g_icon = QGroupBox('Notification Icon')
        g_icon.setFlat(True)
        self.tray_icon = QCheckBox('Show tray icon')
        self.tray_icon.toggled.connect(self.s_tray_icon)
        self.close_to_tray = QCheckBox('Close to tray')
        self.start_in_tray = QCheckBox('Start minimized to tray')
        self.tray_api_icon = QCheckBox('Use API icon as tray icon')
        self.notifications = QCheckBox('Show notification when tracker detects new media')
        g_icon_layout = QVBoxLayout()
        g_icon_layout.addWidget(self.tray_icon)
        g_icon_layout.addWidget(self.close_to_tray)
        g_icon_layout.addWidget(self.start_in_tray)
        g_icon_layout.addWidget(self.tray_api_icon)
        g_icon_layout.addWidget(self.notifications)
        g_icon.setLayout(g_icon_layout)

        # Group: Window
        g_window = QGroupBox('Window')
        g_window.setFlat(True)
        self.remember_geometry = QCheckBox('Remember window size and position')
        self.remember_columns = QCheckBox('Remember column layouts and widths')
        self.columns_per_api = QCheckBox('Use different visible columns per API')
        g_window_layout = QVBoxLayout()
        g_window_layout.addWidget(self.remember_geometry)
        g_window_layout.addWidget(self.remember_columns)
        g_window_layout.addWidget(self.columns_per_api)
        g_window.setLayout(g_window_layout)

        # Group: Lists
        g_lists = QGroupBox('Lists')
        g_lists.setFlat(True)
        self.filter_bar_position = QComboBox()
        filter_bar_positions = [(FilterBar.PositionHidden,     'Hidden'),
                                (FilterBar.PositionAboveLists, 'Above lists'),
                                (FilterBar.PositionBelowLists, 'Below lists')]
        for (n, label) in filter_bar_positions:
            self.filter_bar_position.addItem(label, n)
        self.inline_edit = QCheckBox('Enable in-line editing')
        g_lists_layout = QFormLayout()
        g_lists_layout.addRow('Filter bar position:', self.filter_bar_position)
        g_lists_layout.addRow(self.inline_edit)
        g_lists.setLayout(g_lists_layout)

        # UI layout
        page_ui_layout.addWidget(g_icon)
        page_ui_layout.addWidget(g_window)
        page_ui_layout.addWidget(g_lists)
        page_ui.setLayout(page_ui_layout)

        # Theming tab
        page_theme = QWidget()
        page_theme_layout = QFormLayout()
        page_theme_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Episode Bar
        g_ep_bar = QGroupBox('Episode Bar')
        g_ep_bar.setFlat(True)
        self.ep_bar_style = QComboBox()
        ep_bar_styles = [(ShowsTableDelegate.BarStyleBasic,  'Basic'),
                         (ShowsTableDelegate.BarStyle04,     'Trackma'),
                         (ShowsTableDelegate.BarStyleHybrid, 'Hybrid')]
        for (n, label) in ep_bar_styles:
            self.ep_bar_style.addItem(label, n)
        self.ep_bar_style.currentIndexChanged.connect(self.s_ep_bar_style)
        self.ep_bar_text = QCheckBox('Show text label')
        g_ep_bar_layout = QFormLayout()
        g_ep_bar_layout.addRow('Style:', self.ep_bar_style)
        g_ep_bar_layout.addRow(self.ep_bar_text)
        g_ep_bar.setLayout(g_ep_bar_layout)

        # Group: Colour scheme
        g_scheme = QGroupBox('Color Scheme')
        g_scheme.setFlat(True)
        col_tabs = [('rows',     '&Row highlights'),
                    ('progress', '&Progress widget')]
        self.colors = {}
        self.colors['rows'] = [('is_playing',  'Playing'),
                               ('is_queued',   'Queued'),
                               ('new_episode', 'New Episode'),
                               ('is_airing',   'Airing'),
                               ('not_aired',   'Unaired')]
        self.colors['progress'] = [('progress_bg',       'Background'),
                                   ('progress_fg',       'Watched bar'),
                                   ('progress_sub_bg',   'Aired episodes'),
                                   ('progress_sub_fg',   'Stored episodes'),
                                   ('progress_complete', 'Complete')]
        self.color_buttons = []
        self.syscolor_buttons = []
        g_scheme_layout = QGridLayout()
        tw_scheme = QTabWidget()
        for (key, tab_title) in col_tabs:
            page = QFrame()
            page_layout = QGridLayout()
            col = 0
            # Generate widgets from the keys and values
            for (key, label) in self.colors[key]:
                self.color_buttons.append(QPushButton())
                # self.color_buttons[-1].setStyleSheet('background-color: ' + getColor(self.config['colors'][key]).name())
                self.color_buttons[-1].setFocusPolicy(QtCore.Qt.NoFocus)
                self.color_buttons[-1].clicked.connect(self.s_color_picker(key, False))
                self.syscolor_buttons.append(QPushButton('System Colors'))
                self.syscolor_buttons[-1].clicked.connect(self.s_color_picker(key, True))
                page_layout.addWidget(QLabel(label),             col, 0, 1, 1)
                page_layout.addWidget(self.color_buttons[-1],    col, 1, 1, 1)
                page_layout.addWidget(self.syscolor_buttons[-1], col, 2, 1, 1)
                col += 1
            page.setLayout(page_layout)
            tw_scheme.addTab(page, tab_title)
        g_scheme_layout.addWidget(tw_scheme)
        g_scheme.setLayout(g_scheme_layout)

        # UI layout
        page_theme_layout.addWidget(g_ep_bar)
        page_theme_layout.addWidget(g_scheme)
        page_theme.setLayout(page_theme_layout)

        # Content
        self.contents = QStackedWidget()
        self.contents.addWidget(page_media)
        self.contents.addWidget(page_sync)
        self.contents.addWidget(page_ui)
        self.contents.addWidget(page_theme)
        if pyqt_version is not 5:
            self.contents.layout().setMargin(0)

        # Bottom buttons
        bottombox = QDialogButtonBox(
            QDialogButtonBox.Ok
            | QDialogButtonBox.Apply
            | QDialogButtonBox.Cancel
        )
        bottombox.accepted.connect(self.s_save)
        bottombox.button(QDialogButtonBox.Apply).clicked.connect(self._save)
        bottombox.rejected.connect(self.reject)

        # Main layout finish
        layout.addWidget(self.category_list,  0, 0, 1, 1)
        layout.addWidget(self.contents,       0, 1, 1, 1)
        layout.addWidget(bottombox,           1, 0, 1, 2)
        layout.setColumnStretch(1, 1)

        self._load()
        self.update_colors()

        self.setLayout(layout)

    def _add_dir(self, path):
        self.searchdirs.addItem(path)

    def _load(self):
        engine = self.worker.engine
        tracker_type = self.tracker_type.findData(engine.get_config('tracker_type'))
        autoretrieve = engine.get_config('autoretrieve')
        autosend = engine.get_config('autosend')

        self.tracker_enabled.setChecked(engine.get_config('tracker_enabled'))
        self.tracker_type.setCurrentIndex(max(0, tracker_type))
        self.tracker_interval.setValue(engine.get_config('tracker_interval'))
        self.tracker_process.setText(engine.get_config('tracker_process'))
        self.tracker_update_wait.setValue(engine.get_config('tracker_update_wait_s'))
        self.tracker_update_close.setChecked(engine.get_config('tracker_update_close'))
        self.tracker_update_prompt.setChecked(engine.get_config('tracker_update_prompt'))
        self.tracker_not_found_prompt.setChecked(engine.get_config('tracker_not_found_prompt'))

        self.player.setText(engine.get_config('player'))
        self.library_autoscan.setChecked(engine.get_config('library_autoscan'))
        self.scan_whole_list.setChecked(engine.get_config('scan_whole_list'))
        self.library_full_path.setChecked(engine.get_config('library_full_path'))
        self.plex_host.setText(engine.get_config('plex_host'))
        self.plex_port.setText(engine.get_config('plex_port'))
        self.plex_obey_wait.setChecked(engine.get_config('plex_obey_update_wait_s'))
        self.plex_user.setText(engine.get_config('plex_user'))
        self.plex_passw.setText(engine.get_config('plex_passwd'))

        for path in engine.get_config('searchdir'):
            self._add_dir(path)

        if autoretrieve == 'always':
            self.autoretrieve_always.setChecked(True)
        elif autoretrieve == 'days':
            self.autoretrieve_days.setChecked(True)
        else:
            self.autoretrieve_off.setChecked(True)

        self.autoretrieve_days_n.setValue(engine.get_config('autoretrieve_days'))

        if autosend == 'always':
            self.autosend_always.setChecked(True)
        elif autosend in ('minutes', 'hours'):
            self.autosend_minutes.setChecked(True)
        elif autosend == 'size':
            self.autosend_size.setChecked(True)
        else:
            self.autosend_off.setChecked(True)

        self.autosend_minutes_n.setValue(engine.get_config('autosend_minutes'))
        self.autosend_size_n.setValue(engine.get_config('autosend_size'))

        self.autosend_at_exit.setChecked(engine.get_config('autosend_at_exit'))
        self.auto_status_change.setChecked(engine.get_config('auto_status_change'))
        self.auto_status_change_if_scored.setChecked(engine.get_config('auto_status_change_if_scored'))
        self.auto_date_change.setChecked(engine.get_config('auto_date_change'))

        self.tray_icon.setChecked(self.config['show_tray'])
        self.close_to_tray.setChecked(self.config['close_to_tray'])
        self.start_in_tray.setChecked(self.config['start_in_tray'])
        self.tray_api_icon.setChecked(self.config['tray_api_icon'])
        self.notifications.setChecked(self.config['notifications'])
        self.remember_geometry.setChecked(self.config['remember_geometry'])
        self.remember_columns.setChecked(self.config['remember_columns'])
        self.columns_per_api.setChecked(self.config['columns_per_api'])
        self.filter_bar_position.setCurrentIndex(self.filter_bar_position.findData(self.config['filter_bar_position']))
        self.inline_edit.setChecked(self.config['inline_edit'])

        self.ep_bar_style.setCurrentIndex(self.ep_bar_style.findData(self.config['episodebar_style']))
        self.ep_bar_text.setChecked(self.config['episodebar_text'])

        self.autoretrieve_days_n.setEnabled(self.autoretrieve_days.isChecked())
        self.autosend_minutes_n.setEnabled(self.autosend_minutes.isChecked())
        self.autosend_size_n.setEnabled(self.autosend_size.isChecked())
        self.close_to_tray.setEnabled(self.tray_icon.isChecked())
        self.start_in_tray.setEnabled(self.tray_icon.isChecked())
        self.notifications.setEnabled(self.tray_icon.isChecked())

        self.color_values = self.config['colors'].copy()

        self.tracker_type_change(None)

    def _save(self):
        engine = self.worker.engine

        engine.set_config('tracker_enabled',       self.tracker_enabled.isChecked())
        engine.set_config('tracker_type',          self.tracker_type.itemData(self.tracker_type.currentIndex()))
        engine.set_config('tracker_interval',      self.tracker_interval.value())
        engine.set_config('tracker_process',       str(self.tracker_process.text()))
        engine.set_config('tracker_update_wait_s', self.tracker_update_wait.value())
        engine.set_config('tracker_update_close',  self.tracker_update_close.isChecked())
        engine.set_config('tracker_update_prompt', self.tracker_update_prompt.isChecked())
        engine.set_config('tracker_not_found_prompt', self.tracker_not_found_prompt.isChecked())

        engine.set_config('player',            self.player.text())
        engine.set_config('library_autoscan',  self.library_autoscan.isChecked())
        engine.set_config('scan_whole_list', self.scan_whole_list.isChecked())
        engine.set_config('library_full_path', self.library_full_path.isChecked())
        engine.set_config('plex_host',         self.plex_host.text())
        engine.set_config('plex_port',         self.plex_port.text())
        engine.set_config('plex_obey_update_wait_s', self.plex_obey_wait.isChecked())
        engine.set_config('plex_user',         self.plex_user.text())
        engine.set_config('plex_passwd',       self.plex_passw.text())

        engine.set_config('searchdir',         [self.searchdirs.item(i).text() for i in range(self.searchdirs.count())])

        if self.autoretrieve_always.isChecked():
            engine.set_config('autoretrieve', 'always')
        elif self.autoretrieve_days.isChecked():
            engine.set_config('autoretrieve', 'days')
        else:
            engine.set_config('autoretrieve', 'off')

        engine.set_config('autoretrieve_days',   self.autoretrieve_days_n.value())

        if self.autosend_always.isChecked():
            engine.set_config('autosend', 'always')
        elif self.autosend_minutes.isChecked():
            engine.set_config('autosend', 'minutes')
        elif self.autosend_size.isChecked():
            engine.set_config('autosend', 'size')
        else:
            engine.set_config('autosend', 'off')

        engine.set_config('autosend_minutes', self.autosend_minutes_n.value())
        engine.set_config('autosend_size',  self.autosend_size_n.value())

        engine.set_config('autosend_at_exit',   self.autosend_at_exit.isChecked())
        engine.set_config('auto_status_change', self.auto_status_change.isChecked())
        engine.set_config('auto_status_change_if_scored', self.auto_status_change_if_scored.isChecked())
        engine.set_config('auto_date_change',   self.auto_date_change.isChecked())

        engine.save_config()

        self.config['show_tray'] = self.tray_icon.isChecked()
        self.config['close_to_tray'] = self.close_to_tray.isChecked()
        self.config['start_in_tray'] = self.start_in_tray.isChecked()
        self.config['tray_api_icon'] = self.tray_api_icon.isChecked()
        self.config['notifications'] = self.notifications.isChecked()
        self.config['remember_geometry'] = self.remember_geometry.isChecked()
        self.config['remember_columns'] = self.remember_columns.isChecked()
        self.config['columns_per_api'] = self.columns_per_api.isChecked()
        self.config['filter_bar_position'] = self.filter_bar_position.itemData(self.filter_bar_position.currentIndex())
        self.config['inline_edit'] = self.inline_edit.isChecked()

        self.config['episodebar_style'] = self.ep_bar_style.itemData(self.ep_bar_style.currentIndex())
        self.config['episodebar_text'] = self.ep_bar_text.isChecked()

        self.config['colors'] = self.color_values

        utils.save_config(self.config, self.configfile)

        self.saved.emit()

    def s_save(self):
        self._save()
        self.accept()

    def tracker_type_change(self, checked):
        if self.tracker_enabled.isChecked():
            self.tracker_interval.setEnabled(True)
            self.tracker_update_wait.setEnabled(True)
            self.tracker_type.setEnabled(True)
            if self.tracker_type.itemData(self.tracker_type.currentIndex()) == 'plex':
                self.plex_host.setEnabled(True)
                self.plex_port.setEnabled(True)
                self.plex_obey_wait.setEnabled(True)
                self.plex_user.setEnabled(True)
                self.plex_passw.setEnabled(True)
                self.tracker_process.setEnabled(False)
            else:
                self.tracker_process.setEnabled(True)
                self.plex_host.setEnabled(False)
                self.plex_port.setEnabled(False)
                self.plex_user.setEnabled(False)
                self.plex_passw.setEnabled(False)
                self.plex_obey_wait.setEnabled(False)
        else:
            self.tracker_type.setEnabled(False)
            self.plex_host.setEnabled(False)
            self.plex_port.setEnabled(False)
            self.plex_user.setEnabled(False)
            self.plex_passw.setEnabled(False)
            self.plex_obey_wait.setEnabled(False)
            self.tracker_process.setEnabled(False)
            self.tracker_interval.setEnabled(False)
            self.tracker_update_wait.setEnabled(False)

    def s_autoretrieve_days(self, checked):
        self.autoretrieve_days_n.setEnabled(checked)

    def s_autosend_minutes(self, checked):
        self.autosend_minutes_n.setEnabled(checked)

    def s_autosend_size(self, checked):
        self.autosend_size_n.setEnabled(checked)

    def s_tray_icon(self, checked):
        self.close_to_tray.setEnabled(checked)
        self.start_in_tray.setEnabled(checked)
        self.tray_api_icon.setEnabled(checked)
        self.notifications.setEnabled(checked)

    def s_ep_bar_style(self, index):
        if self.ep_bar_style.itemData(index) == ShowsTableDelegate.BarStyle04:
            self.ep_bar_text.setEnabled(False)
        else:
            self.ep_bar_text.setEnabled(True)

    def s_auto_status_change(self, checked):
        self.auto_status_change_if_scored.setEnabled(checked)

    def s_player_browse(self):
        if pyqt_version is 5:
            self.player.setText(QFileDialog.getOpenFileName(caption='Choose player executable')[0])
        else:
            self.player.setText(QFileDialog.getOpenFileName(caption='Choose player executable'))

    def s_searchdirs_add(self):
        self._add_dir(QFileDialog.getExistingDirectory(caption='Choose media directory'))

    def s_searchdirs_remove(self):
        row = self.searchdirs.currentRow()
        if row != -1:
            self.searchdirs.takeItem(row)

    def s_switch_page(self, new, old):
        if not new:
            new = old

        self.contents.setCurrentIndex(self.category_list.row(new))

    def s_color_picker(self, key, system):
        return lambda: self.color_picker(key, system)

    def color_picker(self, key, system):
        if system is True:
            current = self.color_values[key]
            result = ThemedColorPicker.do()
            if result is not None and result is not current:
                self.color_values[key] = result
                self.update_colors()
        else:
            current = getColor(self.color_values[key])
            result = QColorDialog.getColor(current)
            if result.isValid() and result is not current:
                self.color_values[key] = str(result.name())
                self.update_colors()

    def update_colors(self):
        for ((key, label), color) in zip(self.colors['rows']+self.colors['progress'], self.color_buttons):
            color.setStyleSheet('background-color: ' + getColor(self.color_values[key]).name())
Beispiel #30
0
class GlyphSetTab(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.name = self.tr("Glyph sets")

        self.defaultGlyphSetBox = QCheckBox(
            self.tr("Default glyph set:"), self)
        self.defaultGlyphSetDrop = QComboBox(self)
        self.defaultGlyphSetBox.toggled.connect(self.toggleGlyphSetDrop)

        self.glyphSetList = QListWidget(self)
        self.glyphSetList.setSortingEnabled(True)
        self.glyphSetContents = QPlainTextEdit(self)
        self.glyphSetList.currentItemChanged.connect(
            self.updateGlyphSetContents)
        self.glyphSetList.itemChanged.connect(self.renameGlyphSet)
        self._cachedName = None
        splitter = QSplitter(self)
        splitter.addWidget(self.glyphSetList)
        splitter.addWidget(self.glyphSetContents)
        self.addGlyphSetButton = QPushButton("+", self)
        self.addGlyphSetButton.clicked.connect(lambda: self.addGlyphSet())
        self.removeGlyphSetButton = QPushButton("−", self)
        self.removeGlyphSetButton.clicked.connect(self.removeGlyphSet)
        self.importButton = QPushButton(self.tr("Import"), self)
        importMenu = QMenu(self)
        importMenu.addAction(
            self.tr("Import from current font"), self.importFromCurrentFont)
        self.importButton.setMenu(importMenu)
        self.glyphListBox = QCheckBox(self.tr("Glyph list path:"), self)
        self.glyphListEdit = QLineEdit(self)
        self.glyphListEdit.setReadOnly(True)
        self.glyphListButton = QPushButton(self.tr("Browse…"), self)
        self.glyphListButton.clicked.connect(self.getGlyphList)
        # TODO: find correct solution for this and maybe make a widget w
        # setSizesToText()
        # http://stackoverflow.com/a/19502467/2037879
        textWidth = self.glyphListButton.fontMetrics().boundingRect(
            self.glyphListButton.text()).width() + 16
        self.glyphListButton.setMaximumWidth(textWidth)
        self.glyphListBox.toggled.connect(self.glyphListEdit.setEnabled)
        self.glyphListBox.toggled.connect(self.glyphListButton.setEnabled)

        firstLayout = QGridLayout()
        l = 0
        firstLayout.addWidget(self.defaultGlyphSetBox, l, 0, 1, 2)
        firstLayout.addWidget(self.defaultGlyphSetDrop, l, 3, 1, 3)
        l += 1
        firstLayout.addWidget(splitter, l, 0, 1, 6)
        l += 1
        firstLayout.addWidget(self.addGlyphSetButton, l, 0)
        firstLayout.addWidget(self.removeGlyphSetButton, l, 1)
        firstLayout.addWidget(self.importButton, l, 2)
        secondLayout = QHBoxLayout()
        secondLayout.addWidget(self.glyphListBox, 0)
        secondLayout.addWidget(self.glyphListEdit, 1)
        secondLayout.addWidget(self.glyphListButton, 2)
        mainLayout = QVBoxLayout(self)
        mainLayout.addLayout(firstLayout)
        mainLayout.addLayout(secondLayout)
        self.setLayout(mainLayout)

        self.readSettings()

    def addGlyphSet(self, glyphNames=[], glyphSetName=None):
        if glyphSetName is None:
            glyphSetName = self.tr("New glyph set")
        if glyphSetName in self.glyphSets:
            index = 1
            while "%s %d" % (glyphSetName, index) in self.glyphSets:
                index += 1
            glyphSetName = "%s %d" % (glyphSetName, index)
        self.glyphSets[glyphSetName] = glyphNames
        item = QListWidgetItem(glyphSetName, self.glyphSetList)
        item.setFlags(item.flags() | Qt.ItemIsEditable)
        self.glyphSetList.setCurrentItem(item)
        self.glyphSetList.editItem(item)
        self.removeGlyphSetButton.setEnabled(True)

    def removeGlyphSet(self):
        i = self.glyphSetList.currentRow()
        text = self.glyphSetList.takeItem(i).text()
        del self.glyphSets[text]
        if self.glyphSetList.count() < 2:
            self.removeGlyphSetButton.setEnabled(False)

    def renameGlyphSet(self):
        newKey = self.glyphSetList.currentItem()
        if newKey is None:
            return
        newKey = newKey.text()
        self.glyphSets[newKey] = self.glyphSets[self._cachedName]
        del self.glyphSets[self._cachedName]

    def importFromCurrentFont(self):
        font = QApplication.instance().currentFont()
        name = "%s %s" % (font.info.familyName, font.info.styleName)
        self.addGlyphSet(font.glyphOrder, name)

    def toggleGlyphSetDrop(self):
        sender = self.sender()
        self.defaultGlyphSetDrop.setEnabled(sender.isChecked())

    def updateGlyphSetContents(self, current, previous):
        # store content of the textEdit in the glyphSet dict
        if previous is not None:
            glyphNames = self.glyphSetContents.toPlainText().split()
            self.glyphSets[previous.text()] = glyphNames
        # now update the text edit to current glyphSet
        glyphSetName = current.text()
        text = " ".join(self.glyphSets[glyphSetName])
        self.glyphSetContents.setPlainText(text)
        # cache current name for renames
        self._cachedName = glyphSetName

    def getGlyphList(self):
        fileFormats = (
            self.tr("Text file {}").format("(*.txt)"),
            self.tr("All files {}").format("(*.*)"),
        )
        path, _ = QFileDialog.getOpenFileName(
            self, self.tr("Open File"), '', ";;".join(fileFormats)
        )
        if path:
            self.glyphListEdit.setText(path)

    def readSettings(self):
        defaultGlyphSet = settings.defaultGlyphSet()
        self.defaultGlyphSetBox.setChecked(len(defaultGlyphSet))

        self.glyphSets = settings.readGlyphSets()
        self.defaultGlyphSetDrop.clear()
        self.defaultGlyphSetDrop.addItems(self.glyphSets.keys())

        self.glyphSetList.clear()
        glyphSetNames = self.glyphSets.keys()
        # Normally we should be enforcing this rather decently in the interface
        # already
        if glyphSetNames:
            for glyphSetName in glyphSetNames:
                item = QListWidgetItem(glyphSetName, self.glyphSetList)
                item.setFlags(item.flags() | Qt.ItemIsEditable)
            self.glyphSetList.setCurrentRow(0)
        self.removeGlyphSetButton.setEnabled(len(self.glyphSets) > 1)

        glyphListPath = settings.glyphListPath()
        self.glyphListBox.setChecked(bool(glyphListPath))
        self.glyphListEdit.setEnabled(bool(glyphListPath))
        self.glyphListEdit.setText(glyphListPath)
        self.glyphListButton.setEnabled(bool(glyphListPath))

    def writeSettings(self):
        # store content of the textEdit in the glyphSet dict
        glyphNames = self.glyphSetContents.toPlainText().split()
        currentGlyphSet = self.glyphSetList.currentItem().text()
        self.glyphSets[currentGlyphSet] = glyphNames

        settings.writeGlyphSets(self.glyphSets)
        if not self.defaultGlyphSetBox.isChecked():
            settings.setDefaultGlyphSet(None)
        else:
            defaultGlyphSet = self.defaultGlyphSetDrop.currentText()
            settings.setDefaultGlyphSet(defaultGlyphSet)
        if not self.glyphListBox.isChecked():
            settings.setGlyphListPath(None)
        else:
            glyphListPath = self.glyphListEdit.text()
            if glyphListPath:
                settings.setGlyphListPath(glyphListPath)
                QApplication.instance().loadGlyphList()
Beispiel #31
0
class PresetsDialog(QDialog):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.resize(500, 400)
        self.setMaximumSize(self.size())
        self.setMinimumSize(self.size())
        self.setModal(True)
        self.setLayout(QGridLayout())

        # TODO: natural sorting (QStringListModel + QListView + ProxyModel)
        self.presetsList = QListWidget(self)
        self.presetsList.setAlternatingRowColors(True)
        self.presetsList.setFocusPolicy(Qt.NoFocus)
        self.presetsList.setSortingEnabled(True)
        self.presetsList.setSelectionMode(QListWidget.ExtendedSelection)
        self.presetsList.itemSelectionChanged.connect(self.__selection_changed)
        self.presetsList.itemDoubleClicked.connect(self.__edit_preset)
        self.layout().addWidget(self.presetsList, 0, 0)

        # Preset buttons
        self.presetsButtons = QWidget(self)
        self.presetsButtons.setLayout(QVBoxLayout())
        self.presetsButtons.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().addWidget(self.presetsButtons, 0, 1)
        self.layout().setAlignment(self.presetsButtons, Qt.AlignTop)

        self.addPresetButton = QPushButton(self.presetsButtons)
        self.addPresetButton.clicked.connect(self.__add_preset)
        self.presetsButtons.layout().addWidget(self.addPresetButton)

        self.renamePresetButton = QPushButton(self.presetsButtons)
        self.renamePresetButton.clicked.connect(self.__rename_preset)
        self.presetsButtons.layout().addWidget(self.renamePresetButton)

        self.editPresetButton = QPushButton(self.presetsButtons)
        self.editPresetButton.clicked.connect(self.__edit_preset)
        self.presetsButtons.layout().addWidget(self.editPresetButton)

        self.removePresetButton = QPushButton(self.presetsButtons)
        self.removePresetButton.clicked.connect(self.__remove_preset)
        self.presetsButtons.layout().addWidget(self.removePresetButton)

        self.cueFromSelectedButton = QPushButton(self.presetsButtons)
        self.cueFromSelectedButton.clicked.connect(self.__cue_from_selected)
        self.presetsButtons.layout().addWidget(self.cueFromSelectedButton)

        self.loadOnSelectedButton = QPushButton(self.presetsButtons)
        self.loadOnSelectedButton.clicked.connect(self.__load_on_selected)
        self.presetsButtons.layout().addWidget(self.loadOnSelectedButton)

        # Import/Export buttons
        self.ieButtons = QWidget(self)
        self.ieButtons.setLayout(QHBoxLayout())
        self.ieButtons.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().addWidget(self.ieButtons, 1, 0)
        self.layout().setAlignment(self.ieButtons, Qt.AlignLeft)

        self.exportSelectedButton = QPushButton(self.ieButtons)
        self.exportSelectedButton.clicked.connect(self.__export_presets)
        self.ieButtons.layout().addWidget(self.exportSelectedButton)

        self.importButton = QPushButton(self.ieButtons)
        self.importButton.clicked.connect(self.__import_presets)
        self.ieButtons.layout().addWidget(self.importButton)

        # Dialog buttons
        self.dialogButtons = QDialogButtonBox(self)
        self.dialogButtons.setStandardButtons(QDialogButtonBox.Ok)
        self.dialogButtons.accepted.connect(self.accept)
        self.layout().addWidget(self.dialogButtons, 1, 1)

        self.layout().setColumnStretch(0, 4)
        self.layout().setColumnStretch(1, 1)

        self.retranslateUi()

        self.__populate()
        self.__selection_changed()

    def retranslateUi(self):
        self.addPresetButton.setText(translate('Presets', 'Add'))
        self.renamePresetButton.setText(translate('Presets', 'Rename'))
        self.editPresetButton.setText(translate('Presets', 'Edit'))
        self.removePresetButton.setText(translate('Presets', 'Remove'))
        self.cueFromSelectedButton.setText(translate('Preset', 'Create Cue'))
        self.loadOnSelectedButton.setText(
            translate('Preset', 'Load on selected Cues'))
        self.exportSelectedButton.setText(
            translate('Presets', 'Export selected'))
        self.importButton.setText(translate('Presets', 'Import'))

    def __populate(self):
        self.presetsList.clear()
        try:
            self.presetsList.addItems(scan_presets())
        except OSError as e:
            scan_presets_error(e, parent=self)

    def __remove_preset(self):
        for item in self.presetsList.selectedItems():
            try:
                delete_preset(item.text())
                self.presetsList.takeItem(self.presetsList.currentRow())
            except OSError as e:
                delete_preset_error(e, item.text(), parent=self)

    def __add_preset(self):
        dialog = NewPresetDialog(parent=self)
        if dialog.exec_() == QDialog.Accepted:
            preset_name = dialog.get_name()
            cue_type = dialog.get_type()

            exists = preset_exists(preset_name)
            if not (exists and not check_override_dialog(preset_name)):
                try:
                    write_preset(preset_name, {'_type_': cue_type})
                    if not exists:
                        self.presetsList.addItem(preset_name)
                except OSError as e:
                    write_preset_error(e, preset_name, parent=self)

    def __rename_preset(self):
        item = self.presetsList.currentItem()
        if item is not None:
            new_name = save_preset_dialog(base_name=item.text())
            if new_name is not None and new_name != item.text():
                if preset_exists(new_name):
                    QMessageBox.warning(
                        self, translate('Presets', 'Warning'),
                        translate('Presets', 'The same name is already used!'))
                else:
                    try:
                        rename_preset(item.text(), new_name)
                        item.setText(new_name)
                    except OSError as e:
                        rename_preset_error(e, item.text(), parent=self)

    def __edit_preset(self):
        item = self.presetsList.currentItem()
        if item is not None:
            try:
                preset = load_preset(item.text())
                if preset is not None:
                    try:
                        cue_class = CueFactory.create_cue(preset.get('_type_'))
                        cue_class = cue_class.__class__
                    except Exception:
                        cue_class = Cue

                    edit_dialog = CueSettings(cue_class=cue_class)
                    edit_dialog.load_settings(preset)
                    if edit_dialog.exec_() == edit_dialog.Accepted:
                        preset.update(edit_dialog.get_settings())
                        try:
                            write_preset(item.text(), preset)
                        except OSError as e:
                            write_preset_error(e, item.text(), parent=self)
            except OSError as e:
                load_preset_error(e, item.text(), parent=self)

    def __cue_from_preset(self, preset_name):
        try:
            preset = load_preset(preset_name)
            if preset is not None:
                if CueFactory.has_factory(preset.get('_type_')):
                    cue = CueFactory.create_cue(preset['_type_'])

                    cue.update_properties(preset)
                    Application().cue_model.add(cue)
                else:
                    QMessageBox.warning(
                        self, translate('Presets', 'Warning'),
                        translate(
                            'Presets', 'Cannot create a cue from this '
                            'preset: {}').format(preset_name))
        except OSError as e:
            load_preset_error(e, preset_name, parent=self)

    def __cue_from_selected(self):
        for item in self.presetsList.selectedItems():
            self.__cue_from_preset(item.text())

    def __load_on_selected(self):
        item = self.presetsList.currentItem()
        if item is not None:
            preset_name = item.text()
            try:
                cues = Application().layout.get_selected_cues()
                if cues:
                    load_on_cues(preset_name, cues)
            except OSError as e:
                load_preset_error(e, preset_name, parent=MainWindow())

    def __export_presets(self):
        names = [item.text() for item in self.presetsList.selectedItems()]
        archive, _ = QFileDialog.getSaveFileName(self,
                                                 directory='archive.presets',
                                                 filter='*.presets')

        if archive != '':
            if not archive.endswith('.presets'):
                archive += '.presets'
            try:
                export_presets(names, archive)
            except PresetExportError as e:
                QDetailedMessageBox.dcritical(translate('Presets', 'Presets'),
                                              translate(
                                                  'Presets',
                                                  'Cannot export correctly.'),
                                              str(e),
                                              parent=self)

    def __import_presets(self):
        archive, _ = QFileDialog.getOpenFileName(self, filter='*.presets')
        if archive != '':
            try:
                if import_has_conflicts(archive):
                    answer = QMessageBox.question(
                        self,
                        translate('Presets', 'Presets'),
                        translate('Presets', 'Some presets already exists, '
                                  'overwrite?'),
                        buttons=QMessageBox.Yes | QMessageBox.Cancel)

                    if answer != QMessageBox.Yes:
                        return

                import_presets(archive)
                self.__populate()
            except PresetImportError as e:
                QDetailedMessageBox.dcritical(translate('Presets', 'Presets'),
                                              translate(
                                                  'Presets',
                                                  'Cannot import correctly.'),
                                              str(e),
                                              parent=self)

    def __selection_changed(self):
        selection = len(self.presetsList.selectedIndexes())

        self.editPresetButton.setEnabled(selection == 1)
        self.renamePresetButton.setEnabled(selection == 1)
        self.loadOnSelectedButton.setEnabled(selection == 1)
        self.removePresetButton.setEnabled(selection > 0)
        self.cueFromSelectedButton.setEnabled(selection > 0)
        self.exportSelectedButton.setEnabled(selection > 0)
class TriggersSettings(SettingsSection):

    Name = 'Triggers'
    Dialog = CueListDialog()

    def __init__(self, size, cue=None, **kwargs):
        super().__init__(size, cue=None, **kwargs)
        self.setLayout(QVBoxLayout(self))

        self.list = QListWidget(self)
        self.list.setAlternatingRowColors(True)
        self.layout().addWidget(self.list)

        self.buttons = QDialogButtonBox(self)
        self.buttons.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.layout().addWidget(self.buttons)

        self.addButton = self.buttons.addButton('Add',
                                                QDialogButtonBox.ActionRole)
        self.delButton = self.buttons.addButton('Remove',
                                                QDialogButtonBox.ActionRole)

        self.addButton.clicked.connect(self._add_trigger_dialog)
        self.delButton.clicked.connect(self._remove_trigger)

        self.Dialog.reset()
        self.Dialog.add_cues(Application().layout.get_cues())

        self.duration = -1

    def _add_new_trigger(self, cue_id, action, name):
        item = QListWidgetItem()
        item.setSizeHint(QSize(200, 30))
        widget = TriggerWidget(item)
        widget.timeEdit.editingFinished.connect(self.list.sortItems)
        widget.load_trigger(cue_id, action, self.duration, name)

        self.list.addItem(item)
        self.list.setItemWidget(item, widget)

    def _add_trigger_dialog(self):
        cue = self.cue_select_dialog()
        if cue is not None:
            self._add_new_trigger(cue['id'], 'play', cue['name'])

    def _remove_trigger(self):
        widget = self.list.itemWidget(self.list.item(self.list.currentRow()))
        widget.timeEdit.editingFinished.disconnect()
        self.list.takeItem(self.list.currentRow())

    @classmethod
    def cue_select_dialog(cls):
        if cls.Dialog.exec_() == QDialog.Accepted:
            return cls.Dialog.selected_cues()[0]

    def set_configuration(self, conf):
        if conf is not None:
            self.duration = conf.get('media', {}).get('duration', -1)

            if 'triggers' in conf:
                for action, ids in conf['triggers'].items():
                    for cue_id in ids:
                        cue = Application().layout.get_cue_by_id(cue_id)
                        if cue is not None:
                            self._add_new_trigger(cue_id, action, cue['name'])

    def get_configuration(self):
        triggers = {}

        for n in range(self.list.count()):
            trigger = self.list.itemWidget(self.list.item(n))
            action, target = trigger.get_trigger()

            if action not in triggers:
                triggers[action] = [target]
            else:
                triggers[action].append(target)

        return {'triggers': triggers}
class MikochikuAlarm(QWidget):
    def __init__(self, parent=None):
        super(MikochikuAlarm, self).__init__(parent)
        self.search_ch_id = settings.CHID
        self.old_video_id_list = []
        self.request = HttpRequest()
        # メンバー一覧のjsonを取得し、memberに格納
        with open("./res/channel/hololive.json", encoding="UTF-8") as file:
            self.member = json.load(file)
        self.initUI()
        # 起動直後にチャンネルIDを調べる
        self.check_live()
        # 古いログファイルを削除しログファイルの数を一定個数以下にする。(既定値:5個)
        logger.remove_old_log()

    def initUI(self):

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.check_live)
        self.timer.setInterval(40000)
        self.timer.start()

        sakura_miko = QLabel(self)
        sakura_miko.setPixmap(QPixmap(resource_path(settings.ICON)))
        sakura_miko.move(65, 70)

        self.alarm_cb = QCheckBox(self.localized_text("alarm"), self)
        self.alarm_cb.toggle()

        self.webbrowser_cb = QCheckBox(self.localized_text("webbrowser"), self)
        self.webbrowser_cb.toggle()

        self.alarm_state = "waiting"
        self.alarm_stop = QPushButton(self.localized_text("waiting"), self)

        self.alarm_stop.clicked[bool].connect(self.stop_alarm)

        self.config_btn = QPushButton("config", self)
        self.config_btn.clicked.connect(self.config_dialog)
        self.dialogs = list()

        # setGeometry
        self.alarm_cb.setGeometry(10, 10, 250, 20)
        self.webbrowser_cb.setGeometry(10, 30, 250, 20)
        self.alarm_stop.setGeometry(80, 80, 80, 25)
        self.config_btn.setGeometry(195, 120, 60, 25)

        main_width = 260
        main_height = 150
        self.setGeometry(300, 300, main_width, main_height)
        self.setFixedSize(main_width, main_height)
        self.setWindowTitle(self.localized_text("title"))

        # メンバー名をlistWidgetに格納
        self.listWidget = QListWidget(self)
        for v in self.member:
            self.listWidget.addItem(v['name'])
        self.listWidget.move(30, 200)
        self.listWidget.itemClicked.connect(self.set_target_channel)

        # v 更新通知 / ログ出力 タブ表示用
        # self.notice_dialog()
        # self.log_viewer_dialog()

        # タスクトレイ周り
        self.tray = TrayWidget(self)
        self.tray.show()

        self.show()

    def config_dialog(self):
        config = config_tab.ConfigTab(self)
        self.dialogs.append(config)

    def notice_dialog(self):
        notice = release_notice.ReleaseNotice(self)
        self.dialogs.append(notice)

    def log_viewer_dialog(self):
        log_out = log_viewer.LogViewer(self)
        self.dialogs.append(log_out)

    def set_target_channel(self, qmode8ndex):
        # 要素番号使うのでcurrentRow()に変更
        member = self.member[self.listWidget.currentRow()]
        self.search_ch_id = member['channel_id']

    def check_live(self):
        videos = []
        should_open_browser = self.webbrowser_cb.checkState()
        buff_video_id_set = self.get_live_video_id(self.search_ch_id)
        for getting_video_id in buff_video_id_set.keys():
            if getting_video_id in self.old_video_id_list:
                continue
            videos.append({
                'vid': getting_video_id,
                'title': buff_video_id_set[getting_video_id]
            })
            self.old_video_id_list.append(getting_video_id)
            if len(self.old_video_id_list) > 30:
                self.old_video_id_list.pop(0)
            log.info(''.join([
                self.localized_text("started"), ' -[', getting_video_id, '] ',
                buff_video_id_set[getting_video_id]
            ]))
            log.debug(
                f"buff_video_id_set: {[id for id in buff_video_id_set.keys()]}"
            )
            log.debug(f"self.old_video_id_list {self.old_video_id_list}")
            self.alarm_stop.click()
            self.alarm_state = "stop"
            self.alarm_stop.setText(self.localized_text("stop"))
            if should_open_browser:
                webbrowser.open("https://www.youtube.com/watch?v=" +
                                getting_video_id)
            if self.alarm_cb.checkState():
                self.alarm_sound()
        if len(videos) > 0:
            t = toast.Toast(self, videos, should_open_browser)
            QTimer.singleShot(10000, t.close)

    def stop_alarm(self):
        pygame.mixer.music.stop()
        self.alarm_stop.setEnabled(True)
        self.alarm_state = "waiting"
        self.alarm_stop.setText(self.localized_text("waiting"))

    def alarm_sound(self):
        # loop = 1
        # if self.loop_cb.checkState():
        loop_count = 5
        pygame.mixer.music.play(loop_count)
        pygame.mixer.music.play(loop_count)

    def get_live_video_id(self, search_ch_id):
        try:
            self.request = HttpRequest()
            source = vparser.get_source_json(self.request, search_ch_id)
            video_ids = vparser.extract_video_ids(source)
            return video_ids
        except vparser.InvalidChannelIDException:
            # チャンネルページが見つからない場合
            # TODO: アラートダイアログをポップアウトさせたい
            log.error(f'{search_ch_id} は、存在しないチャンネルです。')
        except Exception as e:
            log.error('不明なエラーが発生しました')
            log.error(f'{type(e)}:{str(e)}')
        return {}

    def load_locale_json(self):  # from json file
        path = "./res/lang/locale.json"
        with open(path, mode='r') as file:
            dict_json = json.load(file)
            return dict_json["locale"]

    def localized_text(self, content):
        path = "./res/lang/" + self.load_locale_json() + ".json"
        with open(path, encoding="UTF-8") as file:
            dict_json = json.load(file)
        return dict_json[content]

    def update_ui_language(self):
        self.setWindowTitle(self.localized_text("title"))
        self.webbrowser_cb.setText(self.localized_text("webbrowser"))
        self.alarm_cb.setText(self.localized_text("alarm"))
        self.alarm_stop.setText(self.localized_text(self.alarm_state))

    def changeEvent(self, event):
        # メインウィジェットの最小化ボタンを押したら非表示にする。
        if self.windowState() & Qt.WindowMinimized:
            # TODO: config内の「最小化時にタスクトレイの格納」にチェックが入っているか否かの
            # 判定をここで行う。チェックが入っている場合はhide()を実行。
            self.hide()

    def closeEvent(self, event: QCloseEvent):
        # main()にてapp.setQuitOnLastWindowClosed(False)を設定しているため
        # そのままだとcloseEventがスルーされXボタンを押しても終了できない。
        # この関数でcloseEventを捕捉して終了させる。
        QCoreApplication.quit()
Beispiel #34
0
class plotWidget(QWidget):
    NextId = 1

    FONT_MAX_SIZE = 30
    FONT_MIN_SIZE = 10

    TEXTCHANGED = 0

    customDataChanged = pyqtSignal()

    def __init__(self, filename=None, parent=None):
        """
        Creates an Instance of QWidget

         Args:
             filename (str): for opening a parameter file
             parent  (object)

        """
        super(plotWidget, self).__init__(parent)
        self.parent = parent

        self.setAttribute(Qt.WA_DeleteOnClose)

        self.filename = filename  # VIT
        self.save_title = ''

        self.setWindowTitle(QFileInfo(self.filename).fileName())

        hbox = QHBoxLayout(self)
        self.plotToolWidget = showPlot(self, width=5, height=4, dpi=100)

        self.listWidget = QListWidget(self)
        self.listWidget.currentRowChanged.connect(self.updatePlot)
        # self.listWidget.setMinimumWidth(200)
        self.listWidget.setMaximumWidth(200)

        self.splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)  # This is Yeah
        self.splitter.addWidget(self.plotToolWidget)  # This is Yeah
        self.splitter.addWidget(self.listWidget)  # This is Yeah

        hbox.addWidget(self.splitter)
        # hbox.addWidget(listWidget)

        self.readData()

    def updatePlot(self):
        print("Update Plot Yahooooo ")
        row = self.listWidget.currentRow()
        self.title = self.listWidget.item(row).text()
        print('Row ', row)
        print('----------------------------')
        x, y = self.edrObject.dataExtractFromRow(row)
        unit = self.edrObject.getUnits(row)
        print('Unit is ', unit)
        # print('x ',len(x))
        # print('y ',len(y))

        self.plotToolWidget.plotFigure(x, y, 'ps', unit, self.title)

    def readData(self):
        self.edrObject = EdrIO(self.filename, 'float')  # for now
        print('edrObject ', self.edrObject)
        self.populateList()

    def populateList(self):
        self.props = self.edrObject.read('avail quantities')
        print('props ', self.props)
        index = 0
        for i in self.props:
            self.listWidget.addItem(str(index) + '. ' + i)
            index += 1

    def save(self):
        #: So self.title has to be modified
        self.save_title = self.title.split(' ')[1] + '.png'
        print('save_title is ', self.save_title)

        if "edr" in self.filename:
            filename = QFileDialog.getSaveFileName(self,
                                                   "G.R.O.M. Editor -- Save File As", self.save_title,
                                                   "png (*.png  *.*)")
            print('filename is ', filename)
            if len(filename[0]) == 0:
                return
            self.filenameSave = filename[0]
            print('Save graph ', self.filenameSave)
        # self.setWindowTitle(QFileInfo(self.filename).fileName())
        exception = None
        fh = None
        try:
            # fh = QFile(self.filenameSave)
            # if not fh.open(QIODevice.WriteOnly):
            # raise IOError(str(fh.errorString()))
            self.plotToolWidget.saveFig(self.filenameSave)
        except EnvironmentError as e:
            exception = e
            print('error in saving ', e)
Beispiel #35
0
class Calendar(QWidget):
    # keep the current time as class variable for reference
    currentDay = datetime.now().day
    currentMonth = datetime.now().month
    currentYear = datetime.now().year

    def __init__(self):
        super().__init__()
        folder = path.dirname(__file__)
        self.icon_folder = path.join(folder, "icons")

        self.setWindowTitle("Planner")
        self.setWindowIcon(
            QtGui.QIcon(path.join(self.icon_folder, 'window.png')))

        self.setGeometry(300, 200, 600, 400)
        self.initUI()

    def initUI(self):
        self.calendar = QCalendarWidget()
        self.calendar.setGeometry(0, 0, 300, 300)
        self.calendar.setGridVisible(True)

        # don't allow going back to past months in calendar
        self.calendar.setMinimumDate(
            QDate(self.currentYear, self.currentMonth, 1))

        # format for dates in calendar that have events
        self.fmt = QTextCharFormat()
        self.fmt.setBackground(QColor(255, 165, 0, 100))

        # format for the current day
        cur_fmt = QTextCharFormat()
        cur_fmt.setBackground(QColor(0, 255, 90, 70))

        # format to change back to if all events are deleted
        self.delfmt = QTextCharFormat()
        self.delfmt.setBackground(Qt.transparent)

        # check if json file exists, if it does load the data from it
        file_exists = path.isfile(
            path.join(path.dirname(__file__), "data.json"))
        if file_exists:
            with open("data.json", "r") as json_file:
                self.data = json.load(json_file)
        else:
            self.data = {}

        # delete data from days prior to the current day
        cur = QDate.currentDate()
        for date in list(self.data.keys()):
            check_date = QDate.fromString(date, "dMyyyy")
            if cur.daysTo(check_date) <= 0 and cur != check_date:
                self.data.pop(date)
            else:
                self.calendar.setDateTextFormat(check_date, self.fmt)

        # mark current day in calendar
        current = str(self.currentDay) + str(self.currentMonth) + str(
            self.currentYear)
        self.calendar.setDateTextFormat(QDate.fromString(current, "dMyyyy"),
                                        cur_fmt)

        # organize buttons and layouts for display
        addButton = QPushButton("Add Event")
        addButton.clicked.connect(self.addNote)
        editButton = QPushButton("Edit")
        editButton.clicked.connect(self.editNote)
        delButton = QPushButton("Delete")
        delButton.clicked.connect(self.delNote)

        self.calendar.selectionChanged.connect(self.showDateInfo)
        self.calendar.selectionChanged.connect(self.labelDate)
        self.calendar.selectionChanged.connect(self.highlightFirstItem)

        self.note_group = QListWidget()
        self.note_group.setSortingEnabled(True)
        self.note_group.setStyleSheet("QListView::item {height: 40px;}")

        self.label = QLabel()
        label_font = QtGui.QFont("Gabriola", 18)
        self.label.setFont(label_font)
        self.labelDate()
        self.showDateInfo()

        labelp = QLabel()
        pixmap = QPixmap(path.join(self.icon_folder, 'calendar.png'))
        labelp.setPixmap(pixmap)

        # set up a timer that automatically updates every second
        self.lcd = QLCDNumber()
        self.lcd.setSegmentStyle(QLCDNumber.Filled)
        self.lcd.setMinimumWidth(80)
        timer = QTimer(self)
        timer.timeout.connect(self.showTime)
        timer.start(1000)
        self.showTime()

        hbox1 = QHBoxLayout()
        hbox1.addStretch(1)
        hbox1.addWidget(self.label)
        hbox1.addStretch(1)

        hbox2 = QHBoxLayout()
        hbox2.addWidget(addButton)
        hbox2.addWidget(editButton)
        hbox2.addWidget(delButton)

        hbox3 = QHBoxLayout()
        hbox3.addStretch(1)
        hbox3.addWidget(labelp)
        hbox3.addWidget(self.lcd)

        vbox = QVBoxLayout()
        vbox.addLayout(hbox1)
        vbox.addWidget(self.note_group)
        vbox.addLayout(hbox2)
        vbox.addLayout(hbox3)

        hbox = QHBoxLayout()
        hbox.addWidget(self.calendar)
        hbox.addLayout(vbox)

        self.setLayout(hbox)

    def showDateInfo(self):
        # add events to selected date
        date = self.getDate()
        self.note_group.clear()
        if date in self.data:
            self.note_group.addItems(self.data[date])

    def addNote(self):
        # adding notes for selected date
        # if a note starts with any number other than 0, 1, 2
        # add a 0 before it so that we can easily sort events
        # by start time
        date = self.getDate()
        row = self.note_group.currentRow()
        title = "Add {}".format("event")
        string, ok = QInputDialog.getText(self, title, title)

        if ok and string:
            if string[0].isdigit() and string[0] not in ["0", "1", "2"]:
                string = string.replace(string[0], "0" + string[0])
            self.note_group.insertItem(row, string)
            self.calendar.setDateTextFormat(QDate.fromString(date, "dMyyyy"),
                                            self.fmt)
            if date in self.data:
                self.data[date].append(string)
            else:
                self.data[date] = [string]

    def delNote(self):
        # delete the currently selected item
        date = self.getDate()
        row = self.note_group.currentRow()
        item = self.note_group.item(row)

        if not item:
            return
        reply = QMessageBox.question(self, "Remove", "Remove",
                                     QMessageBox.Yes | QMessageBox.No)

        if reply == QMessageBox.Yes:
            item = self.note_group.takeItem(row)
            self.data[date].remove(item.text())
            if not self.data[date]:
                del (self.data[date])
                self.calendar.setDateTextFormat(
                    QDate.fromString(date, "dMyyyy"), self.delfmt)
            del (item)

    def editNote(self):
        # edit the currently selected item
        date = self.getDate()
        row = self.note_group.currentRow()
        item = self.note_group.item(row)

        if item:
            copy = item.text()
            title = "Edit event"
            string, ok = QInputDialog.getText(self, title, title,
                                              QLineEdit.Normal, item.text())

            if ok and string:
                self.data[date].remove(copy)
                self.data[date].append(string)
                if string[0].isdigit() and string[0] not in ["0", "1", "2"]:
                    string = string.replace(string[0], "0" + string[0])
                item.setText(string)

    def getDate(self):
        # parse the selected date into usable string form
        select = self.calendar.selectedDate()
        date = str(select.day()) + str(select.month()) + str(select.year())
        return date

    def labelDate(self):
        # label to show the long name form of the selected date
        # format US style like "Thursday, February 20, 2020"
        select = self.calendar.selectedDate()
        weekday, month = select.dayOfWeek(), select.month()
        day, year = str(select.day()), str(select.year())
        week_day, word_month = QDate.longDayName(weekday), QDate.longMonthName(
            month)
        self.label.setText(week_day + ", " + word_month + " " + day + ", " +
                           year)

    def highlightFirstItem(self):
        # highlight the first item immediately after switching selection
        if self.note_group.count() > 0:
            self.note_group.setCurrentRow(0)

    def showTime(self):
        # keep the current time updated
        time = QTime.currentTime()
        text = time.toString("hh:mm")
        if time.second() % 2 == 0:
            text.replace(text[2], '')
        self.lcd.display(text)

    def closeEvent(self, e):
        # save all data into json file when user closes app
        with open("data.json", "w") as json_file:
            json.dump(self.data, json_file)
        e.accept()
Beispiel #36
0
class HyphenDialog(QDialog):
    def __init__(self, mainwindow):
        super(HyphenDialog, self).__init__(mainwindow)
        self.setWindowModality(Qt.WindowModal)
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.topLabel = QLabel()
        self.listWidget = QListWidget()

        layout.addWidget(self.topLabel)
        layout.addWidget(self.listWidget)
        layout.addWidget(widgets.Separator())

        self.buttons = b = QDialogButtonBox()
        layout.addWidget(b)
        b.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        userguide.addButton(b, "lyrics")
        b.rejected.connect(self.reject)
        b.accepted.connect(self.accept)

        self.load()
        app.translateUI(self)
        qutil.saveDialogSize(self, "hyphenation/dialog/size")

    def translateUI(self):
        self.setWindowTitle(app.caption(_("Hyphenate Lyrics Text")))
        self.topLabel.setText(_("Please select a language:"))

    def load(self):
        current = po.setup.current()
        self._langs = [(language_names.languageName(lang, current), lang, dic)
                       for lang, dic in findDicts().items()]
        self._langs.sort()
        for name, lang, dic in self._langs:
            self.listWidget.addItem("{0}  ({1})".format(name, lang))

        def select():
            lastused = settings().value("lastused", "", str)
            if lastused:
                yield lastused
            lang = po.setup.preferred()[0]
            yield lang
            yield lang.split('_')[0]

        langs = [item[1] for item in self._langs]
        for preselect in select():
            try:
                self.listWidget.setCurrentRow(langs.index(preselect))
                break
            except ValueError:
                continue

    def hyphenator(self):
        if self.exec_() and self._langs:
            lang, dic = self._langs[self.listWidget.currentRow()][1:]
            result = hyphenator.Hyphenator(dic)
            settings().setValue("lastused", lang)
        else:
            result = None
        self.deleteLater()
        return result
Beispiel #37
0
class SettingsDialog(QDialog):
    worker = None
    config = None
    configfile = None

    saved = QtCore.pyqtSignal()

    def __init__(self, parent, worker, config, configfile):
        QDialog.__init__(self, parent)

        self.worker = worker
        self.config = config
        self.configfile = configfile
        self.setStyleSheet("QGroupBox { font-weight: bold; } ")
        self.setWindowTitle('Settings')
        layout = QGridLayout()

        # Categories
        self.category_list = QListWidget()
        category_media = QListWidgetItem(getIcon('media-playback-start'),
                                         'Media', self.category_list)
        category_sync = QListWidgetItem(getIcon('view-refresh'), 'Sync',
                                        self.category_list)
        category_ui = QListWidgetItem(getIcon('window-new'), 'User Interface',
                                      self.category_list)
        category_theme = QListWidgetItem(getIcon('applications-graphics'),
                                         'Theme', self.category_list)
        self.category_list.setSelectionMode(QAbstractItemView.SingleSelection)
        self.category_list.setCurrentRow(0)
        self.category_list.setMaximumWidth(
            self.category_list.sizeHintForColumn(0) + 15)
        self.category_list.setFocus()
        self.category_list.currentItemChanged.connect(self.s_switch_page)

        # Media tab
        page_media = QWidget()
        page_media_layout = QVBoxLayout()
        page_media_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Media settings
        g_media = QGroupBox('Media settings')
        g_media.setFlat(True)
        g_media_layout = QFormLayout()
        self.tracker_enabled = QCheckBox()
        self.tracker_enabled.toggled.connect(self.tracker_type_change)

        self.tracker_type = QComboBox()
        for (n, label) in utils.available_trackers:
            self.tracker_type.addItem(label, n)
        self.tracker_type.currentIndexChanged.connect(self.tracker_type_change)

        self.tracker_interval = QSpinBox()
        self.tracker_interval.setRange(5, 1000)
        self.tracker_interval.setMaximumWidth(60)
        self.tracker_process = QLineEdit()
        self.tracker_update_wait = QSpinBox()
        self.tracker_update_wait.setRange(0, 1000)
        self.tracker_update_wait.setMaximumWidth(60)
        self.tracker_update_close = QCheckBox()
        self.tracker_update_prompt = QCheckBox()
        self.tracker_not_found_prompt = QCheckBox()
        self.tracker_ignore_not_next = QCheckBox()

        g_media_layout.addRow('Enable tracker', self.tracker_enabled)
        g_media_layout.addRow('Tracker type', self.tracker_type)
        g_media_layout.addRow('Tracker interval (seconds)',
                              self.tracker_interval)
        g_media_layout.addRow('Process name (regex)', self.tracker_process)
        g_media_layout.addRow('Wait before updating (seconds)',
                              self.tracker_update_wait)
        g_media_layout.addRow('Wait until the player is closed',
                              self.tracker_update_close)
        g_media_layout.addRow('Ask before updating',
                              self.tracker_update_prompt)
        g_media_layout.addRow('Ask to add new shows',
                              self.tracker_not_found_prompt)
        g_media_layout.addRow('Ignore if not next episode',
                              self.tracker_ignore_not_next)

        g_media.setLayout(g_media_layout)

        # Group: Plex settings
        g_plex = QGroupBox('Plex Media Server')
        g_plex.setFlat(True)
        self.plex_host = QLineEdit()
        self.plex_port = QLineEdit()
        self.plex_user = QLineEdit()
        self.plex_passw = QLineEdit()
        self.plex_passw.setEchoMode(QLineEdit.Password)
        self.plex_obey_wait = QCheckBox()

        g_plex_layout = QGridLayout()
        g_plex_layout.addWidget(QLabel('Host and Port'), 0, 0, 1, 1)
        g_plex_layout.addWidget(self.plex_host, 0, 1, 1, 1)
        g_plex_layout.addWidget(self.plex_port, 0, 2, 1, 2)
        g_plex_layout.addWidget(QLabel('Use "wait before updating" time'), 1,
                                0, 1, 1)
        g_plex_layout.addWidget(self.plex_obey_wait, 1, 2, 1, 1)
        g_plex_layout.addWidget(QLabel('myPlex login (claimed server)'), 2, 0,
                                1, 1)
        g_plex_layout.addWidget(self.plex_user, 2, 1, 1, 1)
        g_plex_layout.addWidget(self.plex_passw, 2, 2, 1, 2)

        g_plex.setLayout(g_plex_layout)

        # Group: Jellyfin settings
        g_jellyfin = QGroupBox('Jellyfin')
        g_jellyfin.setFlat(True)
        self.jellyfin_host = QLineEdit()
        self.jellyfin_port = QLineEdit()
        self.jellyfin_user = QLineEdit()
        self.jellyfin_apikey = QLineEdit()

        g_jellyfin_layout = QGridLayout()
        g_jellyfin_layout.addWidget(QLabel('Host and Port'), 0, 0, 1, 1)
        g_jellyfin_layout.addWidget(self.jellyfin_host, 0, 1, 1, 1)
        g_jellyfin_layout.addWidget(self.jellyfin_port, 0, 2, 1, 2)
        g_jellyfin_layout.addWidget(QLabel('API and User'), 1, 0, 1, 1)
        g_jellyfin_layout.addWidget(self.jellyfin_apikey, 1, 1, 1, 1)
        g_jellyfin_layout.addWidget(self.jellyfin_user, 1, 2, 1, 2)

        g_jellyfin.setLayout(g_jellyfin_layout)

        # Group: Kodi settings
        g_kodi = QGroupBox('Kodi')
        g_kodi.setFlat(True)
        self.kodi_host = QLineEdit()
        self.kodi_port = QLineEdit()
        self.kodi_user = QLineEdit()
        self.kodi_passw = QLineEdit()
        self.kodi_passw.setEchoMode(QLineEdit.Password)
        self.kodi_obey_wait = QCheckBox()

        g_kodi_layout = QGridLayout()
        g_kodi_layout.addWidget(QLabel('Host and Port'), 0, 0, 1, 1)
        g_kodi_layout.addWidget(self.kodi_host, 0, 1, 1, 1)
        g_kodi_layout.addWidget(self.kodi_port, 0, 2, 1, 2)
        g_kodi_layout.addWidget(QLabel('Use "wait before updating" time'), 1,
                                0, 1, 1)
        g_kodi_layout.addWidget(self.kodi_obey_wait, 1, 2, 1, 1)
        g_kodi_layout.addWidget(QLabel('Kodi login'), 2, 0, 1, 1)
        g_kodi_layout.addWidget(self.kodi_user, 2, 1, 1, 1)
        g_kodi_layout.addWidget(self.kodi_passw, 2, 2, 1, 2)

        g_kodi.setLayout(g_kodi_layout)

        # Group: Library
        g_playnext = QGroupBox('Library')
        g_playnext.setFlat(True)
        self.player = QLineEdit()
        self.player_browse = QPushButton('Browse...')
        self.player_browse.clicked.connect(self.s_player_browse)
        lbl_searchdirs = QLabel('Media directories')
        lbl_searchdirs.setAlignment(QtCore.Qt.AlignTop)
        self.searchdirs = QListWidget()
        self.searchdirs_add = QPushButton('Add...')
        self.searchdirs_add.clicked.connect(self.s_searchdirs_add)
        self.searchdirs_remove = QPushButton('Remove')
        self.searchdirs_remove.clicked.connect(self.s_searchdirs_remove)
        self.searchdirs_buttons = QVBoxLayout()
        self.searchdirs_buttons.setAlignment(QtCore.Qt.AlignTop)
        self.searchdirs_buttons.addWidget(self.searchdirs_add)
        self.searchdirs_buttons.addWidget(self.searchdirs_remove)
        self.searchdirs_buttons.addWidget(QSplitter())
        self.library_autoscan = QCheckBox()
        self.scan_whole_list = QCheckBox()
        self.library_full_path = QCheckBox()

        g_playnext_layout = QGridLayout()
        g_playnext_layout.addWidget(QLabel('Player'), 0, 0, 1, 1)
        g_playnext_layout.addWidget(self.player, 0, 1, 1, 1)
        g_playnext_layout.addWidget(self.player_browse, 0, 2, 1, 1)
        g_playnext_layout.addWidget(lbl_searchdirs, 1, 0, 1, 1)
        g_playnext_layout.addWidget(self.searchdirs, 1, 1, 1, 1)
        g_playnext_layout.addLayout(self.searchdirs_buttons, 1, 2, 1, 1)
        g_playnext_layout.addWidget(QLabel('Rescan Library at startup'), 2, 0,
                                    1, 2)
        g_playnext_layout.addWidget(self.library_autoscan, 2, 2, 1, 1)
        g_playnext_layout.addWidget(QLabel('Scan through whole list'), 3, 0, 1,
                                    2)
        g_playnext_layout.addWidget(self.scan_whole_list, 3, 2, 1, 1)
        g_playnext_layout.addWidget(
            QLabel('Take subdirectory name into account'), 4, 0, 1, 2)
        g_playnext_layout.addWidget(self.library_full_path, 4, 2, 1, 1)

        g_playnext.setLayout(g_playnext_layout)

        # Media form
        page_media_layout.addWidget(g_media)
        page_media_layout.addWidget(g_plex)
        page_media_layout.addWidget(g_jellyfin)
        page_media_layout.addWidget(g_kodi)
        page_media_layout.addWidget(g_playnext)
        page_media.setLayout(page_media_layout)

        # Sync tab
        page_sync = QWidget()
        page_sync_layout = QVBoxLayout()
        page_sync_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Autoretrieve
        g_autoretrieve = QGroupBox('Autoretrieve')
        g_autoretrieve.setFlat(True)
        self.autoretrieve_off = QRadioButton('Disabled')
        self.autoretrieve_always = QRadioButton('Always at start')
        self.autoretrieve_days = QRadioButton('After n days')
        self.autoretrieve_days.toggled.connect(self.s_autoretrieve_days)
        self.autoretrieve_days_n = QSpinBox()
        self.autoretrieve_days_n.setRange(1, 100)
        g_autoretrieve_layout = QGridLayout()
        g_autoretrieve_layout.setColumnStretch(0, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_off, 0, 0, 1, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_always, 1, 0, 1, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_days, 2, 0, 1, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_days_n, 2, 1, 1, 1)
        g_autoretrieve.setLayout(g_autoretrieve_layout)

        # Group: Autosend
        g_autosend = QGroupBox('Autosend')
        g_autosend.setFlat(True)
        self.autosend_off = QRadioButton('Disabled')
        self.autosend_always = QRadioButton('Immediately after every change')
        self.autosend_minutes = QRadioButton('After n minutes')
        self.autosend_minutes.toggled.connect(self.s_autosend_minutes)
        self.autosend_minutes_n = QSpinBox()
        self.autosend_minutes_n.setRange(1, 1000)
        self.autosend_size = QRadioButton('After the queue reaches n items')
        self.autosend_size.toggled.connect(self.s_autosend_size)
        self.autosend_size_n = QSpinBox()
        self.autosend_size_n.setRange(2, 20)
        self.autosend_at_exit = QCheckBox('At exit')
        g_autosend_layout = QGridLayout()
        g_autosend_layout.setColumnStretch(0, 1)
        g_autosend_layout.addWidget(self.autosend_off, 0, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_always, 1, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_minutes, 2, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_minutes_n, 2, 1, 1, 1)
        g_autosend_layout.addWidget(self.autosend_size, 3, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_size_n, 3, 1, 1, 1)
        g_autosend_layout.addWidget(self.autosend_at_exit, 4, 0, 1, 1)
        g_autosend.setLayout(g_autosend_layout)

        # Group: Extra
        g_extra = QGroupBox('Additional options')
        g_extra.setFlat(True)
        self.auto_status_change = QCheckBox('Change status automatically')
        self.auto_status_change.toggled.connect(self.s_auto_status_change)
        self.auto_status_change_if_scored = QCheckBox(
            'Change status automatically only if scored')
        self.auto_date_change = QCheckBox(
            'Change start and finish dates automatically')
        g_extra_layout = QVBoxLayout()
        g_extra_layout.addWidget(self.auto_status_change)
        g_extra_layout.addWidget(self.auto_status_change_if_scored)
        g_extra_layout.addWidget(self.auto_date_change)
        g_extra.setLayout(g_extra_layout)

        # Sync layout
        page_sync_layout.addWidget(g_autoretrieve)
        page_sync_layout.addWidget(g_autosend)
        page_sync_layout.addWidget(g_extra)
        page_sync.setLayout(page_sync_layout)

        # UI tab
        page_ui = QWidget()
        page_ui_layout = QFormLayout()
        page_ui_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Icon
        g_icon = QGroupBox('Notification Icon')
        g_icon.setFlat(True)
        self.tray_icon = QCheckBox('Show tray icon')
        self.tray_icon.toggled.connect(self.s_tray_icon)
        self.close_to_tray = QCheckBox('Close to tray')
        self.start_in_tray = QCheckBox('Start minimized to tray')
        self.tray_api_icon = QCheckBox('Use API icon as tray icon')
        self.notifications = QCheckBox(
            'Show notification when tracker detects new media')
        g_icon_layout = QVBoxLayout()
        g_icon_layout.addWidget(self.tray_icon)
        g_icon_layout.addWidget(self.close_to_tray)
        g_icon_layout.addWidget(self.start_in_tray)
        g_icon_layout.addWidget(self.tray_api_icon)
        g_icon_layout.addWidget(self.notifications)
        g_icon.setLayout(g_icon_layout)

        # Group: Window
        g_window = QGroupBox('Window')
        g_window.setFlat(True)
        self.remember_geometry = QCheckBox('Remember window size and position')
        self.remember_columns = QCheckBox('Remember column layouts and widths')
        self.columns_per_api = QCheckBox(
            'Use different visible columns per API')
        g_window_layout = QVBoxLayout()
        g_window_layout.addWidget(self.remember_geometry)
        g_window_layout.addWidget(self.remember_columns)
        g_window_layout.addWidget(self.columns_per_api)
        g_window.setLayout(g_window_layout)

        # Group: Lists
        g_lists = QGroupBox('Lists')
        g_lists.setFlat(True)
        self.filter_bar_position = QComboBox()
        filter_bar_positions = [(FilterBar.PositionHidden, 'Hidden'),
                                (FilterBar.PositionAboveLists, 'Above lists'),
                                (FilterBar.PositionBelowLists, 'Below lists')]
        for (n, label) in filter_bar_positions:
            self.filter_bar_position.addItem(label, n)
        self.inline_edit = QCheckBox('Enable in-line editing')
        g_lists_layout = QFormLayout()
        g_lists_layout.addRow('Filter bar position:', self.filter_bar_position)
        g_lists_layout.addRow(self.inline_edit)
        g_lists.setLayout(g_lists_layout)

        # UI layout
        page_ui_layout.addWidget(g_icon)
        page_ui_layout.addWidget(g_window)
        page_ui_layout.addWidget(g_lists)
        page_ui.setLayout(page_ui_layout)

        # Theming tab
        page_theme = QWidget()
        page_theme_layout = QFormLayout()
        page_theme_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Episode Bar
        g_ep_bar = QGroupBox('Episode Bar')
        g_ep_bar.setFlat(True)
        self.ep_bar_style = QComboBox()
        ep_bar_styles = [(ShowsTableDelegate.BarStyleBasic, 'Basic'),
                         (ShowsTableDelegate.BarStyle04, 'Trackma'),
                         (ShowsTableDelegate.BarStyleHybrid, 'Hybrid')]
        for (n, label) in ep_bar_styles:
            self.ep_bar_style.addItem(label, n)
        self.ep_bar_style.currentIndexChanged.connect(self.s_ep_bar_style)
        self.ep_bar_text = QCheckBox('Show text label')
        g_ep_bar_layout = QFormLayout()
        g_ep_bar_layout.addRow('Style:', self.ep_bar_style)
        g_ep_bar_layout.addRow(self.ep_bar_text)
        g_ep_bar.setLayout(g_ep_bar_layout)

        # Group: Colour scheme
        g_scheme = QGroupBox('Color Scheme')
        g_scheme.setFlat(True)
        col_tabs = [('rows', '&Row highlights'),
                    ('progress', '&Progress widget')]
        self.colors = {}
        self.colors['rows'] = [('is_playing', 'Playing'),
                               ('is_queued', 'Queued'),
                               ('new_episode', 'New Episode'),
                               ('is_airing', 'Airing'),
                               ('not_aired', 'Unaired')]
        self.colors['progress'] = [('progress_bg', 'Background'),
                                   ('progress_fg', 'Watched bar'),
                                   ('progress_sub_bg', 'Aired episodes'),
                                   ('progress_sub_fg', 'Stored episodes'),
                                   ('progress_complete', 'Complete')]
        self.color_buttons = []
        self.syscolor_buttons = []
        g_scheme_layout = QGridLayout()
        tw_scheme = QTabWidget()
        for (key, tab_title) in col_tabs:
            page = QFrame()
            page_layout = QGridLayout()
            col = 0
            # Generate widgets from the keys and values
            for (key, label) in self.colors[key]:
                self.color_buttons.append(QPushButton())
                # self.color_buttons[-1].setStyleSheet('background-color: ' + getColor(self.config['colors'][key]).name())
                self.color_buttons[-1].setFocusPolicy(QtCore.Qt.NoFocus)
                self.color_buttons[-1].clicked.connect(
                    self.s_color_picker(key, False))
                self.syscolor_buttons.append(QPushButton('System Colors'))
                self.syscolor_buttons[-1].clicked.connect(
                    self.s_color_picker(key, True))
                page_layout.addWidget(QLabel(label), col, 0, 1, 1)
                page_layout.addWidget(self.color_buttons[-1], col, 1, 1, 1)
                page_layout.addWidget(self.syscolor_buttons[-1], col, 2, 1, 1)
                col += 1
            page.setLayout(page_layout)
            tw_scheme.addTab(page, tab_title)
        g_scheme_layout.addWidget(tw_scheme)
        g_scheme.setLayout(g_scheme_layout)

        # UI layout
        page_theme_layout.addWidget(g_ep_bar)
        page_theme_layout.addWidget(g_scheme)
        page_theme.setLayout(page_theme_layout)

        # Content
        self.contents = QStackedWidget()
        for page in (page_media, page_sync, page_ui, page_theme):
            scrollable_page = QScrollArea()
            scrollable_page.setWidgetResizable(True)
            scrollable_page.setWidget(page)
            self.contents.addWidget(scrollable_page)

        if pyqt_version != 5:
            self.contents.layout().setMargin(0)

        # Bottom buttons
        bottombox = QDialogButtonBox(QDialogButtonBox.Ok
                                     | QDialogButtonBox.Apply
                                     | QDialogButtonBox.Cancel)
        bottombox.accepted.connect(self.s_save)
        bottombox.button(QDialogButtonBox.Apply).clicked.connect(self._save)
        bottombox.rejected.connect(self.reject)

        # Main layout finish
        layout.addWidget(self.category_list, 0, 0, 1, 1)
        layout.addWidget(self.contents, 0, 1, 1, 1)
        layout.addWidget(bottombox, 1, 0, 1, 2)
        layout.setColumnStretch(1, 1)

        self._load()
        self.update_colors()

        self.setLayout(layout)

    def _add_dir(self, path):
        self.searchdirs.addItem(path)

    def _load(self):
        engine = self.worker.engine
        tracker_type = self.tracker_type.findData(
            engine.get_config('tracker_type'))
        autoretrieve = engine.get_config('autoretrieve')
        autosend = engine.get_config('autosend')

        self.tracker_enabled.setChecked(engine.get_config('tracker_enabled'))
        self.tracker_type.setCurrentIndex(max(0, tracker_type))
        self.tracker_interval.setValue(engine.get_config('tracker_interval'))
        self.tracker_process.setText(engine.get_config('tracker_process'))
        self.tracker_update_wait.setValue(
            engine.get_config('tracker_update_wait_s'))
        self.tracker_update_close.setChecked(
            engine.get_config('tracker_update_close'))
        self.tracker_update_prompt.setChecked(
            engine.get_config('tracker_update_prompt'))
        self.tracker_not_found_prompt.setChecked(
            engine.get_config('tracker_not_found_prompt'))
        self.tracker_ignore_not_next.setChecked(
            engine.get_config('tracker_ignore_not_next'))

        self.player.setText(engine.get_config('player'))
        self.library_autoscan.setChecked(engine.get_config('library_autoscan'))
        self.scan_whole_list.setChecked(engine.get_config('scan_whole_list'))
        self.library_full_path.setChecked(
            engine.get_config('library_full_path'))

        self.plex_host.setText(engine.get_config('plex_host'))
        self.plex_port.setText(engine.get_config('plex_port'))
        self.plex_obey_wait.setChecked(
            engine.get_config('plex_obey_update_wait_s'))
        self.plex_user.setText(engine.get_config('plex_user'))
        self.plex_passw.setText(engine.get_config('plex_passwd'))

        self.jellyfin_host.setText(engine.get_config('jellyfin_host'))
        self.jellyfin_port.setText(engine.get_config('jellyfin_port'))
        self.jellyfin_user.setText(engine.get_config('jellyfin_user'))
        self.jellyfin_apikey.setText(engine.get_config('jellyfin_api_key'))

        self.kodi_host.setText(engine.get_config('kodi_host'))
        self.kodi_port.setText(engine.get_config('kodi_port'))
        self.kodi_obey_wait.setChecked(
            engine.get_config('kodi_obey_update_wait_s'))
        self.kodi_user.setText(engine.get_config('kodi_user'))
        self.kodi_passw.setText(engine.get_config('kodi_passwd'))

        for path in engine.get_config('searchdir'):
            self._add_dir(path)

        if autoretrieve == 'always':
            self.autoretrieve_always.setChecked(True)
        elif autoretrieve == 'days':
            self.autoretrieve_days.setChecked(True)
        else:
            self.autoretrieve_off.setChecked(True)

        self.autoretrieve_days_n.setValue(
            engine.get_config('autoretrieve_days'))

        if autosend == 'always':
            self.autosend_always.setChecked(True)
        elif autosend in ('minutes', 'hours'):
            self.autosend_minutes.setChecked(True)
        elif autosend == 'size':
            self.autosend_size.setChecked(True)
        else:
            self.autosend_off.setChecked(True)

        self.autosend_minutes_n.setValue(engine.get_config('autosend_minutes'))
        self.autosend_size_n.setValue(engine.get_config('autosend_size'))

        self.autosend_at_exit.setChecked(engine.get_config('autosend_at_exit'))
        self.auto_status_change.setChecked(
            engine.get_config('auto_status_change'))
        self.auto_status_change_if_scored.setChecked(
            engine.get_config('auto_status_change_if_scored'))
        self.auto_date_change.setChecked(engine.get_config('auto_date_change'))

        self.tray_icon.setChecked(self.config['show_tray'])
        self.close_to_tray.setChecked(self.config['close_to_tray'])
        self.start_in_tray.setChecked(self.config['start_in_tray'])
        self.tray_api_icon.setChecked(self.config['tray_api_icon'])
        self.notifications.setChecked(self.config['notifications'])
        self.remember_geometry.setChecked(self.config['remember_geometry'])
        self.remember_columns.setChecked(self.config['remember_columns'])
        self.columns_per_api.setChecked(self.config['columns_per_api'])
        self.filter_bar_position.setCurrentIndex(
            self.filter_bar_position.findData(
                self.config['filter_bar_position']))
        self.inline_edit.setChecked(self.config['inline_edit'])

        self.ep_bar_style.setCurrentIndex(
            self.ep_bar_style.findData(self.config['episodebar_style']))
        self.ep_bar_text.setChecked(self.config['episodebar_text'])

        self.autoretrieve_days_n.setEnabled(self.autoretrieve_days.isChecked())
        self.autosend_minutes_n.setEnabled(self.autosend_minutes.isChecked())
        self.autosend_size_n.setEnabled(self.autosend_size.isChecked())
        self.close_to_tray.setEnabled(self.tray_icon.isChecked())
        self.start_in_tray.setEnabled(self.tray_icon.isChecked())
        self.notifications.setEnabled(self.tray_icon.isChecked())

        self.color_values = self.config['colors'].copy()

        self.tracker_type_change(None)

    def _save(self):
        engine = self.worker.engine

        engine.set_config('tracker_enabled', self.tracker_enabled.isChecked())
        engine.set_config(
            'tracker_type',
            self.tracker_type.itemData(self.tracker_type.currentIndex()))
        engine.set_config('tracker_interval', self.tracker_interval.value())
        engine.set_config('tracker_process', str(self.tracker_process.text()))
        engine.set_config('tracker_update_wait_s',
                          self.tracker_update_wait.value())
        engine.set_config('tracker_update_close',
                          self.tracker_update_close.isChecked())
        engine.set_config('tracker_update_prompt',
                          self.tracker_update_prompt.isChecked())
        engine.set_config('tracker_not_found_prompt',
                          self.tracker_not_found_prompt.isChecked())
        engine.set_config('tracker_ignore_not_next',
                          self.tracker_ignore_not_next.isChecked())

        engine.set_config('player', self.player.text())
        engine.set_config('library_autoscan',
                          self.library_autoscan.isChecked())
        engine.set_config('scan_whole_list', self.scan_whole_list.isChecked())
        engine.set_config('library_full_path',
                          self.library_full_path.isChecked())

        engine.set_config('plex_host', self.plex_host.text())
        engine.set_config('plex_port', self.plex_port.text())
        engine.set_config('plex_obey_update_wait_s',
                          self.plex_obey_wait.isChecked())
        engine.set_config('plex_user', self.plex_user.text())
        engine.set_config('plex_passwd', self.plex_passw.text())

        engine.set_config('jellyfin_host', self.jellyfin_host.text())
        engine.set_config('jellyfin_port', self.jellyfin_port.text())
        engine.set_config('jellyfin_user', self.jellyfin_user.text())
        engine.set_config('jellyfin_apikey', self.jellyfin_apikey.text())

        engine.set_config('kodi_host', self.kodi_host.text())
        engine.set_config('kodi_port', self.kodi_port.text())
        engine.set_config('kodi_obey_update_wait_s',
                          self.kodi_obey_wait.isChecked())
        engine.set_config('kodi_user', self.kodi_user.text())
        engine.set_config('kodi_passwd', self.kodi_passw.text())

        engine.set_config('searchdir', [
            self.searchdirs.item(i).text()
            for i in range(self.searchdirs.count())
        ])

        if self.autoretrieve_always.isChecked():
            engine.set_config('autoretrieve', 'always')
        elif self.autoretrieve_days.isChecked():
            engine.set_config('autoretrieve', 'days')
        else:
            engine.set_config('autoretrieve', 'off')

        engine.set_config('autoretrieve_days',
                          self.autoretrieve_days_n.value())

        if self.autosend_always.isChecked():
            engine.set_config('autosend', 'always')
        elif self.autosend_minutes.isChecked():
            engine.set_config('autosend', 'minutes')
        elif self.autosend_size.isChecked():
            engine.set_config('autosend', 'size')
        else:
            engine.set_config('autosend', 'off')

        engine.set_config('autosend_minutes', self.autosend_minutes_n.value())
        engine.set_config('autosend_size', self.autosend_size_n.value())

        engine.set_config('autosend_at_exit',
                          self.autosend_at_exit.isChecked())
        engine.set_config('auto_status_change',
                          self.auto_status_change.isChecked())
        engine.set_config('auto_status_change_if_scored',
                          self.auto_status_change_if_scored.isChecked())
        engine.set_config('auto_date_change',
                          self.auto_date_change.isChecked())

        engine.save_config()

        self.config['show_tray'] = self.tray_icon.isChecked()
        self.config['close_to_tray'] = self.close_to_tray.isChecked()
        self.config['start_in_tray'] = self.start_in_tray.isChecked()
        self.config['tray_api_icon'] = self.tray_api_icon.isChecked()
        self.config['notifications'] = self.notifications.isChecked()
        self.config['remember_geometry'] = self.remember_geometry.isChecked()
        self.config['remember_columns'] = self.remember_columns.isChecked()
        self.config['columns_per_api'] = self.columns_per_api.isChecked()
        self.config['filter_bar_position'] = self.filter_bar_position.itemData(
            self.filter_bar_position.currentIndex())
        self.config['inline_edit'] = self.inline_edit.isChecked()

        self.config['episodebar_style'] = self.ep_bar_style.itemData(
            self.ep_bar_style.currentIndex())
        self.config['episodebar_text'] = self.ep_bar_text.isChecked()

        self.config['colors'] = self.color_values

        utils.save_config(self.config, self.configfile)

        self.saved.emit()

    def s_save(self):
        self._save()
        self.accept()

    def switch_kodi_state(self, state):
        self.kodi_host.setEnabled(state)
        self.kodi_port.setEnabled(state)
        self.kodi_user.setEnabled(state)
        self.kodi_passw.setEnabled(state)
        self.kodi_obey_wait.setEnabled(state)

    def switch_plex_state(self, state):
        self.plex_host.setEnabled(state)
        self.plex_port.setEnabled(state)
        self.plex_user.setEnabled(state)
        self.plex_passw.setEnabled(state)
        self.plex_obey_wait.setEnabled(state)

    def switch_jellyfin_state(self, state):
        self.jellyfin_host.setEnabled(state)
        self.jellyfin_port.setEnabled(state)
        self.jellyfin_user.setEnabled(state)
        self.jellyfin_apikey.setEnabled(state)

    def tracker_type_change(self, checked):
        if self.tracker_enabled.isChecked():
            self.tracker_interval.setEnabled(True)
            self.tracker_update_wait.setEnabled(True)
            self.tracker_type.setEnabled(True)
            if self.tracker_type.itemData(
                    self.tracker_type.currentIndex()) == 'plex':
                self.switch_jellyfin_state(False)
                self.switch_plex_state(True)
                self.switch_kodi_state(False)
                self.tracker_process.setEnabled(False)
            elif self.tracker_type.itemData(
                    self.tracker_type.currentIndex()) == 'jellyfin':
                self.switch_jellyfin_state(True)
                self.switch_kodi_state(False)
                self.switch_plex_state(False)
                self.tracker_process.setEnabled(False)
            elif self.tracker_type.itemData(
                    self.tracker_type.currentIndex()) == 'kodi':
                self.switch_jellyfin_state(False)
                self.switch_kodi_state(True)
                self.switch_plex_state(False)
                self.tracker_process.setEnabled(False)
            else:
                self.tracker_process.setEnabled(True)
                self.switch_jellyfin_state(False)
                self.switch_plex_state(False)
                self.switch_kodi_state(False)
        else:
            self.tracker_type.setEnabled(False)
            self.switch_jellyfin_state(False)
            self.switch_plex_state(False)
            self.switch_kodi_state(False)

            self.tracker_process.setEnabled(False)
            self.tracker_interval.setEnabled(False)
            self.tracker_update_wait.setEnabled(False)

    def s_autoretrieve_days(self, checked):
        self.autoretrieve_days_n.setEnabled(checked)

    def s_autosend_minutes(self, checked):
        self.autosend_minutes_n.setEnabled(checked)

    def s_autosend_size(self, checked):
        self.autosend_size_n.setEnabled(checked)

    def s_tray_icon(self, checked):
        self.close_to_tray.setEnabled(checked)
        self.start_in_tray.setEnabled(checked)
        self.tray_api_icon.setEnabled(checked)
        self.notifications.setEnabled(checked)

    def s_ep_bar_style(self, index):
        if self.ep_bar_style.itemData(index) == ShowsTableDelegate.BarStyle04:
            self.ep_bar_text.setEnabled(False)
        else:
            self.ep_bar_text.setEnabled(True)

    def s_auto_status_change(self, checked):
        self.auto_status_change_if_scored.setEnabled(checked)

    def s_player_browse(self):
        if pyqt_version == 5:
            self.player.setText(
                QFileDialog.getOpenFileName(
                    caption='Choose player executable')[0])
        else:
            self.player.setText(
                QFileDialog.getOpenFileName(
                    caption='Choose player executable'))

    def s_searchdirs_add(self):
        self._add_dir(
            QFileDialog.getExistingDirectory(caption='Choose media directory'))

    def s_searchdirs_remove(self):
        row = self.searchdirs.currentRow()
        if row != -1:
            self.searchdirs.takeItem(row)

    def s_switch_page(self, new, old):
        if not new:
            new = old

        self.contents.setCurrentIndex(self.category_list.row(new))

    def s_color_picker(self, key, system):
        return lambda: self.color_picker(key, system)

    def color_picker(self, key, system):
        if system is True:
            current = self.color_values[key]
            result = ThemedColorPicker.do()
            if result is not None and result is not current:
                self.color_values[key] = result
                self.update_colors()
        else:
            current = getColor(self.color_values[key])
            result = QColorDialog.getColor(current)
            if result.isValid() and result is not current:
                self.color_values[key] = str(result.name())
                self.update_colors()

    def update_colors(self):
        for ((key, label),
             color) in zip(self.colors['rows'] + self.colors['progress'],
                           self.color_buttons):
            color.setStyleSheet('background-color: ' +
                                getColor(self.color_values[key]).name())
class Theme(QWidget):
    """Theme widget class."""

    def __init__(self, parent):
        super(Theme, self).__init__()
        self._preferences, vbox = parent, QVBoxLayout(self)
        vbox.addWidget(QLabel(self.tr("<b>Select Theme:</b>")))
        self.list_skins = QListWidget()
        self.list_skins.setSelectionMode(QListWidget.SingleSelection)
        vbox.addWidget(self.list_skins)
        self.btn_delete = QPushButton(self.tr("Delete Theme"))
        self.btn_preview = QPushButton(self.tr("Preview Theme"))
        self.btn_create = QPushButton(self.tr("Create Theme"))
        hbox = QHBoxLayout()
        hbox.addWidget(self.btn_delete)
        hbox.addSpacerItem(QSpacerItem(10, 0, QSizePolicy.Expanding,
                           QSizePolicy.Fixed))
        hbox.addWidget(self.btn_preview)
        hbox.addWidget(self.btn_create)
        vbox.addLayout(hbox)
        self._refresh_list()

        self.btn_preview.clicked['bool'].connect(self.preview_theme)
        self.btn_delete.clicked['bool'].connect(self.delete_theme)
        self.btn_create.clicked['bool'].connect(self.create_theme)

        self._preferences.savePreferences.connect(self.save)

    def delete_theme(self):
        if self.list_skins.currentRow() != 0:
            file_name = ("%s.qss" % self.list_skins.currentItem().text())
            qss_file = file_manager.create_path(resources.NINJA_THEME_DOWNLOAD,
                                                file_name)
            file_manager.delete_file(qss_file)
            self._refresh_list()

    def create_theme(self):
        designer = preferences_theme_editor.ThemeEditor(self)
        designer.exec_()
        self._refresh_list()

    def showEvent(self, event):
        self._refresh_list()
        super(Theme, self).showEvent(event)

    def _refresh_list(self):
        self.list_skins.clear()
        self.list_skins.addItem("Default")

        files = [file_manager.get_file_name(filename) for filename in
                 file_manager.get_files_from_folder(
                     resources.NINJA_THEME_DOWNLOAD, "qss")]
        files.sort()
        self.list_skins.addItems(files)

        if settings.NINJA_SKIN in files:
            index = files.index(settings.NINJA_SKIN)
            self.list_skins.setCurrentRow(index + 1)
        else:
            self.list_skins.setCurrentRow(0)

    def save(self):
        qsettings = IDE.ninja_settings()
        settings.NINJA_SKIN = self.list_skins.currentItem().text()
        qsettings.setValue("preferences/theme/skin", settings.NINJA_SKIN)
        self.preview_theme()

    def preview_theme(self):
        if self.list_skins.currentRow() == 0:
            qss_file = resources.NINJA_THEME
        else:
            file_name = ("%s.qss" % self.list_skins.currentItem().text())
            qss_file = file_manager.create_path(resources.NINJA_THEME_DOWNLOAD,
                                                file_name)
        with open(qss_file) as f:
            qss = f.read()
        QApplication.instance().setStyleSheet(qss)
class PseudoDialog(QDialog):
    def __init__(self, parent=None):
        super(PseudoDialog, self).__init__(parent)
        self.initDialog()

    def initDialog(self):
        self.setWindowTitle('伪指令')

        self.addButton = QPushButton('添加')
        self.addButton.clicked.connect(self.addItem)
        self.delButton = QPushButton('删除')
        self.delButton.setEnabled(False)
        self.delButton.clicked.connect(self.delItem)
        self.saveButton = QPushButton('保存')
        self.saveButton.setEnabled(False)
        self.saveButton.clicked.connect(self.saveItem)

        self.pseudoNameLabel = QLabel('伪指令名')
        self.pseudoNameValue = QLineEdit()
        self.pseudoNameValue.setEnabled(False)
        self.operandNumberLabel = QLabel('操作数数量')
        self.operandNumberValue = QLineEdit()
        self.operandNumberValue.setEnabled(False)
        self.realInsLabel = QLabel('真指令')
        self.realInsValue = MipsEditor()
        self.realInsValue.setEnabled(False)
        self.highlighter = MipsHighlighter(self.realInsValue.document())

        self.pseudoList = QListWidget(self)
        for op in pseudo.pseudo_dict.keys():
            self.pseudoList.addItem(op)
        self.pseudoList.currentItemChanged.connect(self.changeItem)
        self.updateList('')

        buttonLayout = QVBoxLayout()
        buttonLayout.addWidget(self.addButton)
        buttonLayout.addWidget(self.delButton)
        buttonLayout.addWidget(self.saveButton)

        upperLayout = QHBoxLayout()
        upperLayout.addWidget(self.pseudoList)
        upperLayout.addLayout(buttonLayout)

        lowerLayout = QVBoxLayout()
        lowerLayout.addWidget(self.pseudoNameLabel)
        lowerLayout.addWidget(self.pseudoNameValue)
        lowerLayout.addWidget(self.operandNumberLabel)
        lowerLayout.addWidget(self.operandNumberValue)
        lowerLayout.addWidget(self.realInsLabel)
        lowerLayout.addWidget(self.realInsValue)

        layout = QVBoxLayout()
        layout.addLayout(upperLayout)
        layout.addLayout(lowerLayout)
        self.setLayout(layout)

    def updateList(self, itemName):
        self.pseudoList.sortItems()

        for i in range(0, self.pseudoList.count()):
            if self.pseudoList.item(i).text() == itemName:
                self.pseudoList.setCurrentRow(i)
                return

        self.pseudoList.setCurrentRow(0)

    def addItem(self):
        id = 0
        while ('pseudo' + str(id)) in pseudo.pseudo_dict.keys():
            id += 1

        ins = 'pseudo' + str(id)
        pseudo.pseudo_dict[ins] = [0, '']
        self.pseudoList.addItem(ins)
        self.updateList(ins)

    def delItem(self):
        ins = self.pseudoList.currentItem().text()
        pseudo.pseudo_dict.pop(ins)
        self.pseudoList.takeItem(self.pseudoList.currentRow())
        self.updateList('')

    def saveItem(self):
        ins = self.pseudoList.currentItem().text()
        new_ins = self.pseudoNameValue.text()
        try:
            opNum = int(self.operandNumberValue.text())
        except:
            opNum = 0
        real = self.realInsValue.toPlainText()

        if new_ins == '':
            QMessageBox.critical(self, '错误',
                                 '<p style="font-size: 16px;">伪指令名不能为空!</p>')
            return
        elif ins != new_ins and (new_ins in pseudo.pseudo_dict.keys()
                                 or new_ins in R_INSTRUCTIONS.keys()
                                 or new_ins in I_INSTRUCTIONS.keys()
                                 or new_ins in J_INSTRUCTIONS.keys()):
            QMessageBox.critical(
                self, '错误', '<p style="font-size: 16px;">伪指令名与已有指令或伪指令重复!</p>')
            return

        pseudo.pseudo_dict.pop(ins)
        self.pseudoList.takeItem(self.pseudoList.currentRow())
        pseudo.pseudo_dict[new_ins] = [opNum, ';'.join(real.split('\n'))]
        self.pseudoList.addItem(new_ins)
        self.updateList(new_ins)

    def changeItem(self):
        if self.pseudoList.currentItem() == None:
            self.pseudoNameValue.clear()
            self.operandNumberValue.clear()
            self.realInsValue.clear()

            self.pseudoNameValue.setEnabled(False)
            self.operandNumberValue.setEnabled(False)
            self.realInsValue.setEnabled(False)
            self.delButton.setEnabled(False)
            self.saveButton.setEnabled(False)
        else:
            ins = self.pseudoList.currentItem().text()

            self.pseudoNameValue.setText(ins)
            self.operandNumberValue.setText(str(pseudo.pseudo_dict[ins][0]))
            self.realInsValue.setPlainText('\n'.join(
                pseudo.pseudo_dict[ins][1].split(';')))

            self.pseudoNameValue.setEnabled(True)
            self.operandNumberValue.setEnabled(True)
            self.realInsValue.setEnabled(True)
            self.delButton.setEnabled(True)
            self.saveButton.setEnabled(True)
Beispiel #40
0
class H5PlotGUI(QDialog):
    """The main GUI for H5Plot.

    From here the SolSets, SolTabs and antennas to plot are selected.
    """
    def __init__(self, h5file, logging_instance, parent=None):
        super(H5PlotGUI, self).__init__(parent)
        self.logger = logging_instance
        self.figures = []

        self.h5parm = lh5.h5parm(h5file)
        self.solset_labels = self.h5parm.getSolsetNames()
        self.solset = self.h5parm.getSolset('sol000')

        self.soltab_labels = self.solset.getSoltabNames()
        self.soltab = self.solset.getSoltab(self.soltab_labels[0])

        self.stations = self.soltab.getValues()[1]['ant']
        self.refant = 'CS001HBA0'
        self.wrapphase = True

        self.stcache = SoltabCache(self.soltab.getValues(), self.soltab.getAxesNames())
        rvals, raxes = reorder_soltab(self.soltab)
        self.stcache.update(rvals, raxes)

        self.move(300, 300)
        self.setWindowTitle('H5Plot')

        self.solset_label = QLabel('SolSet: ')
        self.solset_picker = QComboBox()
        for l in self.solset_labels:
            self.solset_picker.addItem(l)
        self.solset_picker.activated.connect(self._solset_picker_event)

        self.soltab_label_y = QLabel('Plot ')
        self.soltab_label_x = QLabel(' vs ')
        self.soltab_picker = QComboBox()
        for l in self.soltab_labels:
            self.soltab_picker.addItem(l)
        self.soltab_picker.activated.connect(self._soltab_picker_event)
        self.axis_picker = QComboBox()
        self.axis_picker.addItems(['time', 'freq'])
        self.axis_picker.activated.connect(self._axis_picker_event)
        self.axis = 'time'

        self.refant_label = QLabel('Ref. Ant. ')
        self.refant_picker = QComboBox()
        self.refant_picker.addItems(self.stations)
        self.refant_picker.activated.connect(self._refant_picker_event)

        self.phasewrap_box = QCheckBox('Wrap Phases')
        self.phasewrap_box.setChecked(True)
        self.phasewrap_box.setEnabled(False)
        self.phasewrap_box.stateChanged.connect(self._phasewrap_event)

        self.plot_button = QPushButton('Plot')
        self.plot_button.clicked.connect(self._plot_button_event)

        self.station_picker = QListWidget()
        self.station_picker.addItems(self.stations)
        self.station_picker.setCurrentRow(0)


        plot_layout = QGridLayout()
        plot_layout.addWidget(self.soltab_label_y, 0, 0)
        plot_layout.addWidget(self.soltab_picker, 0, 1)
        plot_layout.addWidget(self.soltab_label_x, 0, 2)
        plot_layout.addWidget(self.axis_picker, 0, 3)
        plot_layout.addWidget(self.refant_label, 1, 0)
        plot_layout.addWidget(self.refant_picker, 1, 1)
        plot_layout.addWidget(self.phasewrap_box, 1, 3)

        layout = QFormLayout(self)
        layout.addRow(self.solset_label, self.solset_picker)
        layout.addRow(plot_layout)
        layout.addRow(self.plot_button)
        layout.addRow(self.station_picker)

    def _axis_picker_event(self):
        """Callback function for when the x-axis is changed.

        Sets the `axis` attribute to the selected axis
        """
        self.logger.debug('Axis changed to: ' + self.axis_picker.currentText())
        self.axis = self.axis_picker.currentText()

    def closeEvent(self, event):
        self.logger.info('Closing all open figures before exiting.')
        plt.close('all' )
        event.accept()

    def _refant_picker_event(self):
        self.logger.debug('Reference antenna changed to: ' + self.refant_picker.currentText())
        self.refant = self.refant_picker.currentText()

    def _solset_picker_event(self):
        """Callback function for when the SolSet is changed.

        Sets the `solset` attribute.
        """
        self.logger.debug('Solset changed to: ' + self.solset_picker.currentText())
        self.solset = self.h5parm.getSolset(self.solset_picker.currentText())
        self.soltab_labels = self.solset.getSoltabNames()
        self.soltab_picker.clear()
        for l in self.soltab_labels:
            self.soltab_picker.addItem(l)
        self._soltab_picker_event()

    def _soltab_picker_event(self):
        """Callback function for when the SolTab is changed.

        Sets the `soltab` attribute.
        """
        self.logger.debug('Soltab changed to: ' + self.soltab_picker.currentText())
        self.soltab = self.solset.getSoltab(self.soltab_picker.currentText())
        rvals, raxes = reorder_soltab(self.soltab)
        self.stcache.update(rvals, raxes)

    def _phasewrap_event(self):
        self.logger.debug('Phase wrapping changed to ' + str(self.phasewrap_box.isChecked()))
        self.wrapphase = self.phasewrap_box.isChecked()

    def _plot_button_event(self):
        """Callback function for when the plot button is pressed.

        Calls the `plot` function subsecquently.
        """
        self.logger.debug('Plotting button pressed.')
        self.plot(labels=(self.axis, self.soltab.name))

    def plot(self, labels=('x-axis', 'y-axis'), limits=([None, None], [None, None])):
        self.logger.info('Plotting ' + self.soltab.name + ' vs ' + self.axis + \
                         ' for ' + self.solset.name)

        antenna = self.station_picker.currentRow()
        refantenna = self.refant_picker.currentIndex()
        # Values have shape (timestamps, frequencies, antennas, polarizations, directions).
        values = self.stcache.values[0]
        if (('rotationmeasure' in self.soltab.name) or ('RMextract' in self.soltab.name) or ('clock' in self.soltab.name)) and (self.axis == 'freq'):
            self.logger.warning('Rotation Measure does not support frequency axis! Switch to time instead.')
            return
        else:
            x_axis = self.stcache.values[1][self.axis]
        st_type = self.soltab.getType()
        print(st_type)

        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.set_title(self.stations[antenna])

        if self.axis == 'time':
            if 'rotationmeasure' in self.soltab.name:
                y_axis = values[:, antenna]
                ax.plot(x_axis, y_axis)
            elif ('pol' in self.stcache.axes) and ('dir' in self.stcache.axes):
                if st_type == 'phase':
                    ax.set_ylim(-np.pi, np.pi)
                    # Plot phase-like quantities w.r.t. to a reference antenna.
                    y_axis = values[:, 0, antenna, :, 0] - values[:, 0, refantenna, :, 0]
                    if self.wrapphase:
                        y_axis = wrap_phase(y_axis)
                elif (st_type == 'clock') or (st_type == 'rotationmeasure'):
                    y_axis = values[:, antenna]
                else:
                    y_axis = values[:, 0, antenna, :, 0]
                for i,y in enumerate(y_axis):
                    ax.plot(x_axis, y[:,i], 'h', label=self.stcache.values[1]['pol'][i])
            elif 'pol' in self.stcache.axes:
                if st_type == 'phase':
                    ax.set_ylim(-np.pi, np.pi)
                    # Plot phase-like quantities w.r.t. to a reference antenna.
                    y_axis = values[:, 0, antenna, :] - values[:, 0, refantenna, :]
                    if self.wrapphase:
                        y_axis = wrap_phase(y_axis)
                elif (st_type == 'clock') or (st_type == 'rotationmeasure'):
                    y_axis = values[:, antenna]
                else:
                    y_axis = values[:, 0, antenna, :]
                for i in range(y_axis.shape[1]):
                    ax.plot(x_axis, y_axis[:, i], 'h', label=self.stcache.values[1]['pol'][i])
            elif 'dir' in self.stcache.axes:
                if st_type == 'phase':
                    ax.set_ylim(-np.pi, np.pi)
                    # Plot phase-like quantities w.r.t. to a reference antenna.
                    y_axis = values[:, 0, antenna, 0] - values[:, 0, refantenna, 0]
                    if self.wrapphase:
                        y_axis = wrap_phase(y_axis)
                elif (st_type == 'clock') or (st_type == 'rotationmeasure'):
                    y_axis = values[:, antenna]
                else:
                    y_axis = values[:, 0, antenna, 0]
                ax.plot(x_axis, y_axis[:, i], 'h')
            elif ('pol' not in self.stcache.axes) and ('dir' not in self.stcache.axes):
                if (st_type == 'clock') or (st_type == 'rotationmeasure'):
                    y_axis = values[:, antenna]
                else:
                    y_axis = values[:, 0, antenna]
                ax.plot(x_axis, y_axis)
        elif self.axis == 'freq':
            if ('rotationmeasure' in self.soltab.name) or ('clock' in self.soltab.name):
                self.logger.warning('Rotation Measure does not support frequency axis! Switch to time instead.')
            if ('pol' in self.stcache.axes) and ('dir' in self.stcache.axes):
                if st_type == 'phase':
                    ax.set_ylim(-np.pi, np.pi)
                    # Plot phase-like quantities w.r.t. to a reference antenna.
                    y_axis = values[0, :, antenna, :, 0] - values[0, :, refantenna, :, 0]
                    if self.wrapphase:
                        y_axis = wrap_phase(y_axis)
                else:
                    y_axis = values[0, :, antenna, :, 0]
                for i,y in enumerate(y_axis):
                    ax.plot(x_axis, y[:,i])
            elif 'pol' in self.stcache.axes:
                if st_type == 'phase':
                    ax.set_ylim(-np.pi, np.pi)
                    # Plot phase-like quantities w.r.t. to a reference antenna.
                    y_axis = values[0, :, antenna, :] - values[0, :, refantenna, :]
                    if self.wrapphase:
                        y_axis = wrap_phase(y_axis)
                else:
                    y_axis = values[0, :, antenna, :]
                for i in range(y_axis.shape[1]):
                    ax.plot(x_axis, y_axis[:, i], 'h', label=self.stcache.values[1]['pol'][i])
            elif 'dir' in self.stcache.axes:
                if st_type == 'phase':
                    ax.set_ylim(-np.pi, np.pi)
                    # Plot phase-like quantities w.r.t. to a reference antenna.
                    y_axis = values[0, :, antenna, 0] - values[0, :, refantenna, 0]
                    if self.wrapphase:
                        y_axis = wrap_phase(y_axis)
                else:
                    y_axis = values[0, :, antenna, 0]
            elif ('pol' not in self.stcache.axes) and ('dir' not in self.stcache.axes):
                y_axis = values[0, :, antenna]
                ax.plot(x_axis, y_axis)

        ax.set(xlabel=self.axis, ylabel=labels[1], xlim=limits[0], ylim=limits[1])
        if ax.get_legend_handles_labels()[1]:
            ax.legend()
        self.figures.append(fig)
        fig.show()
Beispiel #41
0
class PhotoAlbum(QWidget):
    def __init__(self):
        super().__init__()
        self.left = 100
        self.top = 100
        self.width = 1600
        self.height = 900
        self.title = 'Photo Album'
        self.icon = 'icon.png'
        self.directory = os.getcwd() + '/images/'
        self.album = 'New Album'
        i = 0
        while self.album in os.listdir(self.directory):
            i += 1
            self.album = 'New Album (' + str(i) + ')'
        self.counter = 0
        self.files = self.get_all_photo(self.directory)
        self.search_word = ''
        self.initUI()

    def initUI(self):
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.setWindowTitle(self.title)
        self.setWindowIcon(QIcon(self.icon))
        self.label = QLabel(self)
        self.label.setGeometry(50, 100, 900, 750)

        button_next = QPushButton('Next', self)
        button_next.move(120, 20)
        button_next.clicked.connect(self.go_next)

        button_previous = QPushButton('Previous', self)
        button_previous.move(20, 20)
        button_previous.clicked.connect(self.go_previous)

        button_change_directory = QPushButton('Change Directory', self)
        button_change_directory.move(250, 20)
        button_change_directory.clicked.connect(self.change_directory)

        button_delete = QPushButton('Delete', self)
        button_delete.move(400, 20)
        button_delete.clicked.connect(self.delete)

        self.line_name_of_photo = QLineEdit(self)
        self.line_name_of_photo.move(550, 20)

        button_add_to_current_album = QPushButton('Add to album', self)
        button_add_to_current_album.move(550, 60)
        button_add_to_current_album.clicked.connect(self.add_to_current_album)

        line_name_of_album = QLineEdit(self)
        line_name_of_album.move(700, 20)
        line_name_of_album.setText(self.album)
        line_name_of_album.textChanged[str].connect(self.get_new_name_of_album)

        button_create_new_album = QPushButton('Create a new album', self)
        button_create_new_album.move(700, 60)
        button_create_new_album.clicked.connect(self.create_new_album)

        button_short_look = QPushButton('Look Album', self)
        button_short_look.move(900, 50)
        button_short_look.clicked.connect(self.create_thumbnails)
        self.thumbnails_window = None

        self.list_of_files = QListWidget(self)
        self.list_of_files.addItems(self.files)
        self.list_of_files.resize(500, 200)
        self.list_of_files.move(1000, 600)
        self.list_of_files.itemClicked.connect(self.load_image_from_list)

        search_line = QLineEdit(self)
        search_line.move(1000, 200)
        search_line.textChanged[str].connect(self.get_new_search_word)

        self.search_parameter = QComboBox(self)
        self.search_parameter.addItem('Name')
        self.search_parameter.addItem('Year of creation')
        self.search_parameter.move(1000, 170)

        button_search = QPushButton('Search', self)
        button_search.move(1000, 230)
        button_search.clicked.connect(self.search)

        self.load_image()
        self.show()

    def search(self):
        new_files = []
        if self.search_parameter.currentText() == 'Name':
            for file in self.files:
                temp_file_name = file.replace('\\', '/')
                if temp_file_name.split('/')[-1].find(self.search_word) != -1:
                    new_files.append(file)
        elif self.search_parameter.currentText() == 'Year of creation':
            for file in self.files:
                print(self.search_word)
                print(
                    datetime.fromtimestamp(
                        os.path.getctime(file)).strftime('%Y'))
                if datetime.fromtimestamp(os.path.getctime(file)).strftime(
                        '%Y') == self.search_word:
                    new_files.append(file)
        self.files = new_files
        self.counter = 0
        if len(self.files) == 0:
            self.list_of_files.hide()
            self.label.hide()
            self.line_name_of_photo.setText('')
        else:
            self.list_of_files.clear()
            self.list_of_files.addItems(self.files)
            self.list_of_files.show()
            self.load_image()
            self.label.show()

    def get_new_search_word(self, text):
        self.search_word = text

    def create_thumbnails(self):
        if not self.album in os.listdir('./images/'):
            return
        self.thumbnails_window = ThumbnailsWindow(
            self.album, self.get_all_photo('./images/' + self.album + '/', []))
        self.thumbnails_window.show()

    def load_image_from_list(self):
        self.counter = self.list_of_files.currentRow()
        self.load_image()

    def get_new_name_of_album(self, text):
        self.album = text

    def create_new_album(self):
        if not self.album in os.listdir('./images/'):
            os.mkdir('./images/' + self.album)
        else:
            return

    def add_to_current_album(self):
        if not self.album in os.listdir('./images/'):
            return
        if len(self.files) == 0:
            return
        temp_file_name = self.files[self.counter].replace('\\', '/')
        shutil.copy(self.files[self.counter], './images/' + self.album)
        os.rename(
            './images/' + self.album + '/' + temp_file_name.split('/')[-1],
            './images/' + self.album + '/' + self.line_name_of_photo.text())
        return

    def delete(self):
        if len(self.files) == 0:
            return
        else:
            self.files.remove(self.files[self.counter])
        if len(self.files) == 0:
            self.label.hide()
            return
        else:
            self.counter = self.counter % len(self.files)
            self.load_image()

    def get_all_photo(self, directory, lisss=[]):
        for file in os.listdir(directory):
            file = os.path.join(directory, file)
            if os.path.isdir(file):
                self.get_all_photo(file, lisss)
            else:
                file_name, ext = os.path.splitext(file)
                if not (ext != '.jpg' and ext != '.png'
                        and ext != '.gif') or os.path.isdir(file):
                    lisss.append(file)
        return lisss

    def change_directory(self):
        directory = QFileDialog.getExistingDirectory(self)
        if directory == '':
            return
        self.directory = directory + '/'
        self.files = []
        self.counter = 0

        self.files = self.get_all_photo(self.directory, self.files)
        if len(self.files) == 0:
            self.list_of_files.hide()
            self.label.hide()
            self.line_name_of_photo.setText('')
        else:
            self.list_of_files.clear()
            self.list_of_files.addItems(self.files)
            self.list_of_files.show()
            self.load_image()
            self.label.show()

    def go_next(self):
        if len(self.files) == 0:
            return
        self.counter = (self.counter + 1) % len(self.files)
        self.load_image()

    def go_previous(self):
        if len(self.files) == 0:
            return
        self.counter = (self.counter - 1) % len(self.files)
        self.load_image()

    def keyPressEvent(self, e):
        key = e.key()
        if key == Qt.Key_Escape:
            self.close()

    def load_image(self):
        if len(self.files) == 0:
            return
        pixmap = QPixmap(self.files[self.counter])
        while pixmap.width() > self.label.width() and pixmap.height(
        ) > self.label.height():
            pixmap = pixmap.scaled(pixmap.width() // 2, pixmap.height() // 2)
        self.label.setPixmap(pixmap)
        temp_file_name = self.files[self.counter].replace('\\', '/')
        self.line_name_of_photo.setText(temp_file_name.split('/')[-1])
class SessionsManager(QDialog):

    """Session Manager, to load different configurations of ninja."""

    def __init__(self, parent=None):
        super(SessionsManager, self).__init__(parent, Qt.Dialog)
        self._ide = parent
        self.setWindowTitle(translations.TR_SESSIONS_TITLE)
        self.setMinimumWidth(400)
        vbox = QVBoxLayout(self)
        vbox.addWidget(QLabel(translations.TR_SESSIONS_DIALOG_BODY))
        self.sessionList = QListWidget()
        self.sessionList.addItems([key for key in settings.SESSIONS])
        self.sessionList.setCurrentRow(0)
        self.contentList = QListWidget()
        self.btnDelete = QPushButton(translations.TR_SESSIONS_BTN_DELETE)
        self.btnDelete.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.btnUpdate = QPushButton(translations.TR_SESSIONS_BTN_UPDATE)
        self.btnUpdate.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.btnCreate = QPushButton(translations.TR_SESSIONS_BTN_CREATE)
        self.btnCreate.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.btnOpen = QPushButton(translations.TR_SESSIONS_BTN_ACTIVATE)
        self.btnOpen.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.btnOpen.setDefault(True)
        hbox = QHBoxLayout()
        hbox.addWidget(self.btnDelete)
        hbox.addWidget(self.btnUpdate)
        hbox.addWidget(self.btnCreate)
        hbox.addWidget(self.btnOpen)

        vbox.addWidget(self.sessionList)
        vbox.addWidget(self.contentList)
        vbox.addLayout(hbox)

        self.sessionList.itemSelectionChanged.connect(self.load_session_content)
        self.btnOpen.clicked['bool'].connect(self.open_session)
        self.btnUpdate.clicked['bool'].connect(self.save_session)
        self.btnCreate.clicked['bool'].connect(self.create_session)
        self.btnDelete.clicked['bool'].connect(self.delete_session)
        self.load_session_content()

    def load_session_content(self):
        """Load the selected session, replacing the current session."""
        item = self.sessionList.currentItem()
        self.contentList.clear()
        if item is not None:
            key = item.text()
            files = [translations.TR_FILES] + \
                [file[0] for file in settings.SESSIONS[key][0]]
            projects = [translations.TR_PROJECT] + settings.SESSIONS[key][1]
            content = files + projects
            self.contentList.addItems(content)

    def create_session(self):
        """Create a new Session."""
        sessionInfo = QInputDialog.getText(None,
            translations.TR_SESSIONS_CREATE_TITLE,
            translations.TR_SESSIONS_CREATE_BODY)
        if sessionInfo[1]:
            sessionName = sessionInfo[0]
            if not sessionName or sessionName in settings.SESSIONS:
                QMessageBox.information(self,
                    translations.TR_SESSIONS_MESSAGE_TITLE,
                    translations.TR_SESSIONS_MESSAGE_BODY)
                return
            SessionsManager.save_session_data(sessionName, self._ide)
        self._ide.Session = sessionName
        self.close()

    @classmethod
    def save_session_data(cls, sessionName, ide):
        """Save the updates from a session."""
        openedFiles = ide.filesystem.get_files()
        files_info = []
        for path in openedFiles:
            editable = ide.get_or_create_editable(path)
            if editable.is_dirty:
                stat_value = 0
            else:
                stat_value = os.stat(path).st_mtime
            files_info.append([path,
                               editable.editor.getCursorPosition(), stat_value])
        projects_obj = ide.filesystem.get_projects()
        projects = [projects_obj[proj].path for proj in projects_obj]
        settings.SESSIONS[sessionName] = [files_info, projects]
        qsettings = ide.data_settings()
        qsettings.setValue('ide/sessions', settings.SESSIONS)

    def save_session(self):
        """Save current session"""
        if self.sessionList.currentItem():
            sessionName = self.sessionList.currentItem().text()
            SessionsManager.save_session_data(sessionName, self._ide)
            self._ide.show_message(translations.TR_SESSIONS_UPDATED_NOTIF %
                                   {'session': sessionName}, 2000)
            self.load_session_content()

    def open_session(self):
        """Open a saved session"""
        if self.sessionList.currentItem():
            key = self.sessionList.currentItem().text()
            self._load_session_data(key)
            self._ide.Session = key
            self.close()

    def delete_session(self):
        """Delete a session"""
        if self.sessionList.currentItem():
            key = self.sessionList.currentItem().text()
            settings.SESSIONS.pop(key)
            self.sessionList.takeItem(self.sessionList.currentRow())
            self.contentList.clear()
            qsettings = self._ide.data_settings()
            qsettings.setValue('ide/sessions', settings.SESSIONS)

    def _load_session_data(self, key):
        """Activate the selected session, closing the current files/projects"""
        main_container = self._ide.get_service('main_container')
        projects_explorer = self._ide.get_service('projects_explorer')
        if projects_explorer and main_container:
            projects_explorer.close_opened_projects()
            for fileData in settings.SESSIONS[key][0]:
                path, line, stat_value = fileData
                if file_manager.file_exists(path):
                    mtime = os.stat(path).st_mtime
                    ignore_checkers = (mtime == stat_value)
                    main_container.open_file(path, line,
                                             ignore_checkers=ignore_checkers)
            if projects_explorer:
                projects_explorer.load_session_projects(
                    settings.SESSIONS[key][1])
class EventSelectionWindow(QMainWindow):
    def __init__(self, main_window):
        super().__init__()

        self.main_window = main_window

        # Defining some variables of the window
        self.title_window = "Event Selection"

        # Setting the window appropriately
        self.setWindowTitle(self.title_window)
        self.set_position()

        self.palette_main_window = self.palette()
        self.palette_main_window.setColor(QPalette.Window, Qt.black)

        # Initiate the sub-widgets
        self.init_window()

    def init_window(self):

        # Read the available labels
        self.labels = list()
        with open('../config/classes.txt') as file:
            for cnt, line in enumerate(file):
                self.labels.append(line.rstrip())

        # Read the available second labels
        self.second_labels = list()
        with open('../config/second_classes.txt') as file:
            for cnt, line in enumerate(file):
                self.second_labels.append(line.rstrip())

        # Read the available third labels
        self.third_labels = list()
        with open('../config/third_classes.txt') as file:
            for cnt, line in enumerate(file):
                self.third_labels.append(line.rstrip())

        self.list_widget = QListWidget()

        for item_nbr, element in enumerate(self.labels):
            self.list_widget.insertItem(item_nbr, element)

        self.list_widget_second = QListWidget()

        for item_nbr, element in enumerate(self.second_labels):
            self.list_widget_second.insertItem(item_nbr, element)

        self.list_widget_third = QListWidget()

        for item_nbr, element in enumerate(self.third_labels):
            self.list_widget_third.insertItem(item_nbr, element)

        #TBD
        self.path_video_first_half = None
        self.path_video_second_half = None
        self.path_labels_event = None
        self.list_events = list()

        self.list_widget_fourth = None

        self.loop_player = LoopPlayer(self.main_window)
        self.loop_duration = 10
        self.video_display = QWidget(self)
        self.video_display.setLayout(self.loop_player.layout)
        self.video_display.setFixedWidth(398)
        self.video_display.setFixedHeight(224)

        # Layout the different widgets
        central_display = QWidget(self)
        self.setCentralWidget(central_display)
        final_layout = QHBoxLayout()
        final_layout.addWidget(self.list_widget)
        final_layout.addWidget(self.list_widget_second)
        final_layout.addWidget(self.list_widget_third)
        central_display.setLayout(final_layout)

        self.to_second = False
        self.to_third = False
        self.to_fourth = False
        self.first_label = None
        self.second_label = None
        self.third_label = None
        self.fourth_label = None

    def init_replay_loop(self):

        self.path_video_first_half = os.path.dirname(
            self.main_window.media_player.path_label) + "/1.mkv"
        self.path_video_second_half = os.path.dirname(
            self.main_window.media_player.path_label) + "/2.mkv"
        self.path_labels_event = os.path.dirname(
            self.main_window.media_player.path_label) + "/Labels-v2.json"

        self.event_list_manager_first_half = ListManager()
        self.event_list_manager_second_half = ListManager()

        self.event_list_manager_first_half.event_list = self.event_list_manager_first_half.read_json_event(
            self.path_labels_event, 1)
        self.event_list_manager_second_half.event_list = self.event_list_manager_second_half.read_json_event(
            self.path_labels_event, 2)
        self.event_list_manager_first_half.sort_list()
        self.event_list_manager_second_half.sort_list()

        self.tmp_list_event = list()
        self.tmp_list_filepath = list()

        self.list_widget_fourth = QListWidget()
        self.list_widget_fourth.clicked.connect(self.doubleClicked)
        self.list_widget_fourth.itemDoubleClicked.connect(self.doubleClicked)
        self.list_widget_fourth.currentItemChanged.connect(self.doubleClicked)
        self.list_widget_fourth.setFixedWidth(398)

        # Layout the different widgets
        central_display = QWidget(self)
        self.setCentralWidget(central_display)

        final_layout = QVBoxLayout()
        horizontal_layout = QHBoxLayout()
        horizontal_layout.addWidget(self.list_widget)
        horizontal_layout.addWidget(self.list_widget_second)
        horizontal_layout.addWidget(self.list_widget_third)
        horizontal_layout.addWidget(self.list_widget_fourth)
        final_layout.addLayout(horizontal_layout)
        final_layout.addWidget(self.video_display)
        central_display.setLayout(final_layout)

    def update_replay_list(self, position, half):
        self.list_widget_fourth.clear()

        self.tmp_list_event = list()
        self.tmp_list_filepath = list()

        path_dir = os.path.dirname(self.main_window.media_player.path_label)

        if half == 1:
            for item_nbr, element in enumerate(
                    self.event_list_manager_first_half.event_list):
                if element.position <= position:
                    self.list_widget_fourth.insertItem(item_nbr,
                                                       element.to_text())
                    self.tmp_list_event.append(element)
                    self.tmp_list_filepath.append(path_dir + "/1.mkv")

        if half == 2:
            counter = 0
            for item_nbr, element in enumerate(
                    self.event_list_manager_second_half.event_list):
                if element.position <= position:
                    self.list_widget_fourth.insertItem(item_nbr,
                                                       element.to_text())
                    self.tmp_list_event.append(element)
                    self.tmp_list_filepath.append(path_dir + "/2.mkv")
                    counter += 1
            for item_nbr, element in enumerate(
                    self.event_list_manager_first_half.event_list):
                self.list_widget_fourth.insertItem(item_nbr + counter,
                                                   element.to_text())
                self.tmp_list_event.append(element)
                self.tmp_list_filepath.append(path_dir + "/1.mkv")

    def doubleClicked(self, qmodelindex):

        if self.list_widget_fourth.currentRow() >= 0:

            video_path = self.tmp_list_filepath[
                self.list_widget_fourth.currentRow()]

            position = self.tmp_list_event[
                self.list_widget_fourth.currentRow()].position

            position_start = position - self.loop_duration // 2 * 1000
            position_end = position + self.loop_duration // 2 * 1000

            self.loop_player.open_file(video_path, position_start,
                                       position_end)
        else:
            self.loop_player.media_player.pause()

    def set_position(self):
        self.xpos_window = self.main_window.pos().x(
        ) + self.main_window.frameGeometry().width() // 4
        self.ypos_window = self.main_window.pos().y(
        ) + self.main_window.frameGeometry().height() // 4
        self.width_window = self.main_window.frameGeometry().width() // 1.5
        self.height_window = self.main_window.frameGeometry().height() // 1.2
        self.setGeometry(self.xpos_window, self.ypos_window, self.width_window,
                         self.height_window)

    def keyPressEvent(self, event):

        if event.key() == Qt.Key_Return:
            if not self.to_second and not self.to_third and not self.to_fourth:
                self.first_label = self.list_widget.currentItem().text()
                self.list_widget_second.setFocus()
                self.to_second = True
            elif self.to_second:
                self.second_label = self.list_widget_second.currentItem().text(
                )
                self.to_second = False
                self.to_third = True
                self.list_widget_third.setFocus()
            elif self.to_third:
                self.third_label = self.list_widget_third.currentItem().text()
                if self.third_label != "replay":
                    position = self.main_window.media_player.media_player.position(
                    )
                    self.main_window.list_manager.add_event(
                        Camera(self.first_label, self.main_window.half,
                               ms_to_time(position), self.second_label,
                               position, self.third_label))
                    self.main_window.list_display.display_list(
                        self.main_window.list_manager.create_text_list())
                    self.first_label = None
                    self.second_label = None
                    self.to_third = False
                    # Save
                    path_label = self.main_window.media_player.get_last_label_file(
                    )
                    self.main_window.list_manager.save_file(
                        path_label, self.main_window.half)
                    self.hide()
                    self.list_widget_second.setCurrentRow(-1)
                    self.list_widget_third.setCurrentRow(-1)
                    self.list_widget_fourth.setCurrentRow(-1)
                    self.main_window.setFocus()
                else:
                    self.to_third = False
                    self.to_fourth = True
                    self.list_widget_fourth.setFocus()
            elif self.to_fourth:
                self.fourth_label = self.list_widget_fourth.currentRow()
                position = self.main_window.media_player.media_player.position(
                )
                self.main_window.list_manager.add_event(
                    Camera(self.first_label, self.main_window.half,
                           ms_to_time(position), self.second_label, position,
                           self.third_label,
                           self.tmp_list_event[self.fourth_label]))
                self.main_window.list_display.display_list(
                    self.main_window.list_manager.create_text_list())
                self.first_label = None
                self.second_label = None
                self.third_label = None
                self.fourth_label = None
                self.to_third = False
                self.to_fourth = False
                # Save
                path_label = self.main_window.media_player.get_last_label_file(
                )
                self.main_window.list_manager.save_file(
                    path_label, self.main_window.half)
                self.hide()
                self.list_widget_second.setCurrentRow(-1)
                self.list_widget_third.setCurrentRow(-1)
                self.list_widget_fourth.setCurrentRow(-1)
                self.main_window.setFocus()

        if event.key() == Qt.Key_Escape:
            self.to_second = False
            self.to_third = False
            self.to_fourth = False
            self.first_label = None
            self.second_label = None
            self.third_label = None
            self.list_widget_second.setCurrentRow(-1)
            self.list_widget_third.setCurrentRow(-1)
            self.list_widget_fourth.setCurrentRow(-1)
            self.hide()
            self.main_window.setFocus()
Beispiel #44
0
class QuestionManagerDlg(TopicManagerDlg):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(QuestionManagerDlg, cls).__new__(cls, *args, **kwargs)
        return cls._instance

    def __init__(self, parent):
        super(QuestionManagerDlg, self).__init__(parent)

        self.btnAddL = QPushButton('Add')
        self.btnRemoveL = QPushButton('Remove')
        self.btnRemoveL.setEnabled(False)
        self.LangBox.addWidget(self.btnAddL)
        self.LangBox.addWidget(self.btnRemoveL)

        self.btnAddT = QPushButton('Add')
        self.btnRemoveT = QPushButton('Remove')
        self.btnRemoveT.setEnabled(False)
        self.topicBox.addWidget(self.btnAddT)
        self.topicBox.addWidget(self.btnRemoveT)

        lblQuestions = QLabel('Select a question.')
        self.lstQuestions = QListWidget()
        self.lstQuestions.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.btnRemoveQ = QPushButton('Remove')
        self.btnRemoveQ.setEnabled(False)

        _layout3 = QVBoxLayout()
        _layout3.addWidget(lblQuestions)
        _layout3.addWidget(self.lstQuestions)
        _layout3.addWidget(self.btnRemoveQ)

        self.layout.addLayout(_layout3)
        self.setLayout(self.layout)

        self.lstLangs.itemClicked.connect(self.selectLang)
        self.lstTopics.itemClicked.connect(self.selectTopic)
        self.lstQuestions.itemClicked.connect(self.onSelectQuestion)
        self.btnAddL.clicked.connect(self.onAddL)
        self.btnRemoveL.clicked.connect(self.onRemoveL)
        self.btnAddT.clicked.connect(self.onAddT)
        self.btnRemoveT.clicked.connect(self.onRemoveT)
        self.btnRemoveQ.clicked.connect(self.onRemoveQ)

    def selectLang(self, item):
        ''' show topics that are related with selected language.
        :param item: language
        :return: QListWidgetItem
        '''
        self.btnRemoveL.setEnabled(True)
        _lang = item.text()
        lang = session.query(Language).filter(Language.name == _lang).first()
        if not lang:
            return
        topics = lang.topics.all()
        self.lstTopics.clear()
        for topic in topics:
            item = QListWidgetItem()
            item.setText(topic.title)
            item.setStatusTip(str(topic.id))
            self.lstTopics.addItem(item)
        self.lstQuestions.clear()

    def selectTopic(self, item):
        self.btnRemoveT.setEnabled(True)
        _topic = item.text()
        topic = session.query(Topic).filter(Topic.title == _topic).first()
        questions = topic.questions.all()
        self.lstQuestions.clear()
        for q in questions:
            item = QListWidgetItem()
            item.setText(q.text)
            item.setStatusTip(str(q.id))
            self.lstQuestions.addItem(item)

    def onSelectQuestion(self, item):
        ''' Set the button 'Remove question' to enabled.
        '''
        self.btnRemoveQ.setEnabled(True)

    def onAddL(self):
        ''' Add new language.
        '''
        lang, ok = QInputDialog.getText(self, 'add language', 'Input language name')
        if ok and lang and lang.strip():
            if self.lstLangs.findItems(lang, Qt.MatchFixedString):
                QMessageBox.information(self, 'Error', '{} exists already.'.format(lang))
                return
            l = Language(name=lang)
            session.add(l)
            session.commit()
            item = QListWidgetItem()
            item.setText(l.name)
            item.setStatusTip(str(l.id))
            self.lstLangs.addItem(item)

    def onRemoveL(self):
        ''' remove selected langauge and realted topics and questions.
        '''
        answer = QMessageBox.question(self, 'Remove Language',
                                      "This operation will remove this language and it's topics.")
        if answer == QMessageBox.No:
            return
        row = self.lstLangs.currentRow()
        id = self.lstLangs.currentItem().statusTip()
        record = session.query(Language).filter(Language.id == id)
        if record:
            record.delete(synchronize_session=False)
            self.lstLangs.takeItem(row)
            self.lstTopics.clear()
            ts = session.query(Topic).filter(Topic.lang_id == id).all()
            for t in ts:
                qs = session.query(Question).filter(Question.topic_id == t.id).all()
                for q in qs:
                    session.delete(q)
                session.delete(t)
        session.commit()
        self.btnRemoveL.setEnabled(False)

    # @pyqtSlot(Topic)
    def _finishAddQuestions(self, t):
        item = QListWidgetItem()
        item.setText(t.title)
        item.setStatusTip(str(t.id))
        self.lstTopics.addItem(item)
        session.commit()
        print('finished')

    # @pyqtSlot(Question)
    def _appendQuestion(self, q):
        print('q: ', q)
        item = QListWidgetItem()
        item.setText(q.text)
        item.setStatusTip(str(q.id))
        self.lstQuestions.addItem(item)
        session.add(q)

    def onAddT(self):
        ''' Add new topic.
        '''
        lang = self.lstLangs.currentItem()
        if not lang:
            return
        lang = lang.text()
        url, ok = QInputDialog.getText(self, 'add topic', 'Input url for {}'.format(lang))
        if ok and lang and url and url.strip():
            try:
                thread = Worker(lang, url, self)
                thread.update.connect(self._appendQuestion)
                thread.finish.connect(self._finishAddQuestions)
                thread.start()
            except FileExistsError:
                QMessageBox.information(self, 'Error', 'This topic exists already.')
            except ValueError:
                QMessageBox.information(self, 'Error', 'cannot access the url.')

    def onRemoveT(self):
        answer = QMessageBox.question(self, 'Remove Topic', "This operation will remove this topic and it's questions.")
        if answer == QMessageBox.No:
            return
        row = self.lstTopics.currentRow()
        id = self.lstTopics.currentItem().statusTip()
        record = session.query(Topic).filter(Topic.id == id)
        if record:
            record.delete(synchronize_session=False)
            self.lstTopics.takeItem(row)
            self.lstQuestions.clear()
            qs = session.query(Question).filter(Question.topic_id == id).all()
            for q in qs:
                session.delete(q)
        session.commit()
        self.btnRemoveT.setEnabled(False)

    def onRemoveQ(self):
        ''' Remove a selected question from database.
        '''
        for item in self.lstQuestions.selectedItems():
            id = item.statusTip()
            self.lstQuestions.setCurrentItem(item)
            row = self.lstQuestions.currentRow()
            record = session.query(Question).filter(Question.id == id)
            if record:
                record.delete(synchronize_session=False)
                self.lstQuestions.takeItem(row)
        session.commit()
        self.btnRemoveQ.setEnabled(False)
Beispiel #45
0
class AutoComplete(PopupWidget):
    def init_popup(self):
        self.list = QListWidget(self)
        self.list.itemClicked.connect(self.insertItem)
        self.layout().addWidget(self.list)
        self.items = []

    def insertItem(self, item):
        self.insert()

    def insert(self):
        completion = self.items[self.list.currentRow()].value
        cursor = self.textedit.textCursor()
        col = cursor.columnNumber()
        line = unicode(cursor.block().text())
        i = self.cursor_start_col
        while i > 0:
            # print(`line[i:col]`)
            if completion.startswith(line[i:col]):
                # print("break")
                break
            i -= 1
        # print(col,i)
        cursor.insertText(completion[col - i:])
        self.hide()

    def setItems(self, proposals):
        proposals = sorted(proposals, cmp=lambda p1, p2: cmp(p1.name, p2.name))
        del self.items[:]
        self.list.clear()
        for entry in proposals:
            i = AutoCompleteItem(entry)
            self.list.addItem(i)
            self.items.append(i)

    def keyPressEvent(self, event):
        self.list.keyPressEvent(event)
        key = event.key()
        text = event.text()
        if key in [Qt.Key_Right, Qt.Key_Enter, Qt.Key_Return]:
            text = ""
        cursor = self.textedit.textCursor()
        line = unicode(cursor.block().text())
        col = cursor.columnNumber()
        prefix = line[self.cursor_start_col:col] + unicode(text)

        found = False
        for row, item in enumerate(self.items):
            if item.value.startswith(prefix):
                current = self.items[self.list.currentRow()].value
                if not current.startswith(prefix):
                    self.list.setCurrentRow(row)
                found = True
                break
        if not found:
            self.hide()
            return

        if key in [Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown]:
            return True
        elif key in [Qt.Key_Tab, Qt.Key_Right, Qt.Key_Enter, Qt.Key_Return]:
            self.insert()
            return True
        elif not text:
            self.hide()
Beispiel #46
0
class GlyphSetTab(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.name = self.tr("Glyph sets")

        self.defaultGlyphSetBox = QCheckBox(
            self.tr("Default glyph set:"), self)
        self.defaultGlyphSetDrop = QComboBox(self)
        self.defaultGlyphSetBox.toggled.connect(self.toggleGlyphSetDrop)

        self.glyphSetList = QListWidget(self)
        self.glyphSetList.setSortingEnabled(True)
        self.glyphSetContents = QPlainTextEdit(self)
        self.glyphSetList.currentItemChanged.connect(
            self.updateGlyphSetContents)
        self.glyphSetList.itemChanged.connect(self.renameGlyphSet)
        self._cachedName = None
        splitter = QSplitter(self)
        splitter.addWidget(self.glyphSetList)
        splitter.addWidget(self.glyphSetContents)
        self.addGlyphSetButton = QPushButton(self)
        self.addGlyphSetButton.setIcon(icons.i_plus())
        self.addGlyphSetButton.clicked.connect(lambda: self.addGlyphSet())
        self.removeGlyphSetButton = QPushButton(self)
        self.removeGlyphSetButton.setIcon(icons.i_minus())
        self.removeGlyphSetButton.clicked.connect(self.removeGlyphSet)
        self.importButton = QPushButton(self.tr("Import"), self)
        importMenu = QMenu(self)
        importMenu.addAction(
            self.tr("Import from Current Font"), self.importFromCurrentFont)
        self.importButton.setMenu(importMenu)
        self.glyphListBox = QCheckBox(self.tr("Glyph list path:"), self)
        self.glyphListEdit = QLineEdit(self)
        self.glyphListEdit.setReadOnly(True)
        self.glyphListButton = QPushButton(self.tr("Browse…"), self)
        self.glyphListButton.clicked.connect(self.getGlyphList)
        self.glyphListBox.toggled.connect(self.glyphListEdit.setEnabled)
        self.glyphListBox.toggled.connect(self.glyphListButton.setEnabled)

        buttonsLayout = QHBoxLayout()
        buttonsLayout.addWidget(self.addGlyphSetButton)
        buttonsLayout.addWidget(self.removeGlyphSetButton)
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        buttonsLayout.addWidget(spacer)
        buttonsLayout.addWidget(self.importButton)

        firstLayout = QGridLayout()
        l = 0
        firstLayout.addWidget(self.defaultGlyphSetBox, l, 0, 1, 2)
        firstLayout.addWidget(self.defaultGlyphSetDrop, l, 3, 1, 3)
        l += 1
        firstLayout.addWidget(splitter, l, 0, 1, 6)
        l += 1
        firstLayout.addLayout(buttonsLayout, l, 0, 1, 3)
        secondLayout = QHBoxLayout()
        secondLayout.addWidget(self.glyphListBox)
        secondLayout.addWidget(self.glyphListEdit)
        secondLayout.addWidget(self.glyphListButton)
        mainLayout = QVBoxLayout(self)
        mainLayout.addLayout(firstLayout)
        mainLayout.addLayout(secondLayout)
        self.setLayout(mainLayout)

        self.readSettings()

    def addGlyphSet(self, glyphNames=[], glyphSetName=None):
        if glyphSetName is None:
            glyphSetName = self.tr("New glyph set")
        if glyphSetName in self.glyphSets:
            index = 1
            while "%s %d" % (glyphSetName, index) in self.glyphSets:
                index += 1
            glyphSetName = "%s %d" % (glyphSetName, index)
        self.glyphSets[glyphSetName] = glyphNames
        item = QListWidgetItem(glyphSetName, self.glyphSetList)
        item.setFlags(item.flags() | Qt.ItemIsEditable)
        self.glyphSetList.setCurrentItem(item)
        self.glyphSetList.editItem(item)
        self.removeGlyphSetButton.setEnabled(True)

    def removeGlyphSet(self):
        i = self.glyphSetList.currentRow()
        text = self.glyphSetList.takeItem(i).text()
        del self.glyphSets[text]
        if self.glyphSetList.count() < 2:
            self.removeGlyphSetButton.setEnabled(False)

    def renameGlyphSet(self):
        newKey = self.glyphSetList.currentItem()
        if newKey is None:
            return
        newKey = newKey.text()
        self.glyphSets[newKey] = self.glyphSets[self._cachedName]
        del self.glyphSets[self._cachedName]

    def importFromCurrentFont(self):
        font = QApplication.instance().currentFont()
        name = "%s %s" % (font.info.familyName, font.info.styleName)
        self.addGlyphSet(font.glyphOrder, name)

    def toggleGlyphSetDrop(self):
        sender = self.sender()
        self.defaultGlyphSetDrop.setEnabled(sender.isChecked())

    def updateGlyphSetContents(self, current, previous):
        # store content of the textEdit in the glyphSet dict
        if previous is not None:
            glyphNames = self.glyphSetContents.toPlainText().split()
            self.glyphSets[previous.text()] = glyphNames
        # now update the text edit to current glyphSet
        glyphSetName = current.text()
        text = " ".join(self.glyphSets[glyphSetName])
        self.glyphSetContents.setPlainText(text)
        # cache current name for renames
        self._cachedName = glyphSetName

    def getGlyphList(self):
        fileFormats = (
            self.tr("Text file {}").format("(*.txt)"),
            self.tr("All files {}").format("(*.*)"),
        )
        path, _ = QFileDialog.getOpenFileName(
            self, self.tr("Open File"), '', ";;".join(fileFormats)
        )
        if path:
            self.glyphListEdit.setText(path)

    def readSettings(self):
        defaultGlyphSet = settings.defaultGlyphSet()
        self.defaultGlyphSetBox.setChecked(len(defaultGlyphSet))

        self.glyphSets = settings.readGlyphSets()
        self.defaultGlyphSetDrop.clear()
        self.defaultGlyphSetDrop.addItems(self.glyphSets.keys())

        self.glyphSetList.clear()
        glyphSetNames = self.glyphSets.keys()
        # Normally we should be enforcing this rather decently in the interface
        # already
        if glyphSetNames:
            for glyphSetName in glyphSetNames:
                item = QListWidgetItem(glyphSetName, self.glyphSetList)
                item.setFlags(item.flags() | Qt.ItemIsEditable)
            self.glyphSetList.setCurrentRow(0)
        self.removeGlyphSetButton.setEnabled(len(self.glyphSets) > 1)

        glyphListPath = settings.glyphListPath()
        self.glyphListBox.setChecked(bool(glyphListPath))
        self.glyphListEdit.setEnabled(bool(glyphListPath))
        self.glyphListEdit.setText(glyphListPath)
        self.glyphListButton.setEnabled(bool(glyphListPath))

    def writeSettings(self):
        # store content of the textEdit in the glyphSet dict
        glyphNames = self.glyphSetContents.toPlainText().split()
        currentGlyphSet = self.glyphSetList.currentItem().text()
        self.glyphSets[currentGlyphSet] = glyphNames

        settings.writeGlyphSets(self.glyphSets)
        if not self.defaultGlyphSetBox.isChecked():
            settings.setDefaultGlyphSet(None)
        else:
            defaultGlyphSet = self.defaultGlyphSetDrop.currentText()
            settings.setDefaultGlyphSet(defaultGlyphSet)
        if not self.glyphListBox.isChecked():
            settings.setGlyphListPath(None)
        else:
            glyphListPath = self.glyphListEdit.text()
            if glyphListPath:
                settings.setGlyphListPath(glyphListPath)
                QApplication.instance().loadGlyphList()
Beispiel #47
0
class SubwindowBrowserSources(QWidget):
    """Show connections settings sub window."""
    current_tab = -1

    def createWindow(self, mainWindow, tab=''):
        """Create window."""
        try:
            parent = None
            super().__init__(parent)
            # self.setWindowFlags(Qt.WindowStaysOnTopHint)

            self.setWindowIcon(
                QIcon(scctool.settings.getResFile('browser.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(mainWindow.size().width() * 0.8,
                              self.sizeHint().height()))
            relativeChange = QPoint(mainWindow.size().width() / 2,
                                    mainWindow.size().height() / 3) -\
                QPoint(self.size().width() / 2,
                       self.size().height() / 3)
            self.move(mainWindow.pos() + relativeChange)

            self.setWindowTitle(_("Browser Sources"))

        except Exception as e:
            module_logger.exception("message")

    def createTabs(self, tab):
        """Create tabs."""
        self.tabs = QTabWidget()

        self.createFormGroupIntro()
        self.createFormGroupMapstats()
        self.createFormGroupMapBox()
        self.createFormGroupMapLandscape()

        # Add tabs
        self.tabs.addTab(self.formGroupIntro, _("Intros"))
        self.tabs.addTab(self.formGroupMapstats, _("Mapstats"))

        self.tabs.addTab(self.formGroupMapBox, _("Box Map Icons"))
        self.tabs.addTab(self.formGroupMapLandscape, _("Landscape Map Icons"))

        table = dict()
        table['intro'] = 0
        table['mapstats'] = 1
        table['mapicons_box'] = 2
        table['mapicons_landscape'] = 3
        self.tabs.setCurrentIndex(
            table.get(tab, SubwindowBrowserSources.current_tab))
        self.tabs.currentChanged.connect(self.tabChanged)

    def tabChanged(self, idx):
        SubwindowBrowserSources.current_tab = idx

    def addHotkey(self, ident, label):
        element = HotkeyLayout(
            self, ident, label,
            scctool.settings.config.parser.get("Intros", ident))
        self.hotkeys[ident] = element
        return element

    def connectHotkeys(self):
        for ident, key in self.hotkeys.items():
            if ident == 'hotkey_debug':
                for ident2, key2 in self.hotkeys.items():
                    if ident == ident2:
                        continue
                    key.modified.connect(key2.check_dublicate)
            key.modified.connect(self.hotkeyChanged)

    def hotkeyChanged(self, key, ident):
        self.changed()

        if(ident == 'hotkey_player1' and self.cb_single_hotkey.isChecked()):
            self.hotkeys['hotkey_player2'].setData(
                self.hotkeys['hotkey_player1'].getKey())

        if not key:
            return

        if((ident == 'hotkey_player1' and
            key == self.hotkeys['hotkey_player2'].getKey()['name']) or
           (ident == 'hotkey_player2' and
                key == self.hotkeys['hotkey_player1'].getKey()['name'])):
            self.cb_single_hotkey.setChecked(True)

        if(ident in ['hotkey_player1', 'hotkey_player2'] and
           key == self.hotkeys['hotkey_debug'].getKey()['name']):
            self.hotkeys['hotkey_debug'].clear()

    def singleHotkeyChanged(self):
        checked = self.cb_single_hotkey.isChecked()
        self.hotkeys['hotkey_player2'].setDisabled(checked)
        if checked:
            self.hotkeys['hotkey_player2'].setData(
                self.hotkeys['hotkey_player1'].getKey())
        elif(self.hotkeys['hotkey_player1'].getKey() ==
             self.hotkeys['hotkey_player2'].getKey()):
            self.hotkeys['hotkey_player2'].clear()

    def createFormGroupMapstats(self):
        self.formGroupMapstats = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("General"))
        layout = QFormLayout()

        container = QHBoxLayout()
        self.qb_boxStyle = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/mapstats",
            "mapstats")
        self.qb_boxStyle.connect2WS(self.controller, 'mapstats')
        label = QLabel(_("Style:"))
        label.setMinimumWidth(120)
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/mapstats.html"))
        container.addWidget(self.qb_boxStyle, 2)
        container.addWidget(button, 1)
        layout.addRow(label, container)

        self.cb_mappool = QComboBox()
        self.cb_mappool.addItem(_("Current Ladder Map Pool"))
        self.cb_mappool.addItem(_("Custom Map Pool (defined below)"))
        self.cb_mappool.addItem(_("Currently entered Maps only"))
        self.cb_mappool.setCurrentIndex(
            self.controller.mapstatsManager.getMapPoolType())
        self.cb_mappool.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(_("Map Pool:")), self.cb_mappool)

        self.cb_autoset_map = QCheckBox(_("Select the next map automatically"))
        self.cb_autoset_map.setChecked(
            scctool.settings.config.parser.getboolean(
                "Mapstats", "autoset_next_map"))
        self.cb_autoset_map.stateChanged.connect(self.changed)
        label = QLabel(_("Next Map:"))
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_autoset_map)

        self.cb_mark_played = QCheckBox(_("Mark already played maps"))
        self.cb_mark_played.setChecked(
            scctool.settings.config.parser.getboolean(
                "Mapstats", "mark_played"))
        self.cb_mark_played.stateChanged.connect(self.changed)
        label = QLabel(_("Mark:"))
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_mark_played)

        box.setLayout(layout)
        mainLayout.addWidget(box)

        box = QGroupBox(_("Custom Map Pool"))
        layout = QGridLayout()
        self.maplist = QListWidget()
        self.maplist.setSortingEnabled(True)

        ls = list(self.controller.mapstatsManager.getCustomMapPool())
        self.maplist.addItems(ls)
        self.maplist.setCurrentItem(self.maplist.item(0))

        layout.addWidget(self.maplist, 0, 0, 3, 1)

        qb_add = QPushButton()
        pixmap = QIcon(
            scctool.settings.getResFile('add.png'))
        qb_add.setIcon(pixmap)
        qb_add.clicked.connect(self.addMap)
        layout.addWidget(qb_add, 0, 1)

        qb_remove = QPushButton()
        pixmap = QIcon(
            scctool.settings.getResFile('delete.png'))
        qb_remove.clicked.connect(self.removeMap)
        qb_remove.setIcon(pixmap)
        layout.addWidget(qb_remove, 1, 1)

        self.sc_removeMap = QShortcut(QKeySequence("Del"), self)
        self.sc_removeMap.setAutoRepeat(False)
        self.sc_removeMap.activated.connect(self.removeMap)

        box.setLayout(layout)
        mainLayout.addWidget(box)

        mainLayout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupMapstats.setLayout(mainLayout)

    def addMap(self):
        maplist = list(scctool.settings.maps)
        for i in range(self.maplist.count()):
            map = str(self.maplist.item(i).text())
            if map in maplist:
                maplist.remove(map)

        if len(maplist) > 0:
            map, ok = QInputDialog.getItem(
                self, _('Add Map'),
                _('Please select a map') + ':',
                maplist, editable=False)

            if ok:
                self.__dataChanged = True
                item = QListWidgetItem(map)
                self.maplist.addItem(item)
                self.maplist.setCurrentItem(item)
        else:
            QMessageBox.information(
                self,
                _("No maps available"),
                _('All available maps have already been added.'))

    def removeMap(self):
        item = self.maplist.currentItem()
        if item:
            self.maplist.takeItem(self.maplist.currentRow())
            self.__dataChanged = True

    def createFormGroupMapBox(self):
        self.formGroupMapBox = QWidget()
        mainLayout = QVBoxLayout()
        box = QGroupBox(_("General"))
        layout = QFormLayout()

        container = QHBoxLayout()
        self.qb_boxStyle = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/mapicons_box",
            "mapicons_box")
        self.qb_boxStyle.connect2WS(self.controller, 'mapicons_box')
        label = QLabel(_("Style:"))
        label.setMinimumWidth(120)
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/mapicons_box_1.html"))
        container.addWidget(self.qb_boxStyle, 2)
        container.addWidget(button, 1)
        layout.addRow(label, container)

        self.sb_padding_box = QDoubleSpinBox()
        self.sb_padding_box.setRange(0, 50)
        self.sb_padding_box.setDecimals(1)
        self.sb_padding_box.setValue(
            scctool.settings.config.parser.getfloat("MapIcons", "padding_box"))
        self.sb_padding_box.setSuffix(" " + _("Pixel"))
        self.sb_padding_box.valueChanged.connect(
            lambda x: self.changePadding('box', x))
        layout.addRow(QLabel(
            _("Icon Padding:") + " "), self.sb_padding_box)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        options = self.controller.matchControl.scopes
        self.scope_box = dict()
        for idx in range(0, 3):
            self.scope_box[idx] = ScopeGroupBox(
                _("Icon Set {} Scope".format(idx + 1)),
                options,
                scctool.settings.config.parser.get(
                    "MapIcons", "scope_box_{}".format(idx + 1)),
                self)
            self.scope_box[idx].dataModified.connect(self.changed)
            mainLayout.addWidget(self.scope_box[idx])

        mainLayout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupMapBox.setLayout(mainLayout)

    def createFormGroupMapLandscape(self):
        self.formGroupMapLandscape = QWidget()
        mainLayout = QVBoxLayout()
        box = QGroupBox(_("General"))
        layout = QFormLayout()

        container = QHBoxLayout()
        self.qb_boxStyle = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/mapicons_landscape",
            "mapicons_landscape")
        self.qb_boxStyle.connect2WS(self.controller, 'mapicons_landscape')
        label = QLabel(_("Style:"))
        label.setMinimumWidth(120)
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/mapicons_landscape_1.html"))
        container.addWidget(self.qb_boxStyle, 2)
        container.addWidget(button, 1)
        layout.addRow(label, container)

        self.sb_padding_landscape = QDoubleSpinBox()
        self.sb_padding_landscape.setRange(0, 50)
        self.sb_padding_landscape.setDecimals(1)
        self.sb_padding_landscape.setValue(
            scctool.settings.config.parser.getfloat(
                "MapIcons", "padding_landscape"))
        self.sb_padding_landscape.setSuffix(" " + _("Pixel"))
        self.sb_padding_landscape.valueChanged.connect(
            lambda x: self.changePadding('landscape', x))
        layout.addRow(QLabel(
            _("Icon Padding:") + " "), self.sb_padding_landscape)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        options = self.controller.matchControl.scopes
        self.scope_landscape = dict()
        for idx in range(0, 3):
            self.scope_landscape[idx] = ScopeGroupBox(
                _("Icon Set {} Scope".format(idx + 1)),
                options,
                scctool.settings.config.parser.get(
                    "MapIcons", "scope_landscape_{}".format(idx + 1)),
                self)
            self.scope_landscape[idx].dataModified.connect(self.changed)
            mainLayout.addWidget(self.scope_landscape[idx])

        mainLayout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupMapLandscape.setLayout(mainLayout)

    def createFormGroupIntro(self):
        """Create forms for websocket connection to intro."""
        self.formGroupIntro = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Style"))
        layout = QHBoxLayout()
        styleqb = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/intro",
            "intro")
        styleqb.connect2WS(self.controller, 'intro')
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/intro.html"))
        layout.addWidget(styleqb, 2)
        layout.addWidget(button, 1)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        self.hotkeyBox = QGroupBox(_("Hotkeys"))
        layout = QVBoxLayout()
        self.controller.websocketThread.unregister_hotkeys(force=True)

        self.cb_single_hotkey = QCheckBox(
            _("Use a single hotkey for both players"))
        self.cb_single_hotkey.stateChanged.connect(self.singleHotkeyChanged)
        layout.addWidget(self.cb_single_hotkey)

        self.hotkeys = dict()
        layout.addLayout(self.addHotkey("hotkey_player1", _("Player 1")))
        layout.addLayout(self.addHotkey("hotkey_player2", _("Player 2")))
        layout.addLayout(self.addHotkey("hotkey_debug", _("Debug")))

        self.cb_single_hotkey.setChecked(
            self.hotkeys['hotkey_player1'].getKey() ==
            self.hotkeys['hotkey_player2'].getKey())
        self.connectHotkeys()
        label = QLabel(_("Player 1 is always the player your observer"
                         " camera is centered on at start of a game."))
        layout.addWidget(label)
        self.hotkeyBox.setLayout(layout)
        mainLayout.addWidget(self.hotkeyBox)

        self.introBox = QGroupBox(_("Animation"))
        layout = QFormLayout()
        self.cb_animation = QComboBox()
        animation = scctool.settings.config.parser.get("Intros", "animation")
        currentIdx = 0
        idx = 0
        options = dict()
        options['Fly-In'] = _("Fly-In")
        options['Slide'] = _("Slide")
        options['Fanfare'] = _("Fanfare")
        for key, item in options.items():
            self.cb_animation.addItem(item, key)
            if(key == animation):
                currentIdx = idx
            idx += 1
        self.cb_animation.setCurrentIndex(currentIdx)
        self.cb_animation.currentIndexChanged.connect(self.changed)
        label = QLabel(_("Animation:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_animation)
        self.sb_displaytime = QDoubleSpinBox()
        self.sb_displaytime.setRange(0, 10)
        self.sb_displaytime.setDecimals(1)
        self.sb_displaytime.setValue(
            scctool.settings.config.parser.getfloat("Intros", "display_time"))
        self.sb_displaytime.setSuffix(" " + _("Seconds"))
        self.sb_displaytime.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Display Duration:") + " "), self.sb_displaytime)
        self.sl_sound = QSlider(Qt.Horizontal)
        self.sl_sound.setMinimum(0)
        self.sl_sound.setMaximum(20)
        self.sl_sound.setValue(
            scctool.settings.config.parser.getint("Intros", "sound_volume"))
        self.sl_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_sound.setTickInterval(1)
        self.sl_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Sound Volume:") + " "), self.sl_sound)
        self.introBox.setLayout(layout)
        mainLayout.addWidget(self.introBox)

        self.ttsBox = QGroupBox(_("Text-to-Speech"))
        layout = QFormLayout()

        self.cb_tts_active = QCheckBox()
        self.cb_tts_active.setChecked(
            scctool.settings.config.parser.getboolean("Intros", "tts_active"))
        self.cb_tts_active.stateChanged.connect(self.changed)
        label = QLabel(_("Activate Text-to-Speech:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_tts_active)

        self.icons = {}
        self.icons['MALE'] = QIcon(scctool.settings.getResFile('male.png'))
        self.icons['FEMALE'] = QIcon(scctool.settings.getResFile('female.png'))
        self.cb_tts_voice = QComboBox()

        currentIdx = 0
        idx = 0
        tts_voices = self.controller.tts.getVoices()
        tts_voice = scctool.settings.config.parser.get("Intros", "tts_voice")
        for voice in tts_voices:
            self.cb_tts_voice.addItem(
                self.icons[voice['ssmlGender']],
                '   ' + voice['name'],
                voice['name'])
            if(voice['name'] == tts_voice):
                currentIdx = idx
            idx += 1
        self.cb_tts_voice.setCurrentIndex(currentIdx)
        self.cb_tts_voice.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Voice:") + " "), self.cb_tts_voice)
        self.ttsBox.setStyleSheet("QComboBox { combobox-popup: 0; }")
        self.ttsBox.setLayout(layout)
        mainLayout.addWidget(self.ttsBox)

        self.sb_tts_pitch = QDoubleSpinBox()
        self.sb_tts_pitch.setRange(-20, 20)
        self.sb_tts_pitch.setDecimals(2)
        self.sb_tts_pitch.setValue(
            scctool.settings.config.parser.getfloat("Intros", "tts_pitch"))
        self.sb_tts_pitch.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Pitch:") + " "), self.sb_tts_pitch)

        self.sb_tts_rate = QDoubleSpinBox()
        self.sb_tts_rate.setRange(0.25, 4.00)
        self.sb_tts_rate.setSingleStep(0.1)
        self.sb_tts_rate.setDecimals(2)
        self.sb_tts_rate.setValue(
            scctool.settings.config.parser.getfloat("Intros", "tts_rate"))
        self.sb_tts_rate.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Rate:") + " "), self.sb_tts_rate)

        self.cb_tts_scope = QComboBox()
        scope = scctool.settings.config.parser.get("Intros", "tts_scope")
        currentIdx = 0
        idx = 0
        options = self.controller.tts.getOptions()
        for key, item in options.items():
            self.cb_tts_scope.addItem(item['desc'], key)
            if(key == scope):
                currentIdx = idx
            idx += 1
        self.cb_tts_scope.setCurrentIndex(currentIdx)
        self.cb_tts_scope.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Line:") + " "), self.cb_tts_scope)

        self.sl_tts_sound = QSlider(Qt.Horizontal)
        self.sl_tts_sound.setMinimum(0)
        self.sl_tts_sound.setMaximum(20)
        self.sl_tts_sound.setValue(
            scctool.settings.config.parser.getint("Intros", "tts_volume"))
        self.sl_tts_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_tts_sound.setTickInterval(1)
        self.sl_tts_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Sound Volume:") + " "), self.sl_tts_sound)

        text = _(
            "Text-to-Speech provided by Google-Cloud is paid for "
            "by StarCraft Casting Tool Patrons. To keep this service up "
            "consider becoming a <a href='{patreon}'>Patron</a> yourself. "
            "You can test all voices at {tts}.")

        patreon = 'https://www.patreon.com/StarCraftCastingTool'

        url = 'https://cloud.google.com/text-to-speech/'
        tts = "<a href='{}'>cloud.google.com/text-to-speech</a>"
        tts = tts.format(url)

        label = QLabel(text.format(patreon=patreon, tts=tts))
        label.setAlignment(Qt.AlignJustify)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setMargin(5)
        layout.addRow(label)

        mainLayout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupIntro.setLayout(mainLayout)

    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 as e:
            module_logger.exception("message")

    def changed(self, *values):
        """Handle changed data."""
        self.__dataChanged = True

    def saveData(self):
        """Save the data to config."""
        if(self.__dataChanged):
            self.saveWebsocketdata()

            maps = list()
            for i in range(self.maplist.count()):
                maps.append(str(self.maplist.item(i).text()).strip())

            self.controller.mapstatsManager.setCustomMapPool(maps)
            self.controller.mapstatsManager.setMapPoolType(
                self.cb_mappool.currentIndex())

            scctool.settings.config.parser.set(
                "Mapstats",
                "autoset_next_map",
                str(self.cb_autoset_map.isChecked()))

            scctool.settings.config.parser.set(
                "Mapstats",
                "mark_played",
                str(self.cb_mark_played.isChecked()))

            self.controller.mapstatsManager.sendMapPool()
            self.mainWindow.updateAllMapButtons()

            for idx in range(0, 3):
                scctool.settings.config.parser.set(
                    "MapIcons", "scope_box_{}".format(idx + 1),
                    self.scope_box[idx].getScope())
                scctool.settings.config.parser.set(
                    "MapIcons", "scope_landscape_{}".format(idx + 1),
                    self.scope_landscape[idx].getScope())
            self.controller.matchMetaDataChanged()
            self.__dataChanged = False
            # self.controller.refreshButtonStatus()

    def saveWebsocketdata(self):
        """Save Websocket data."""
        for ident, key in self.hotkeys.items():
            string = scctool.settings.config.dumpHotkey(key.getKey())
            scctool.settings.config.parser.set("Intros", ident, string)
        scctool.settings.config.parser.set(
            "Intros", "display_time", str(self.sb_displaytime.value()))
        scctool.settings.config.parser.set(
            "Intros", "sound_volume", str(self.sl_sound.value()))
        scctool.settings.config.parser.set(
            "Intros", "animation", self.cb_animation.currentData().strip())
        scctool.settings.config.parser.set(
            "Intros", "tts_voice", self.cb_tts_voice.currentData().strip())
        scctool.settings.config.parser.set(
            "Intros", "tts_scope", self.cb_tts_scope.currentData().strip())
        scctool.settings.config.parser.set(
            "Intros", "tts_active", str(self.cb_tts_active.isChecked()))
        scctool.settings.config.parser.set(
            "Intros", "tts_volume", str(self.sl_tts_sound.value()))
        scctool.settings.config.parser.set(
            "Intros", "tts_pitch", str(self.sb_tts_pitch.value()))
        scctool.settings.config.parser.set(
            "Intros", "tts_rate", str(self.sb_tts_rate.value()))

    def openHTML(self, file):
        """Open file in browser."""
        self.controller.openURL(scctool.settings.getAbsPath(file))

    def changePadding(self, scope, padding):
        scctool.settings.config.parser.set(
            "MapIcons", "padding_{}".format(scope),
            str(padding))
        self.controller.websocketThread.changePadding(
            "mapicons_{}".format(scope), padding)

    def saveCloseWindow(self):
        """Save and close window."""
        self.saveData()
        self.closeWindow()

    def closeWindow(self):
        """Close window without save."""
        self.passEvent = True
        self.close()

    def closeEvent(self, event):
        """Handle close event."""
        try:
            if(not self.__dataChanged):
                self.controller.updateHotkeys()
                event.accept()
                return
            if(not self.passEvent):
                if(self.isMinimized()):
                    self.showNormal()
                buttonReply = QMessageBox.question(
                    self, _('Save data?'), _("Do you want to save the data?"),
                    QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No)
                if buttonReply == QMessageBox.Yes:
                    self.saveData()
            self.controller.updateHotkeys()
            event.accept()
        except Exception as e:
            module_logger.exception("message")
Beispiel #48
0
class MainWidget(QFrame):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.initDone = False
        QToolTip.setFont(QFont('SansSerif', 10))
        self.setToolTip('This is a <b>QWidget</b> widget')
        '''
        self.widgetList = QListWidget()
        self.widgetList.insertItem(0, 'device cmd')
        self.widgetList.insertItem(1, 'compile cmd')

        self.layoutHead = QHBoxLayout()
        self.layoutHead.addWidget(self.widgetList)

        self.widgetHead = QFrame()
        self.widgetHead.setLayout(self.layoutHead)
        '''
        #HEAD part
        self.btnIndex1 = QPushButton('&ADB CMD', self)
        self.btnIndex1.setToolTip('Show adb command')
        self.btnIndex1.setChecked(True)
        #self.btnIndex1.toggle()
        self.btnIndex1.clicked.connect(
            lambda: self.onBtnClicked(self.btnIndex1))

        #self.btnIndex1.resize(self.btnIndex1.sizeHint())
        #self.btnIndex1.move(0, 0)

        self.btnIndex2 = QPushButton('&COM CMD', self)
        self.btnIndex2.setToolTip('Show  complie command')
        self.btnIndex1.setChecked(True)
        #self.btnIndex2.resize(self.btnIndex2.sizeHint())
        self.btnIndex2.clicked.connect(
            lambda: self.onBtnClicked(self.btnIndex2))

        self.layoutHead = QHBoxLayout()

        self.layoutHead.addStretch(1)
        self.layoutHead.addWidget(self.btnIndex1)
        self.layoutHead.addStretch(1)
        self.layoutHead.addWidget(self.btnIndex2)
        self.layoutHead.addStretch(30)

        self.widgetHead = QFrame()
        self.widgetHead.setLayout(self.layoutHead)

        #BODY part
        #BODY LEFT
        '''
        self.btnCDM1 = QPushButton('CMD1_INDEX1', self)
        self.btnCDM1.resize(self.btnCDM1.sizeHint())
        self.btnCDM1.clicked.connect(lambda: self.onBtnClicked(self.btnCDM1))


        self.btnCDM2 = QPushButton('CMD2_INDEX1', self)
        self.btnCDM2.resize(self.btnCDM2.sizeHint())
        self.btnCDM2.clicked.connect(lambda: self.onBtnClicked(self.btnCDM2))
        '''
        self.widgetListBodyLeftIndex1 = QListWidget()
        self.widgetListBodyLeftIndex1.insertItem(0, 'index1 cmd1')
        self.widgetListBodyLeftIndex1.insertItem(1, 'index1 cmd2')
        self.widgetListBodyLeftIndex1.setFixedWidth(120)
        self.widgetListBodyLeftIndex1.itemDoubleClicked.connect(
            lambda: self.excuteCMD(1, self.widgetListBodyLeftIndex1.currentRow(
            )))

        self.widgetListBodyLeftIndex2 = QListWidget()
        self.widgetListBodyLeftIndex2.insertItem(0, 'index2 cmd1')
        self.widgetListBodyLeftIndex2.insertItem(1, 'index2 cmd2')
        self.widgetListBodyLeftIndex2.setFixedWidth(120)
        self.widgetListBodyLeftIndex2.itemDoubleClicked.connect(
            lambda: self.excuteCMD(2, self.widgetListBodyLeftIndex2.currentRow(
            )))

        self.widgetBodyLeftLayout = QStackedLayout()
        self.widgetBodyLeftLayout.addWidget(self.widgetListBodyLeftIndex1)
        self.widgetBodyLeftLayout.addWidget(self.widgetListBodyLeftIndex2)

        self.widgetBodyLeft = QFrame()
        self.widgetBodyLeft.setLayout(self.widgetBodyLeftLayout)

        #BODY MID
        self.textBodyMid = QTextEdit()
        self.textBodyMid.setPlainText("This text1")

        self.widgetBodyMidLayout = QVBoxLayout()
        self.widgetBodyMidLayout.addWidget(self.textBodyMid)

        self.widgetBodyMid = QFrame()
        self.widgetBodyMid.setLayout(self.widgetBodyMidLayout)

        #BODY RIGHT
        self.textBodyRight = QTextEdit()
        self.textBodyRight.setPlainText("This text2")

        self.widgetBodyRightLayout = QVBoxLayout()
        self.widgetBodyRightLayout.addWidget(self.textBodyRight)

        self.widgetBodyRight = QFrame()
        self.widgetBodyRight.setLayout(self.widgetBodyRightLayout)

        #BODY ROOT
        self.layoutBody = QHBoxLayout()
        self.layoutBody.addWidget(self.widgetBodyLeft)
        self.layoutBody.addWidget(self.widgetBodyMid)
        self.layoutBody.addWidget(self.widgetBodyRight)

        self.widgetBody = QFrame()
        self.widgetBody.setLayout(self.layoutBody)

        #root
        self.layoutRoot = QVBoxLayout()
        self.layoutRoot.addWidget(self.widgetHead)
        self.layoutRoot.addWidget(self.widgetBody)
        self.setLayout(self.layoutRoot)

        self.setGeometry(1200, 300, 800, 600)
        self.setWindowTitle('MyTool')
        self.initDone = True
        self.show()

    def onBtnClicked(self, btn):
        type = self.getBtnType(btn)

        typeFuntion = {
            self.btnType.INDEX: lambda btn: self.changeCMDList(btn),
            self.btnType.CMD: lambda btn: self.excuteCMD(btn),
        }

        print(type.name)
        typeFuntion[type](btn)

    def getBtnType(self, btn):
        if btn == self.btnIndex1 or btn == self.btnIndex2:
            return self.btnType.INDEX
        elif btn == self.btnCDM1 or btn == self.btnCDM2:
            return self.btnType.CMD
        else:
            return self.btnType.UNKNOWN

    def changeCMDList(self, btn):
        self.textBodyMid.setPlainText("enter " + btn.text())
        if btn == self.btnIndex1:
            self.widgetBodyLeftLayout.setCurrentIndex(0)
        elif btn == self.btnIndex2:
            self.widgetBodyLeftLayout.setCurrentIndex(1)
        else:
            self.widgetBodyLeftLayout.setCurrentIndex(0)

    def excuteCMD(self, i=0, j=0):
        self.textBodyMid.setPlainText("i = " + i.__str__() + "; j = " +
                                      j.__str__())

        if j == 0:
            obj = subprocess.Popen('adb devices', stdout=subprocess.PIPE)
            out = obj.stdout.read()
            obj.stdout.close()
            self.textBodyMid.setPlainText(out.__str__())
        elif j == 1:
            obj = subprocess.Popen('dir', stdout=subprocess.PIPE)
            out = obj.stdout.read()
            obj.stdout.close()
            self.textBodyMid.setPlainText(out.__str__())

    def closeEvent(self, event):

        reply = QMessageBox.question(self, 'Message', "Are you sure to quit?",
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)

        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

    class btnType(Enum):
        UNKNOWN = -1
        INDEX = 0
        CMD = 1
Beispiel #49
0
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.width = 640
        self.height = 400
        self.files = 0

        self.setWindowTitle(__file__)
        # self.setGeometry(10, 10, self.width, self.height)

        inner_layout_1 = QVBoxLayout()
        inner_layout_1.addWidget(QLabel('Выбирай параметр:'))
        self.columns = QListWidget()
        inner_layout_1.addWidget(self.columns)
        button_open = QPushButton('Open')
        button_open.clicked.connect(self.open_files)
        inner_layout_1.addWidget(button_open)
        self.horizontalGroupBox = QGroupBox()
        self.horizontalGroupBox.setLayout(inner_layout_1)

        vertical_lay_2 = QGridLayout()
        vertical_lay_2.addWidget(QLabel('Ось Y:'), 0, 0)
        self.axe_y = QListWidget()
        vertical_lay_2.addWidget(self.axe_y, 1, 0)
        button_add_to_y = QPushButton('Add to Y')
        button_add_to_y.clicked.connect(self.add_to_y, 2, 0)
        vertical_lay_2.addWidget(button_add_to_y)
        button_remove_y = QPushButton('Remove from Y')
        button_remove_y.clicked.connect(self.remove_y)
        vertical_lay_2.addWidget(button_remove_y, 3, 0)
        self.horizontalGroupBox_2 = QGroupBox()
        self.horizontalGroupBox_2.setLayout(vertical_lay_2)

        vertical_lay_3 = QGridLayout()
        vertical_lay_3.addWidget(QLabel('Ось X:'), 0, 0)
        self.axe_x = QListWidget()
        vertical_lay_3.addWidget(self.axe_x, 1, 0)
        button_add_to_x = QPushButton('Add to X')
        button_add_to_x.clicked.connect(self.add_to_x)
        vertical_lay_3.addWidget(button_add_to_x, 2, 0)
        button_remove_x = QPushButton('Remove from X')
        button_remove_x.clicked.connect(self.remove_x)
        vertical_lay_3.addWidget(button_remove_x, 3, 0)
        self.horizontalGroupBox_3 = QGroupBox()
        self.horizontalGroupBox_3.setLayout(vertical_lay_3)

        self.first_huge_lay = QHBoxLayout()
        self.first_huge_lay.addWidget(self.horizontalGroupBox)
        self.first_huge_lay.addWidget(self.horizontalGroupBox_2)
        self.first_huge_lay.addWidget(self.horizontalGroupBox_3)
        self.first_huge_GroupBox = QGroupBox()
        self.first_huge_GroupBox.setLayout(self.first_huge_lay)

        second_vertical_lay = QGridLayout()
        second_vertical_lay.addWidget(QLabel('Количество данных:'), 0, 0)
        self.number_point = QLabel()
        second_vertical_lay.addWidget(self.number_point, 0, 1)

        button_load_data = QPushButton('Загрузить данные')
        button_load_data.clicked.connect(self.load_data)
        second_vertical_lay.addWidget(button_load_data, 0, 2)

        second_vertical_lay.addWidget(
            QLabel('Количество отображаемых данных:'), 3, 0)
        self.number_point_grath = QLabel()
        second_vertical_lay.addWidget(self.number_point_grath, 3, 1)

        list_dot = ['1', '10', '100', '1000']
        self.combobox_dot = QComboBox()
        self.combobox_dot.addItems(list_dot)
        self.combobox_dot.setCurrentIndex(1)
        second_vertical_lay.addWidget(self.combobox_dot, 2, 0)

        button_grath = QPushButton('Построить графики')
        button_grath.clicked.connect(self.plot_grath)
        second_vertical_lay.addWidget(button_grath, 2, 1)

        self.horizontal_2_GroupBox = QGroupBox()
        self.horizontal_2_GroupBox.setLayout(second_vertical_lay)

        windowLayout = QVBoxLayout()
        windowLayout.addWidget(self.first_huge_GroupBox)
        windowLayout.addWidget(self.horizontal_2_GroupBox)
        self.setLayout(windowLayout)

        # self.show()

    def open_files(self):
        # очищаем колонки
        self.columns.clear()
        self.axe_y.clear()
        self.axe_x.clear()

        self.files, _filter = QFileDialog.getOpenFileNames(
            self, 'Выбор данных: ', '', "GZ Files (*.gz) ;; CSV Files (*.csv)")
        try:
            # Определение кодировки
            if self.filename_extension():
                with open(self.files[0], 'rb') as f:
                    raw_data = f.read(20000)
                    self.encoding = chardet.detect(raw_data)['encoding']
                # и разделителя csv
                with open(self.files[0], 'r', encoding=self.encoding) as f:
                    print(f.readline(100))
                    if f.readline(100).count(';'):
                        self.delimiter = ';'
                    else:
                        self.delimiter = '\t'

                # Считывание названия всех колонок
                self.name_column = pd.read_csv(self.files[0],
                                               encoding=self.encoding,
                                               delimiter=self.delimiter,
                                               nrows=0)

                # заполняем колонку ось columns (Выбирай параметр)
                for i, _ in enumerate(self.name_column):
                    self.columns.insertItem(i, _)
            else:
                # Определение кодировки
                with gzip.open(self.files[0], 'rb') as f:
                    raw_data = f.read(20000)
                    self.encoding = chardet.detect(raw_data)['encoding']
                # и разделителя gz
                with gzip.open(self.files[0], 'r') as f:
                    if f.readline(100).decode(self.encoding).count(';'):
                        self.delimiter = ';'
                    else:
                        self.delimiter = '\t'

                # Считывание названия всех колонок
                self.name_column = pd.read_csv(self.files[0],
                                               encoding=self.encoding,
                                               delimiter=self.delimiter,
                                               nrows=0)

                # заполняем колонку ось columns (Выбирай параметр)
                for i, _ in enumerate(self.name_column):
                    self.columns.insertItem(i, _)
        except IndexError as e:
            print('не выбраны данные')

        # по умолчанию на ось columns (Выбирай параметр) добавляем 'time'
        # и тут же ее перемещяем на ось Х
        self.columns.addItem('time, c')
        self.columns.setCurrentRow(self.columns.count() - 1)
        self.axe_x.addItem(self.columns.takeItem(self.columns.currentRow()))
        self.columns.setCurrentRow(0)
        self.axe_x.setCurrentRow(0)

    # определение расширения
    def filename_extension(self):
        """
        true -> file is 'csv;
        false -> file is 'gz;
        :rtype: object
        """
        extension = self.files[0][-3:]
        if extension == 'csv':
            return True
        else:
            return False

    def add_to_x(self):
        self.axe_x.addItem(self.columns.takeItem(self.columns.currentRow()))
        self.axe_x.setCurrentRow(0)

    def remove_x(self):
        self.columns.addItem(self.axe_x.takeItem(self.axe_x.currentRow()))
        self.columns.setCurrentRow(0)

    def add_to_y(self):
        self.axe_y.addItem(self.columns.takeItem(self.columns.currentRow()))
        self.axe_y.setCurrentRow(0)

    def remove_y(self):
        self.columns.addItem(self.axe_y.takeItem(self.axe_y.currentRow()))
        self.columns.setCurrentRow(0)

    def clear_y(self):
        self.axe_y.clear()

    def load_data(self):

        if self.axe_x.count() > 0:
            self.field_x = []
            for _ in range(self.axe_x.count()):
                self.field_x.append(self.axe_x.item(_).text())
            print('Ось Х:', self.field_x)

        if self.axe_y.count() > 0:
            self.field_y = []
            for _ in range(self.axe_y.count()):
                self.field_y.append(self.axe_y.item(_).text())
            print('Ось Y:', self.field_y)

        # Основная загрузка данных (из множества CSV файлов)
        if self.axe_x.count() > 0 and self.axe_y.count() > 0:
            list_ = []
            for file in self.files:
                df = pd.read_csv(file,
                                 header=0,
                                 encoding=self.encoding,
                                 delimiter=self.delimiter,
                                 usecols=self.field_y)
                list_.append(df)

            # only single file
            # df = pd.read_csv(open(self.files[0], 'r'), header=0, delimiter=';', usecols=self.field_y)

            # создаем другой df
            self.df = pd.concat(list_)

            # добавляем колонку time
            self.time_c = 'time, c'
            time_data = []
            summa = 0
            for z in range(len(self.df.index)):
                time_data.append(float('%.2f' % summa))
                summa = summa + 0.01
            self.df[self.time_c] = time_data

            self.number_point.setText(str(len(self.df.index)))
        else:
            print('No data.Ось Y')

        # для токов и мощностей учет отрицательных значений
        name_column = [
            'Электрическая мощность двигателя ЭМП ОЗ ГСМ-А, десятки Вт',
            'Электрическая мощность двигателя ЭМП ОЗ ГСМ-Б, десятки Вт',
            'Ток момента двигателя ЭМП ОЗ ГСМ-А, десятки мА',
            'Ток момента двигателя ЭМП ОЗ ГСМ-Б, десятки мА',
            'Ток статора ЭМП ОЗ ГСМ-А, десятки мА',
            'Ток статора ЭМП ОЗ ГСМ-Б, десятки мА'
        ]
        for _ in self.field_y:
            if _ in name_column:
                self.df[_] = self.df[_].where(lambda x: x < 50000,
                                              lambda x: x - 65536)
                print(_, ' - есть такой')
            else:
                pass
                # print(_, ' - нет такого')

        print('-' * 30)
        print(self.df.info())
        print('-' * 30)

        # TODO

    def plot_grath(self):

        grath = WindowGrath(self.df,
                            self.field_x,
                            self.field_y,
                            step=self.combobox_dot.currentText())
        grath.exec_()
Beispiel #50
0
class LabelerWindow(QMainWindow):
    """A simple GUI to label dataset for classification tasks."""
    def __init__(self, *args, **kwargs):
        super(LabelerWindow, self).__init__(*args, **kwargs)
        self.data = None
        self.current = -1
        self.isLabeling = False
        self.changesSaved = True

        # labels
        self.labels = []
        self.labels_id = [
            'bom', 'maisoumenos', 'ruim', 'empty-hand', 'falso', 'not-sure',
            'cropped-gun', 'handgun', 'rifle', 'shotgun', 'smartphone',
            'wallet', 'card', 'money'
        ]
        self.labels_name = [
            'Bom', '+ ou -', 'Ruim', 'Empty Hand', 'Falso', 'Not sure',
            'Cropped Gun', 'Handgun', 'Rifle', 'Shotgun', 'Smartphone',
            'Wallet', 'Card', 'Money'
        ]
        self.labels_default = [1, 4]  #none

        self.setWindowTitle("Simple Labeler")
        # self.setApplicationDisplayName('Simple Labeler')
        self.setWindowIcon(QIcon('assets/task-icon.png'))
        self.embedToolBar()

        self.mainWidget = QWidget(self)
        self.mainLayout = QVBoxLayout()
        self.mainWidget.setLayout(self.mainLayout)
        self.setCentralWidget(self.mainWidget)
        self.resize(450, 550)

        label = QLabel("Labels")
        label.setAlignment(Qt.AlignCenter)
        self.mainLayout.addWidget(label)

        self.embedCheckBox()

        self.imagesInfo = QLabel("Images")
        self.imagesInfo.setAlignment(Qt.AlignCenter)
        self.mainLayout.addWidget(self.imagesInfo)

        self.embedList()

        self.embedButtons()
        self.embedShortcuts()

    @property
    def haveLabels(self):
        return len(self.labels) > 0

    @property
    def isMultiLabel(self):
        return len(self.labels) > 1

    @property
    def isSingleLabel(self):
        return len(self.labels) == 1

    def embedList(self):

        self.listWidget = QListWidget(self.mainWidget)
        self.listWidget.itemSelectionChanged.connect(self.onSelectItem)

        self.mainLayout.addWidget(self.listWidget)

    def embedButtons(self):
        # image navigation buttons
        layout = QHBoxLayout()

        prev_button = QPushButton(self.mainWidget)
        prev_button.setIcon(QIcon("assets/arrow-prev.png"))
        prev_button.setStatusTip("See previous image")
        prev_button.setShortcut("A")
        prev_button.clicked.connect(self.onPrevImage)
        layout.addWidget(prev_button)

        next_button = QPushButton(self.mainWidget)
        next_button.setIcon(QIcon("assets/arrow-next.png"))
        next_button.setStatusTip("See next image")
        next_button.setShortcut("D")
        next_button.clicked.connect(self.onNextImage)
        layout.addWidget(next_button)

        self.mainLayout.addLayout(layout)

    def embedToolBar(self):
        toolbar = QToolBar("Main Toolbar", self)
        self.addToolBar(toolbar)

        # read
        button_action = QAction(QIcon("assets/database.png"), "Read CSV File",
                                self)
        button_action.setStatusTip("Choose dataset's CSV file")
        button_action.triggered.connect(self.onImportCsv)
        toolbar.addAction(button_action)

        # create
        button_action = QAction(QIcon("assets/database--plus.png"),
                                "Generate CSV File", self)
        button_action.setStatusTip(
            "Choose folder which contains the images, or subfolder with images."
        )
        button_action.triggered.connect(self.onCreateCsv)
        toolbar.addAction(button_action)

        toolbar.addSeparator()

        # stats
        button_action = QAction(QIcon("assets/information-frame.png"),
                                "Show labels information", self)
        button_action.setStatusTip("Show labeled statistics")
        button_action.triggered.connect(self.onShowStats)
        toolbar.addAction(button_action)

        toolbar.addSeparator()

        # write
        button_action = QAction(QIcon("assets/disk.png"), "Save CSV File",
                                self)
        button_action.setStatusTip("Write changes to dataset CSV file")
        button_action.triggered.connect(self.onSaveCsv)
        toolbar.addAction(button_action)

        toolbar.addSeparator()

        self.setStatusBar(QStatusBar(self))

    def embedCheckBox(self):
        col_len = len(self.labels_name) // 2
        layout = QHBoxLayout()
        layoutv = QVBoxLayout()
        self.checkboxes = []

        for label, c in enumerate(self.labels_name):
            if (label % col_len == 0 and label > 0):
                layout.addLayout(layoutv)
                layoutv = QVBoxLayout()

            checkbox = QCheckBox(c, self.mainWidget)
            checkbox.setChecked(label in self.labels)
            # checkbox.stateChanged.connect(self.onCheckboxChange)
            self.checkboxes.append(checkbox)
            layoutv.addWidget(checkbox)
        layout.addLayout(layoutv)

        self.mainLayout.addLayout(layout)

    def embedShortcuts(self):
        shortcuts = [("Q", self.labelBomCropped), ("W", self.labelBomHandgun),
                     ("E", self.labelBomEmpty), ("F", self.label2Falso),
                     ("R", self.label2Ruim), ("V", self.label2SoSo),
                     ("H", self.labelRepeat), ("B", self.labelUnset)]

        for sh_key, sh_fn in shortcuts:
            sh = QShortcut(sh_key, self.mainWidget)
            sh.activated.connect(sh_fn)
            sh.activated.connect(self.refreshCheckboxes)
            sh.activated.connect(self.setLastLabel)

    def setLastLabel(self):
        self.labels_default = self.labels

    def labelRepeat(self):
        self.labels = self.labels_default

    def labelUnset(self):
        self.labels = []

    def labelResetSGM(self):
        for lbl in [0, 1, 2]:
            try:
                del self.labels[self.labels.index(lbl)]
            except ValueError as e:
                pass

    def labelBomCropped(self):
        self.labels = [0, 6]
        # self.processLabels()

    def labelBomHandgun(self):
        self.labels = [0, 7]
        # self.processLabels()

    def labelBomEmpty(self):
        self.labels = [0, 3]
        # self.processLabels()

    def label2Falso(self):
        self.labels = [1, 4]

    def label2Ruim(self):
        self.labelResetSGM()
        self.labels.append(2)

    def label2SoSo(self):
        self.labelResetSGM()
        self.labels.append(1)

    def onImportCsv(self, s):
        dialog = QFileDialog.getOpenFileName(self, 'Open file', '',
                                             "CSV files (*.csv)")

        if os.path.isfile(dialog[0]):
            # reset if was labeling
            if self.isLabeling:
                self.stopLabeling()
            # load data
            self.data = DataHandler(dialog[0])
            # start labeling
            self.startLabeling()

    def onCreateCsv(self, s):
        main_folder = str(
            QFileDialog.getExistingDirectory(self, "Choose dataset directory"))

        # if there is no folder, do nothing
        if not os.path.isdir(main_folder):
            return False

        optDialog = CsvNameDialog()

        # get desired filename and if images are organized from user
        if optDialog.exec_() == CsvNameDialog.Accepted:
            filename, have_labels = optDialog.getValues()
        else:
            # if cancel, do nothing
            return False

        # reset if was labeling
        if self.isLabeling:
            self.stopLabeling()

        # initialize data handler
        self.data = DataHandler(os.path.join(main_folder, filename))
        self.data.create(have_labels)

        if len(self.data) > 0:
            message = "{} images found, save to file?".format(len(self.data))
            title = "Images found"
        else:
            message = "No image were found!"
            title = "No images in folder"

        dlg = MessageDialog(title, message)

        if dlg.exec_():
            # save csv
            self.data.save()
            # start labeling
            self.startLabeling()
        else:
            self.data = None

    def onShowStats(self):
        stats = self.data.get_stats()
        stats_str = ""
        for key, val in stats:
            key = '_'.join(key) if type(key) is list else key
            stats_str += f"{key} = {val}\n"

        dlg = MessageDialog('Label stats', stats_str)
        dlg.exec_()

    def onSaveCsv(self, button):
        self.changesSaved = True
        self.data.save()

    def onNextImage(self, s):
        if self.current + 1 < len(self.data):
            self.listWidget.setCurrentRow(self.current + 1)

    def onPrevImage(self, s):
        if self.current >= 1:
            self.listWidget.setCurrentRow(self.current - 1)

    def onSelectItem(self):
        if self.current > -1:
            self.processLabels()
            # update labels
            self.updateItem()
        self.current = self.listWidget.currentRow()
        self.processImage()

    def startLabeling(self):
        # init data and pointer
        self.data.read()
        self.current = -1
        # populate list for selection
        self.populateList()
        # initiate image visor
        cv2.namedWindow("Image", cv2.WINDOW_NORMAL)
        # process first image
        self.listWidget.setCurrentRow(0)
        self.changesSaved = True
        self.isLabeling = True

    def stopLabeling(self):
        self.launchSaveChanges()
        # images presenter and info
        self.imagesInfo.setText("Images")
        self.listWidget.clear()
        cv2.destroyAllWindows()
        # reset data
        self.data = None
        self.labels = []
        self.isLabeling = False

    def parseLabel(self, label_name):
        label = []

        def label_idx(l):
            if l in self.labels_id:
                return self.labels_id.index(l)
            else:
                return l

        if isinstance(label_name, str) and '[' in label_name:  # is a list
            label_name = ast.literal_eval(label_name)

        if isinstance(label_name, list):
            label = []
            for l in label_name:
                label.append(label_idx(l))
        else:
            label.append(label_idx(label_name))

        return label

    def id2label(self, label_id):
        labels = None

        def label_name(label):
            if isinstance(label, int):
                return self.labels_id[label]
            elif isinstance(label, str) or label.isNull():
                return "Undefined: {}".format(label)

        if isinstance(label_id, list):
            labels = []
            for i in label_id:
                labels.append(label_name(i))
        else:
            labels = label_name(label_id)

        # reduce to single label
        if len(labels) == 1:
            return labels[0]
        else:
            return labels

    def row2text(self, idx):
        impath, label = self.data[idx]
        labels = self.parseLabel(label)
        item_class = self.id2label(labels)
        item_text = "{} -> label: {}".format(impath, item_class)
        return item_text

    def populateList(self):
        for i in range(len(self.data)):
            item = self.row2text(i)
            self.listWidget.addItem(item)

    def updateItem(self):
        itemText = self.row2text(self.current)
        item = self.listWidget.item(self.current)

        if item.text() != itemText:
            self.changesSaved = False
            item.setText(itemText)

    def refreshCheckboxes(self):
        #        if self.haveLabels:
        for label, checkbox in enumerate(self.checkboxes):
            if label in self.labels:
                checkbox.setChecked(True)
            else:
                checkbox.setChecked(False)

    def getValidLabels(self):
        self.labels = [
            i for i, c in enumerate(self.checkboxes) if c.isChecked()
        ]

        # if not self.haveLabels:
        # if type(self.labels_default) is list:
        # self.labels.extend(self.labels_default)
        # else:
        # self.labels.append(self.labels_default)

        return self.labels

    def processLabels(self):
        self.getValidLabels()
        self.refreshCheckboxes()
        out_labels = []

        if self.isMultiLabel:  #multi label
            for l in self.labels:
                out_labels.append(self.labels_id[l])
        elif self.isSingleLabel:
            out_labels = self.labels_id[self.labels[0]]

        # self.dataset.iloc[self.current]['class'] = out_labels
        self.data[self.current] = out_labels if len(out_labels) > 0 else \
            'unset'

    def updateImageInfo(self):
        image_summ = "Image Nro. {} of {}".format(self.current + 1,
                                                  len(self.data))
        self.imagesInfo.setText(image_summ)

    def showImage(self):
        img_path, label = self.data[self.current]

        # idx = self.current
        # row = self.dataset.iloc[idx]
        impath = os.path.join(self.data.root_path, img_path)
        text_height = 20
        self.labels = self.parseLabel(label)

        # check if image exist
        if not os.path.isfile(impath):
            print('Error!', impath, 'Not Found!')
            return False

        img = cv2.imread(impath)
        #define the screen resulation
        screen_res = 800, 600
        # must resize?
        # if img.shape[1] > screen_res[0] or img.shape[0] > screen_res[1]:
        scale_width = screen_res[0] / img.shape[1]
        scale_height = screen_res[1] / img.shape[0]
        scale = min(scale_width, scale_height)

        #resized window width and height
        window_width = int(img.shape[1] * scale)
        window_height = int(img.shape[0] * scale)

        #resize the window according to the screen resolution
        cv2.resizeWindow("Image", window_width, window_height)

        # draw labels
        if self.haveLabels:
            for i, l in enumerate(self.labels):
                if not isinstance(l, int):
                    continue
                y_pos = text_height + (text_height * i) + (5 * i)
                cv2.putText(img,
                            text=self.labels_name[l],
                            org=(3, y_pos),
                            fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                            fontScale=0.70,
                            color=(255, 0, 0),
                            thickness=2)

        cv2.imshow("Image", img)

    def processImage(self):
        self.updateImageInfo()
        self.showImage()
        self.refreshCheckboxes()

    def launchSaveChanges(self):
        if not self.changesSaved:
            saveDialog = MessageDialog(
                'Not saved changes!',
                'Some labeled images were not saved, do you want to save the changes?'
            )
            if saveDialog.exec_():
                # save csv
                self.data.save()

    def closeEvent(self, event):
        # stop process
        self.stopLabeling()
        event.accept()  # let the window close
Beispiel #51
0
class Lookup(QWidget):
    MODEL_CLASS = None

    def __init__(self, parent, model):
        QWidget.__init__(self, parent, Qt.Window)
        self.model = model
        self.model.view = self
        self._setupUi()

        self.searchEdit.searchChanged.connect(self.searchChanged)
        self.searchEdit.returnPressed.connect(self.returnPressed)
        self.namesList.currentRowChanged.connect(self.currentRowChanged)
        self.namesList.itemDoubleClicked.connect(self.itemDoubleClicked)
        self._shortcutUp.activated.connect(self.upPressed)
        self._shortcutDown.activated.connect(self.downPressed)

    def _setupUi(self):
        self.setWindowTitle(tr("Lookup"))
        self.resize(314, 331)
        self.verticalLayout = QVBoxLayout(self)
        self.searchEdit = SearchEdit(self)
        self.verticalLayout.addWidget(self.searchEdit)
        self.namesList = QListWidget(self)
        self.namesList.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.namesList.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.namesList.setUniformItemSizes(True)
        self.namesList.setSelectionRectVisible(True)
        self.verticalLayout.addWidget(self.namesList)

        self.searchEdit.immediate = True
        self._shortcutUp = QShortcut(self.searchEdit)
        self._shortcutUp.setKey(QKeySequence(Qt.Key_Up))
        self._shortcutUp.setContext(Qt.WidgetShortcut)
        self._shortcutDown = QShortcut(self.searchEdit)
        self._shortcutDown.setKey(QKeySequence(Qt.Key_Down))
        self._shortcutDown.setContext(Qt.WidgetShortcut)

    def _restoreSelection(self):
        self.namesList.setCurrentRow(self.model.selected_index)

    #--- Event Handlers
    def returnPressed(self):
        self.model.go()

    def searchChanged(self):
        self.model.search_query = str(self.searchEdit.text())

    def currentRowChanged(self, row):
        if row >= 0:
            self.model.selected_index = row

    def itemDoubleClicked(self, item):
        self.model.go()

    def upPressed(self):
        if self.namesList.currentRow() > 0:
            self.namesList.setCurrentRow(self.namesList.currentRow()-1)

    def downPressed(self):
        if self.namesList.currentRow() < self.namesList.count()-1:
            self.namesList.setCurrentRow(self.namesList.currentRow()+1)

    #--- model --> view
    def refresh(self):
        self.namesList.clear()
        self.namesList.addItems(self.model.names)
        self._restoreSelection()
        self.searchEdit.setText(self.model.search_query)

    def show(self):
        QWidget.show(self)
        self.searchEdit.setFocus()
        # see csv_options
        self.raise_()

    def hide(self):
        QWidget.hide(self)
class MySimpleList(QGroupBox):

    def __init__(self):
        super().__init__()
        self.setTitle('Simple List Widget')
        self.init_ui()

    def init_ui(self):
        self.list = QListWidget()
        self.list.setSelectionMode(QListWidget.MultiSelection)

        # add Layout
        layout_add = QHBoxLayout()
        self.line_add_edit = QLineEdit('Add Item')
        add_btn = QPushButton('add')
        layout_add.addWidget(self.line_add_edit)
        layout_add.addWidget(add_btn)

        # insert to position layout
        layout_insert = QHBoxLayout()
        self.line_insert_position_edit = QLineEdit('Insert Item')
        self.spinbox_pos = QSpinBox()
        print(self.list.count())
        self.spinbox_pos.setRange(0, self.list.count())  # list항목이 추가되면 max도 변경되도록 추가할 것
        insert_btn = QPushButton('Insert')
        layout_insert.addWidget(self.line_insert_position_edit)
        layout_insert.addWidget(self.spinbox_pos)
        layout_insert.addWidget(insert_btn)

        print_btn = QPushButton('Print')
        print_multi_btn = QPushButton('Print Multi')
        remove_btn = QPushButton('Current Item Remove')
        clear_btn = QPushButton('Clear')

        # connect
        add_btn.clicked.connect(self.add_item)
        insert_btn.clicked.connect(self.insert_pos_item)

        print_btn.clicked.connect(lambda: print(self.list.currentItem().text()))
        print_multi_btn.clicked.connect(self.print_multi_items)
        remove_btn.clicked.connect(self.remove_current_item)
        clear_btn.clicked.connect(lambda: self.list.clear())

        layout_btns = QHBoxLayout()
        layout_btns.addWidget(print_btn)
        layout_btns.addWidget(print_multi_btn)
        layout_btns.addWidget(remove_btn)
        layout_btns.addWidget(clear_btn)

        layout = QVBoxLayout()
        layout.addWidget(self.list)
        layout.addLayout(layout_add)
        layout.addLayout(layout_insert)
        layout.addLayout(layout_btns)

        self.setLayout(layout)

    def add_item(self):
        print('add_item')
        self.list.addItem(self.line_add_edit.text())
        self.spinbox_pos.setMaximum(self.list.count())

    def insert_pos_item(self):
        item = self.line_insert_position_edit.text()
        row = self.spinbox_pos.value()
        self.list.insertItem(row, item)
        self.spinbox_pos.setMaximum(self.list.count())

    def print_multi_items(self):
        for i in self.list.selectedItems():
            print(i.text())

    def remove_current_item(self):
        selectedRow = self.list.currentRow()
        removeItem = self.list.takeItem(selectedRow)
        print(removeItem.text(), ' 삭제')
Beispiel #53
0
class MainWindow(QMainWindow):  #Main window of the Serie Browser
    def __init__(self, series_list, nameWindow):
        # series_list = list of series to be displayed and retrieved from the API,
        # nameWindow = name to be displayed on top of the indow

        super().__init__()
        self.__nDisplay = len(series_list)  # Number of series displayed
        self.__seriesList = series_list  # List of series displayed at first

        # Load .ui designed on Qt
        self.__UI = uic.loadUi('main.ui', self)

        # Show window on desktop
        self.showMaximized()

        # Name of the window
        self.__textLabel = QLabel(nameWindow)
        self.__textLabel.setText(nameWindow)
        self.__textLabel.setTextFormat(QtCore.Qt.RichText)
        self.__textLabel.setText(
            "<span style=' font-size:16pt; font-weight:600; color:#aa0000;'>" +
            nameWindow + "</span>")
        self.__UI.horizontalLayout.addWidget(self.__textLabel)

        # Define a Scroll area for serie display
        self.__serieWind = QWidget()  # Create a widget for the scroll area
        self.__scrollArea = QScrollArea()  # I Create a Scroll Area
        self.__scrollArea.setWidgetResizable(True)
        self.__scrollArea.setWidget(
            self.__serieWind)  # Insert the scroll area in the widget
        self.__gridLayout = QGridLayout(
        )  # Create a grid layout for the scroll area
        self.__serieWind.setLayout(
            self.__gridLayout)  # Insert the grid layout in the scroll area
        self.__UI.horizontalLayout_2.addWidget(
            self.__scrollArea
        )  # Insert the scroll area in the main horizontal layout from .ui

        self.__serieWind.setObjectName("serieWind")
        self.setStyleSheet("#serieWind{background-color: black;}"
                           )  # Define color of the scroll area

        #  Add research bar
        self.__searchWidget = QLineEdit()
        self.__searchWidget.setFixedSize(150, 40)
        self.__searchWidget.returnPressed.connect(
            self.slot_research
        )  # Connect the signal return pressed to slot_research
        self.__UI.horizontalLayout.addWidget(
            self.__searchWidget)  # Insert research bar in layout from .ui

        #  Add research button
        self.__researchButton = QPushButton("Search")
        self.__researchButton.setFixedSize(100, 40)
        self.__researchButton.pressed.connect(
            self.slot_research)  # Connect the signal pressed to slot_research
        self.__UI.horizontalLayout.addWidget(
            self.__researchButton
        )  # Insert research button in layout from .ui  # Add favourites title

        # Favourites Layout
        self.__favLayout = QVBoxLayout()
        self.__UI.horizontalLayout_2.addLayout(self.__favLayout)

        # Favourites Title
        self.__favouritesTitle = QLabel("Favourites")
        self.__favouritesTitle.setText(
            "<span style=' font-size:16pt; font-weight:600; color:#aa0000;'> Favourites </span>"
        )
        self.__favLayout.addWidget(self.__favouritesTitle)

        #  Add favourites list to the layout with a QListWidget
        self.__favouritesWidget = QListWidget()
        self.__favouritesWidget.setMaximumWidth(250)
        self.__favLayout.addWidget(self.__favouritesWidget)
        self.__favButtonsTopLayout = QHBoxLayout()
        self.__favButtonsBottomLayout = QHBoxLayout()
        self.__favLayout.addLayout(self.__favButtonsTopLayout)
        self.__favLayout.addLayout(self.__favButtonsBottomLayout)

        #  Add More Info button for favourites list
        self.__favMoreInfoButton = QPushButton("More Info")
        self.__favButtonsTopLayout.addWidget(self.__favMoreInfoButton)
        self.__favMoreInfoButton.clicked.connect(
            self.slot_open_serie_window
        )  # Connect clicked signal of MoreInfo button to slot_open_serie_window

        #  Add Remove favourite button for favourites list
        self.__removeFavButton = QPushButton("Remove Favourite")
        self.__favButtonsTopLayout.addWidget(self.__removeFavButton)
        self.__removeFavButton.clicked.connect(
            self.slot_remove_favourite
        )  # Connect clicked signal to Remove button to slot_remove_favourites

        # Magic Button : finds a serie that has an episode that is going to be aired soon
        self.__magicButton = QPushButton("MAGIC BUTTON")
        self.__magicButton.clicked.connect(self.slot_magic_add_to_favourites)
        self.__favButtonsBottomLayout.addWidget(self.__magicButton)

        # Clear Favourites Button
        self.__clearFavButton = QPushButton("Clear Favourites")
        self.__favButtonsBottomLayout.addWidget(self.__clearFavButton)
        self.__clearFavButton.clicked.connect(self.slot_clear_favourites)

        # Notifications checkbox
        self.__enableNotifications = QCheckBox()
        self.__enableNotifications.setText("Enable Notifications")
        self.__enableNotifications.setChecked(True)
        self.__enableNotifications.stateChanged.connect(
            self.slot_change_notifications_state)
        self.__favLayout.addWidget(self.__enableNotifications)

        #Create and load favourites list
        self.__favouritesIDList = []  # Creation of a ID list of favourites
        self.__favouriteSeries = [
        ]  # Creation of list of favourites of class Serie
        self.__fileName = "favourites"  #Creation of a file for pickler
        if (os.path.exists(self.__fileName)) and (os.path.getsize(
                self.__fileName) > 0):  #Check if the file exists
            with open(self.__fileName, "rb") as favFile:
                depickler = pickle.Unpickler(favFile)
                self.__favouriteSeries = depickler.load(
                )  #Loading previously saved favourites from file
                for i in range(
                        len(self.__favouriteSeries)
                ):  #Loop to add favourites series to favourite QWidgetList and create favouritesIDList
                    favItem = self.__favouriteSeries[i].name
                    self.__favouritesWidget.addItem(favItem)
                    self.__favouritesIDList += [self.__favouriteSeries[i].id]

        #Alert
        self.__alertWindow = Afficher(self.__favouriteSeries,
                                      self.__enableNotifications.isChecked(),
                                      self)
        self.__alertWindow.start()

        # Signal Mapper to connect slot_add_to_favourites to class MainWidget
        self.__sigMapper = QSignalMapper(self)
        self.__sigMapper.mapped.connect(self.slot_add_to_favourites)

        # Display series MainWidgets on MainWindow
        self.__numberSeriesWidgetLines = ceil(self.__nDisplay /
                                              5)  # 5 widgets per line maximum
        self.__positions = [(i + 1, j)
                            for i in range(self.__numberSeriesWidgetLines)
                            for j in range(5)
                            ]  # Define positions for the grid layout
        self.__seriesWidgetList = []
        for i in range(len(self.__seriesList)
                       ):  #Loop for creation and display of serie MainWidgets
            currentWidget = MainWidget(i, self.__seriesList[i])
            self.__seriesWidgetList += [currentWidget]
            self.__gridLayout.addWidget(currentWidget, *self.__positions[i])
            i += 1
            self.__sigMapper.setMapping(currentWidget.favButton,
                                        currentWidget.id)
            currentWidget.favButton.clicked.connect(
                self.__sigMapper.map
            )  #Connect add to favourite button of MainWidget to signal mapper

    # Getters and setters for seriesList
    @property
    def seriesList(self):
        return self.__seriesList

    @seriesList.setter
    def seriesList(self, newSeriesList):
        self.__seriesList = newSeriesList

    # Methods
    # Slot to add a favourite to the QListWidget
    def slot_add_to_favourites(self,
                               id):  #id = id of the serie to add to favourites
        if (id not in self.__favouritesIDList
            ):  #Check if the serie is already in the favourite
            self.__favouritesIDList += [id]
            serie = searchSerie(id)
            nm = serie.name
            self.__favouriteSeries += [serie]
            self.__favouritesWidget.addItem(nm)
            self.__alertWindow.quit()
            self.__alertWindow = Afficher(
                self.__favouriteSeries, self.__enableNotifications.isChecked(),
                self)
            self.__alertWindow.start()
            with open(self.__fileName, "wb") as favFile:
                pickler = pickle.Pickler(favFile)
                pickler.dump(
                    self.__favouriteSeries)  # Saving refreshed favourites list
        else:  # If the serie is already in the favourites, displaying an error message
            error_dialog = QMessageBox.information(None, "Error",
                                                   "Favourite already added.",
                                                   QMessageBox.Cancel)

    # The slot that adds a serie that has a soon-to-be-aired episode to favourites
    def slot_magic_add_to_favourites(self):
        serie = findForthcomingSerie(self.__favouritesIDList)
        id = serie.id
        self.__favouritesIDList += [id]
        nm = serie.name
        self.__favouriteSeries += [serie]
        self.__favouritesWidget.addItem(nm)
        self.__alertWindow.quit()
        self.__alertWindow = Afficher(self.__favouriteSeries,
                                      self.__enableNotifications.isChecked(),
                                      self)
        self.__alertWindow.start()
        with open(self.__fileName, "wb") as favFile:
            pickler = pickle.Pickler(favFile)
            pickler.dump(
                self.__favouriteSeries)  # Saving refreshed favourites list

    # Slot to remove a serie from user's favourites list
    def slot_remove_favourite(self):
        idx = self.__favouritesWidget.currentRow(
        )  # The index of the selected row
        if (
                idx == -1
        ):  # Exception if the user didn't select a favourite before clicking "remove favourite" button
            QMessageBox.information(None, "Error",
                                    "You didn't select a favourite.",
                                    QMessageBox.Ok)
        else:
            del self.__favouriteSeries[
                idx]  # The favourites widget list and the inner user's favourites list are sorted in the same order
            del self.__favouritesIDList[idx]
            self.__favouritesWidget.takeItem(idx)
            self.__alertWindow.quit()
            self.__alertWindow = Afficher(
                self.__favouriteSeries, self.__enableNotifications.isChecked(),
                self)
            self.__alertWindow.start()
            with open(self.__fileName, "wb") as favFile:
                pickler = pickle.Pickler(favFile)
                pickler.dump(
                    self.__favouriteSeries)  # Saving refreshed favourites list

    # Slot that clears user's favourites list
    def slot_clear_favourites(self):
        self.__favouriteSeries = []
        self.__favouritesIDList = []
        self.__favouritesWidget.clear()
        self.__alertWindow.quit()
        self.__alertWindow = Afficher(self.__favouriteSeries,
                                      self.__enableNotifications.isChecked(),
                                      self)
        self.__alertWindow.start()
        # Clear favourites file
        with open(self.__fileName, "wb") as favFile:
            pickler = pickle.Pickler(favFile)
            pickler.dump(
                self.__favouriteSeries)  # Saving refreshed favourites list

    # Slot to open window with more information for favourites
    def slot_open_serie_window(self):
        idx = self.__favouritesWidget.currentRow()
        try:
            if idx == -1:
                raise ValueError("No row is selected in favourites widget.")
            else:
                ser = self.__favouriteSeries[idx]
                self.__newWindow = NewWindow(ser, self)
                self.__newWindow.exec_()
        except ValueError:  # If the user hasn't selected an element from the list, currentRow returns -1 and an error dialog is displayed
            QMessageBox.information(None, "Error",
                                    "You didn't select a serie in your list.",
                                    QMessageBox.Cancel)

    # Slot to do the research
    def slot_research(self):
        self.__searchText = self.__searchWidget.text()

        # Delete all the widgets displayed in scroll area
        for i in reversed(range(self.__gridLayout.count())):
            self.__gridLayout.itemAt(i).widget().setParent(None)

        # Research on the API
        self.__seriesList = searchSeries(self.__searchText)

        # Add results of the research to the scroll area
        for i in range(len(self.__seriesList)):
            currentWidget = MainWidget(i, self.__seriesList[i])
            self.__gridLayout.addWidget(currentWidget, *self.__positions[i])
            self.__sigMapper.setMapping(currentWidget.favButton,
                                        currentWidget.id)
            currentWidget.favButton.clicked.connect(self.__sigMapper.map)

    # A slot that is activated when the notifications checkbox is checked or unchecked. It starts and stops the notifications thread
    def slot_change_notifications_state(self):
        if (self.__alertWindow.notificationsEnabled):
            self.__alertWindow.notificationsEnabled = False
            self.__alertWindow.quit()
        else:
            self.__alertWindow.notificationsEnabled = True
            self.__alertWindow = Afficher(
                self.__favouriteSeries, self.__enableNotifications.isChecked(),
                self)
            self.__alertWindow.start()
Beispiel #54
0
class Ui_Settings(object):
    def __init__(self, parent=None):
        self.set_data()
        self.widget = QtWidgets.QWidget()
        self.setupUi(self.widget)
        self.set_connect()

    def set_data(self):
        self.del_missions = set()  # 需要被去除的的任务配置

    def set_connect(self):
        self.btn_save.clicked.connect(self.save)
        self.btn_cancel.clicked.connect(self.cancel)
        self.btn_add.clicked.connect(self.add_item)
        self.btn_del.clicked.connect(self.del_item)

    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(600, 400)
        self.verticalLayoutWidget = QWidget(Form)
        self.verticalLayoutWidget.setGeometry(
            QtCore.QRect(10, 10,
                         Form.width() - 20,
                         Form.height() - 20))
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")

        self.listWidget = QListWidget(self.verticalLayoutWidget)
        self.listWidget.setObjectName("listWidget")
        self.listWidget.setFixedWidth(self.verticalLayoutWidget.width())
        for mission_id in TIME_MISSION.missions:
            self.item = TimeItem(self.listWidget)
            self.listWidget.setItemWidget(
                self.item,
                self.item.setupUi(info=TIME_MISSION.missions[mission_id]))
        self.verticalLayout.addWidget(self.listWidget)
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.horizontalLayout.addStretch(1)
        self.btn_add = QPushButton(self.verticalLayoutWidget)
        self.btn_add.setObjectName("btn_add")
        self.horizontalLayout.addWidget(self.btn_add)
        self.btn_del = QPushButton(self.verticalLayoutWidget)
        self.btn_del.setObjectName("btn_del")
        self.horizontalLayout.addWidget(self.btn_del)
        self.btn_save = QPushButton(self.verticalLayoutWidget)
        self.btn_save.setObjectName("btn_save")
        self.horizontalLayout.addWidget(self.btn_save)
        self.btn_cancel = QPushButton(self.verticalLayoutWidget)
        self.btn_cancel.setObjectName("btn_cancel")
        self.horizontalLayout.addWidget(self.btn_cancel)
        self.verticalLayout.addLayout(self.horizontalLayout)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)
        self.widget = Form

    def refreash_items(self, is_save):
        if is_save:
            for i in range(self.listWidget.count()):
                item = self.listWidget.item(i)
                flag = item.update_info()
                TIME_MISSION.update_mission(item.info, flag)
            for mission_id in self.del_missions:
                TIME_MISSION.del_mission(mission_id)
            self.del_missions.clear()
        else:
            self.listWidget.clear()
            for mission_id in TIME_MISSION.missions:
                self.item = TimeItem(self.listWidget)
                self.listWidget.setItemWidget(
                    self.item,
                    self.item.setupUi(info=TIME_MISSION.missions[mission_id]))

    def save(self):
        self.btn_save.setDisabled(True)
        self.refreash_items(True)
        self.btn_save.setEnabled(True)
        self.widget.hide()

    def cancel(self):
        self.refreash_items(False)
        self.widget.hide()

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Settings"))
        self.btn_add.setText(_translate("Form", "add"))
        self.btn_del.setText(_translate("Form", "del"))
        self.btn_save.setText(_translate("Form", "save"))
        self.btn_cancel.setText(_translate("Form", "cancel"))

    def add_item(self, mission=None):
        item = TimeItem(self.listWidget)
        if mission:
            self.listWidget.setItemWidget(item, item.setupUi(info=mission))
        else:
            self.listWidget.setItemWidget(item, item.setupUi())

    def del_item(self):
        cur_index = self.listWidget.currentRow()
        if -1 != cur_index:
            try:
                self.del_missions.add(self.listWidget.currentItem().info['id'])
                self.listWidget.takeItem(cur_index)
                self.listWidget.setCurrentRow(-1)
            except Exception as e:
                print(e)
        else:
            Toast(self.widget).show_toast('未选中item', 2000)

    def show(self):
        self.widget.show()

    def dev_later(self):
        Toast(self.widget).show_toast('功能正在开发中,不要着急先喝杯咖啡', 2000)
Beispiel #55
0
class Listspace(QSplitter, ViewManager):
    """
    Class implementing the listspace viewmanager class.
    
    @signal changeCaption(str) emitted if a change of the caption is necessary
    @signal editorChanged(str) emitted when the current editor has changed
    @signal editorChangedEd(Editor) emitted when the current editor has changed
    @signal lastEditorClosed() emitted after the last editor window was closed
    @signal editorOpened(str) emitted after an editor window was opened
    @signal editorOpenedEd(Editor) emitted after an editor window was opened
    @signal editorClosed(str) emitted just before an editor window gets closed
    @signal editorClosedEd(Editor) emitted just before an editor window gets
        closed
    @signal editorRenamed(str) emitted after an editor was renamed
    @signal editorRenamedEd(Editor) emitted after an editor was renamed
    @signal editorSaved(str) emitted after an editor window was saved
    @signal editorSavedEd(Editor) emitted after an editor window was saved
    @signal checkActions(Editor) emitted when some actions should be checked
        for their status
    @signal cursorChanged(Editor) emitted after the cursor position of the
        active window has changed
    @signal breakpointToggled(Editor) emitted when a breakpoint is toggled.
    @signal bookmarkToggled(Editor) emitted when a bookmark is toggled.
    @signal syntaxerrorToggled(Editor) emitted when a syntax error is toggled.
    @signal previewStateChanged(bool) emitted to signal a change in the
        preview state
    @signal editorLanguageChanged(Editor) emitted to signal a change of an
        editors language
    @signal editorTextChanged(Editor) emitted to signal a change of an
        editor's text
    @signal editorLineChanged(str,int) emitted to signal a change of an
        editor's current line (line is given one based)
    """
    changeCaption = pyqtSignal(str)
    editorChanged = pyqtSignal(str)
    editorChangedEd = pyqtSignal(Editor)
    lastEditorClosed = pyqtSignal()
    editorOpened = pyqtSignal(str)
    editorOpenedEd = pyqtSignal(Editor)
    editorClosed = pyqtSignal(str)
    editorClosedEd = pyqtSignal(Editor)
    editorRenamed = pyqtSignal(str)
    editorRenamedEd = pyqtSignal(Editor)
    editorSaved = pyqtSignal(str)
    editorSavedEd = pyqtSignal(Editor)
    checkActions = pyqtSignal(Editor)
    cursorChanged = pyqtSignal(Editor)
    breakpointToggled = pyqtSignal(Editor)
    bookmarkToggled = pyqtSignal(Editor)
    syntaxerrorToggled = pyqtSignal(Editor)
    previewStateChanged = pyqtSignal(bool)
    editorLanguageChanged = pyqtSignal(Editor)
    editorTextChanged = pyqtSignal(Editor)
    editorLineChanged = pyqtSignal(str, int)

    def __init__(self, parent):
        """
        Constructor
        
        @param parent parent widget (QWidget)
        """
        self.stacks = []

        QSplitter.__init__(self, parent)
        ViewManager.__init__(self)
        self.setChildrenCollapsible(False)

        self.viewlist = QListWidget(self)
        policy = self.viewlist.sizePolicy()
        policy.setHorizontalPolicy(QSizePolicy.Ignored)
        self.viewlist.setSizePolicy(policy)
        self.addWidget(self.viewlist)
        self.viewlist.setContextMenuPolicy(Qt.CustomContextMenu)
        self.viewlist.currentRowChanged.connect(self.__showSelectedView)
        self.viewlist.customContextMenuRequested.connect(self.__showMenu)

        self.stackArea = QSplitter(self)
        self.stackArea.setChildrenCollapsible(False)
        self.addWidget(self.stackArea)
        self.stackArea.setOrientation(Qt.Vertical)
        stack = StackedWidget(self.stackArea)
        self.stackArea.addWidget(stack)
        self.stacks.append(stack)
        self.currentStack = stack
        stack.currentChanged.connect(self.__currentChanged)
        stack.installEventFilter(self)
        self.setSizes([int(self.width() * 0.2), int(self.width() * 0.8)])
        # 20% for viewlist, 80% for the editors
        self.__inRemoveView = False

        self.__initMenu()
        self.contextMenuEditor = None
        self.contextMenuIndex = -1

    def __initMenu(self):
        """
        Private method to initialize the viewlist context menu.
        """
        self.__menu = QMenu(self)
        self.__menu.addAction(UI.PixmapCache.getIcon("tabClose.png"),
                              self.tr('Close'), self.__contextMenuClose)
        self.closeOthersMenuAct = self.__menu.addAction(
            UI.PixmapCache.getIcon("tabCloseOther.png"),
            self.tr("Close Others"), self.__contextMenuCloseOthers)
        self.__menu.addAction(self.tr('Close All'), self.__contextMenuCloseAll)
        self.__menu.addSeparator()
        self.saveMenuAct = self.__menu.addAction(
            UI.PixmapCache.getIcon("fileSave.png"), self.tr('Save'),
            self.__contextMenuSave)
        self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveAs.png"),
                              self.tr('Save As...'), self.__contextMenuSaveAs)
        self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveAll.png"),
                              self.tr('Save All'), self.__contextMenuSaveAll)
        self.__menu.addSeparator()
        self.openRejectionsMenuAct = self.__menu.addAction(
            self.tr("Open 'rejection' file"), self.__contextMenuOpenRejections)
        self.__menu.addSeparator()
        self.__menu.addAction(UI.PixmapCache.getIcon("print.png"),
                              self.tr('Print'), self.__contextMenuPrintFile)
        self.__menu.addSeparator()
        self.copyPathAct = self.__menu.addAction(
            self.tr("Copy Path to Clipboard"),
            self.__contextMenuCopyPathToClipboard)

    def __showMenu(self, point):
        """
        Private slot to handle the customContextMenuRequested signal of
        the viewlist.
        
        @param point position to open the menu at (QPoint)
        """
        if self.editors:
            itm = self.viewlist.itemAt(point)
            if itm is not None:
                row = self.viewlist.row(itm)
                self.contextMenuEditor = self.editors[row]
                self.contextMenuIndex = row
                if self.contextMenuEditor:
                    self.saveMenuAct.setEnabled(
                        self.contextMenuEditor.isModified())
                    fileName = self.contextMenuEditor.getFileName()
                    self.copyPathAct.setEnabled(bool(fileName))
                    if fileName:
                        rej = "{0}.rej".format(fileName)
                        self.openRejectionsMenuAct.setEnabled(
                            os.path.exists(rej))
                    else:
                        self.openRejectionsMenuAct.setEnabled(False)

                    self.closeOthersMenuAct.setEnabled(
                        self.viewlist.count() > 1)

                    self.__menu.popup(self.viewlist.mapToGlobal(point))

    def canCascade(self):
        """
        Public method to signal if cascading of managed windows is available.
        
        @return flag indicating cascading of windows is available
        """
        return False

    def canTile(self):
        """
        Public method to signal if tiling of managed windows is available.
        
        @return flag indicating tiling of windows is available
        """
        return False

    def canSplit(self):
        """
        public method to signal if splitting of the view is available.
        
        @return flag indicating splitting of the view is available.
        """
        return True

    def tile(self):
        """
        Public method to tile the managed windows.
        """
        pass

    def cascade(self):
        """
        Public method to cascade the managed windows.
        """
        pass

    def _removeAllViews(self):
        """
        Protected method to remove all views (i.e. windows).
        """
        self.viewlist.clear()
        for win in self.editors:
            for stack in self.stacks:
                if stack.hasEditor(win):
                    stack.removeWidget(win)
                    break
            win.closeIt()

    def _removeView(self, win):
        """
        Protected method to remove a view (i.e. window).
        
        @param win editor window to be removed
        """
        self.__inRemoveView = True
        ind = self.editors.index(win)
        itm = self.viewlist.takeItem(ind)
        if itm:
            del itm
        for stack in self.stacks:
            if stack.hasEditor(win):
                stack.removeWidget(win)
                break
        win.closeIt()
        self.__inRemoveView = False
        if ind > 0:
            ind -= 1
        else:
            if len(self.editors) > 1:
                ind = 1
            else:
                return
        stack.setCurrentWidget(stack.firstEditor())
        self._showView(self.editors[ind].parent())

        aw = self.activeWindow()
        fn = aw and aw.getFileName() or None
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, aw.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(aw)

    def _addView(self, win, fn=None, noName="", next=False):
        """
        Protected method to add a view (i.e. window).
        
        @param win editor assembly to be added
        @param fn filename of this editor (string)
        @param noName name to be used for an unnamed editor (string)
        @param next flag indicating to add the view next to the current
            view (bool)
        """
        editor = win.getEditor()
        if fn is None:
            if not noName:
                self.untitledCount += 1
                noName = self.tr("Untitled {0}").format(self.untitledCount)
            self.viewlist.addItem(noName)
            editor.setNoName(noName)
        else:
            txt = os.path.basename(fn)
            if not QFileInfo(fn).isWritable():
                txt = self.tr("{0} (ro)").format(txt)
            itm = QListWidgetItem(txt)
            itm.setToolTip(fn)
            self.viewlist.addItem(itm)
        self.currentStack.addWidget(win)
        self.currentStack.setCurrentWidget(win)
        editor.captionChanged.connect(self.__captionChange)
        editor.cursorLineChanged.connect(self.__cursorLineChanged)

        index = self.editors.index(editor)
        self.viewlist.setCurrentRow(index)
        editor.setFocus()
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)

    def __captionChange(self, cap, editor):
        """
        Private method to handle caption change signals from the editor.
        
        Updates the listwidget text to reflect the new caption information.
        
        @param cap Caption for the editor (string)
        @param editor Editor to update the caption for
        """
        fn = editor.getFileName()
        if fn:
            self.setEditorName(editor, fn)

    def __cursorLineChanged(self, lineno):
        """
        Private slot to handle a change of the current editor's cursor line.
        
        @param lineno line number of the current editor's cursor (zero based)
        """
        editor = self.sender()
        if editor:
            fn = editor.getFileName()
            if fn:
                self.editorLineChanged.emit(fn, lineno + 1)

    def _showView(self, win, fn=None):
        """
        Protected method to show a view (i.e. window).
        
        @param win editor assembly to be shown
        @param fn filename of this editor (string)
        """
        editor = win.getEditor()
        for stack in self.stacks:
            if stack.hasEditor(editor):
                stack.setCurrentWidget(win)
                self.currentStack = stack
                break
        index = self.editors.index(editor)
        self.viewlist.setCurrentRow(index)
        editor.setFocus()
        fn = editor.getFileName()
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)

    def __showSelectedView(self, row):
        """
        Private slot called to show a view selected in the list.
        
        @param row row number of the item clicked on (integer)
        """
        if row != -1:
            self._showView(self.editors[row].parent())
            self._checkActions(self.editors[row])

    def activeWindow(self):
        """
        Public method to return the active (i.e. current) window.
        
        @return reference to the active editor
        """
        return self.currentStack.currentWidget()

    def showWindowMenu(self, windowMenu):
        """
        Public method to set up the viewmanager part of the Window menu.
        
        @param windowMenu reference to the window menu
        """
        pass

    def _initWindowActions(self):
        """
        Protected method to define the user interface actions for window
        handling.
        """
        pass

    def setEditorName(self, editor, newName):
        """
        Public method to change the displayed name of the editor.
        
        @param editor editor window to be changed
        @param newName new name to be shown (string)
        """
        if newName:
            currentRow = self.viewlist.currentRow()
            index = self.editors.index(editor)
            txt = os.path.basename(newName)
            if not QFileInfo(newName).isWritable():
                txt = self.tr("{0} (ro)").format(txt)
            itm = self.viewlist.item(index)
            itm.setText(txt)
            itm.setToolTip(newName)
            self.viewlist.setCurrentRow(currentRow)
            self.changeCaption.emit(newName)

    def _modificationStatusChanged(self, m, editor):
        """
        Protected slot to handle the modificationStatusChanged signal.
        
        @param m flag indicating the modification status (boolean)
        @param editor editor window changed
        """
        currentRow = self.viewlist.currentRow()
        index = self.editors.index(editor)
        keys = []
        if m:
            keys.append("fileModified.png")
        if editor.hasSyntaxErrors():
            keys.append("syntaxError22.png")
        elif editor.hasWarnings():
            keys.append("warning22.png")
        if not keys:
            keys.append("empty.png")
        self.viewlist.item(index).setIcon(UI.PixmapCache.getCombinedIcon(keys))
        self.viewlist.setCurrentRow(currentRow)
        self._checkActions(editor)

    def _syntaxErrorToggled(self, editor):
        """
        Protected slot to handle the syntaxerrorToggled signal.
        
        @param editor editor that sent the signal
        """
        currentRow = self.viewlist.currentRow()
        index = self.editors.index(editor)
        keys = []
        if editor.isModified():
            keys.append("fileModified.png")
        if editor.hasSyntaxErrors():
            keys.append("syntaxError22.png")
        elif editor.hasWarnings():
            keys.append("warning22.png")
        if not keys:
            keys.append("empty.png")
        self.viewlist.item(index).setIcon(UI.PixmapCache.getCombinedIcon(keys))
        self.viewlist.setCurrentRow(currentRow)

        ViewManager._syntaxErrorToggled(self, editor)

    def addSplit(self):
        """
        Public method used to split the current view.
        """
        stack = StackedWidget(self.stackArea)
        stack.show()
        self.stackArea.addWidget(stack)
        self.stacks.append(stack)
        self.currentStack = stack
        stack.currentChanged.connect(self.__currentChanged)
        stack.installEventFilter(self)
        if self.stackArea.orientation() == Qt.Horizontal:
            size = self.stackArea.width()
        else:
            size = self.stackArea.height()
        self.stackArea.setSizes([int(size / len(self.stacks))] *
                                len(self.stacks))
        self.splitRemoveAct.setEnabled(True)
        self.nextSplitAct.setEnabled(True)
        self.prevSplitAct.setEnabled(True)

    def removeSplit(self):
        """
        Public method used to remove the current split view.
        
        @return flag indicating successfull removal
        """
        if len(self.stacks) > 1:
            stack = self.currentStack
            res = True
            savedEditors = stack.editors[:]
            for editor in savedEditors:
                res &= self.closeEditor(editor)
            if res:
                try:
                    i = self.stacks.index(stack)
                except ValueError:
                    return True
                if i == len(self.stacks) - 1:
                    i -= 1
                self.stacks.remove(stack)
                stack.close()
                self.currentStack = self.stacks[i]
                if len(self.stacks) == 1:
                    self.splitRemoveAct.setEnabled(False)
                    self.nextSplitAct.setEnabled(False)
                    self.prevSplitAct.setEnabled(False)
                return True

        return False

    def getSplitOrientation(self):
        """
        Public method to get the orientation of the split view.
        
        @return orientation of the split (Qt.Horizontal or Qt.Vertical)
        """
        return self.stackArea.orientation()

    def setSplitOrientation(self, orientation):
        """
        Public method used to set the orientation of the split view.
        
        @param orientation orientation of the split
                (Qt.Horizontal or Qt.Vertical)
        """
        self.stackArea.setOrientation(orientation)

    def nextSplit(self):
        """
        Public slot used to move to the next split.
        """
        aw = self.activeWindow()
        _hasFocus = aw and aw.hasFocus()
        ind = self.stacks.index(self.currentStack) + 1
        if ind == len(self.stacks):
            ind = 0

        self.currentStack = self.stacks[ind]
        if _hasFocus:
            aw = self.activeWindow()
            if aw:
                aw.setFocus()

        index = self.editors.index(self.currentStack.currentWidget())
        self.viewlist.setCurrentRow(index)

    def prevSplit(self):
        """
        Public slot used to move to the previous split.
        """
        aw = self.activeWindow()
        _hasFocus = aw and aw.hasFocus()
        ind = self.stacks.index(self.currentStack) - 1
        if ind == -1:
            ind = len(self.stacks) - 1

        self.currentStack = self.stacks[ind]
        if _hasFocus:
            aw = self.activeWindow()
            if aw:
                aw.setFocus()
        index = self.editors.index(self.currentStack.currentWidget())
        self.viewlist.setCurrentRow(index)

    def __contextMenuClose(self):
        """
        Private method to close the selected editor.
        """
        if self.contextMenuEditor:
            self.closeEditorWindow(self.contextMenuEditor)

    def __contextMenuCloseOthers(self):
        """
        Private method to close the other editors.
        """
        index = self.contextMenuIndex
        for i in list(range(self.viewlist.count() - 1, index, -1)) + \
                list(range(index - 1, -1, -1)):
            editor = self.editors[i]
            self.closeEditorWindow(editor)

    def __contextMenuCloseAll(self):
        """
        Private method to close all editors.
        """
        savedEditors = self.editors[:]
        for editor in savedEditors:
            self.closeEditorWindow(editor)

    def __contextMenuSave(self):
        """
        Private method to save the selected editor.
        """
        if self.contextMenuEditor:
            self.saveEditorEd(self.contextMenuEditor)

    def __contextMenuSaveAs(self):
        """
        Private method to save the selected editor to a new file.
        """
        if self.contextMenuEditor:
            self.saveAsEditorEd(self.contextMenuEditor)

    def __contextMenuSaveAll(self):
        """
        Private method to save all editors.
        """
        self.saveEditorsList(self.editors)

    def __contextMenuOpenRejections(self):
        """
        Private slot to open a rejections file associated with the selected
        editor.
        """
        if self.contextMenuEditor:
            fileName = self.contextMenuEditor.getFileName()
            if fileName:
                rej = "{0}.rej".format(fileName)
                if os.path.exists(rej):
                    self.openSourceFile(rej)

    def __contextMenuPrintFile(self):
        """
        Private method to print the selected editor.
        """
        if self.contextMenuEditor:
            self.printEditor(self.contextMenuEditor)

    def __contextMenuCopyPathToClipboard(self):
        """
        Private method to copy the file name of the selected editor to the
        clipboard.
        """
        if self.contextMenuEditor:
            fn = self.contextMenuEditor.getFileName()
            if fn:
                cb = QApplication.clipboard()
                cb.setText(fn)

    def __currentChanged(self, index):
        """
        Private slot to handle the currentChanged signal.
        
        @param index index of the current editor
        """
        if index == -1 or not self.editors:
            return

        editor = self.activeWindow()
        if editor is None:
            return

        self._checkActions(editor)
        editor.setFocus()
        fn = editor.getFileName()
        if fn:
            self.changeCaption.emit(fn)
            if not self.__inRemoveView:
                self.editorChanged.emit(fn)
                self.editorLineChanged.emit(fn,
                                            editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)

        cindex = self.editors.index(editor)
        self.viewlist.setCurrentRow(cindex)

    def eventFilter(self, watched, event):
        """
        Public method called to filter the event queue.
        
        @param watched the QObject being watched
        @param event the event that occurred
        @return flag indicating, if we handled the event
        """
        if event.type() == QEvent.MouseButtonPress and \
           not event.button() == Qt.RightButton:
            switched = True
            if isinstance(watched, QStackedWidget):
                switched = watched is not self.currentStack
                self.currentStack = watched
            elif isinstance(watched, QScintilla.Editor.Editor):
                for stack in self.stacks:
                    if stack.hasEditor(watched):
                        switched = stack is not self.currentStack
                        self.currentStack = stack
                        break
            currentWidget = self.currentStack.currentWidget()
            if currentWidget:
                index = self.editors.index(currentWidget)
                self.viewlist.setCurrentRow(index)

            aw = self.activeWindow()
            if aw is not None:
                self._checkActions(aw)
                aw.setFocus()
                fn = aw.getFileName()
                if fn:
                    self.changeCaption.emit(fn)
                    if switched:
                        self.editorChanged.emit(fn)
                        self.editorLineChanged.emit(
                            fn,
                            aw.getCursorPosition()[0] + 1)
                else:
                    self.changeCaption.emit("")
                self.editorChangedEd.emit(aw)

        return False
Beispiel #56
0
class Demo(QWidget):
    def __init__(self):
        super(Demo, self).__init__()

        self.time_label = QLabel(self)
        self.volume_slider = QSlider(self)
        self.progress_slider = QSlider(self)
        self.sound_btn = QPushButton(self)
        self.previous_btn = QPushButton(self)
        self.play_pause_btn = QPushButton(self)
        self.next_btn = QPushButton(self)
        self.mode_btn = QPushButton(self)
        self.list_btn = QPushButton(self)
        self.list_widget = QListWidget(self)

        self.h1_layout = QHBoxLayout()
        self.h2_layout = QHBoxLayout()
        self.all_v_layout = QVBoxLayout()

        self.playlist = QMediaPlaylist(self)        
        self.player = QMediaPlayer(self)

        self.widget_init()
        self.layout_init()
        self.signal_init()

    def widget_init(self):
        self.time_label.setText('--/--')
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setValue(100)
        self.volume_slider.setOrientation(Qt.Horizontal)
        self.progress_slider.setEnabled(False)
        self.progress_slider.setOrientation(Qt.Horizontal)
        self.sound_btn.setIcon(QIcon('images/sound_on.png'))
        self.previous_btn.setIcon(QIcon('images/previous.png'))
        self.play_pause_btn.setIcon(QIcon('images/play.png'))
        self.next_btn.setIcon(QIcon('images/next.png'))
        self.mode_btn.setIcon(QIcon('images/list_loop.png'))
        self.list_btn.setIcon(QIcon('images/show.png'))

        self.player.setPlaylist(self.playlist)
        self.media_list = ['/Users/louis/Downloads/music1.mp3',
                           '/Users/louis/Downloads/music2.mp4',
                           '/Users/louis/Downloads/music3.mp3']
        for m in self.media_list:
            self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(m)))
        self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)

        self.list_widget.addItems([m.split('/')[-1] for m in self.media_list])

    def layout_init(self):
        self.h1_layout.addWidget(self.progress_slider)
        self.h1_layout.addWidget(self.time_label)
        self.h2_layout.addWidget(self.volume_slider)
        self.h2_layout.addWidget(self.sound_btn)
        self.h2_layout.addWidget(self.previous_btn)
        self.h2_layout.addWidget(self.play_pause_btn)
        self.h2_layout.addWidget(self.next_btn)
        self.h2_layout.addWidget(self.mode_btn)
        self.h2_layout.addWidget(self.list_btn)

        self.all_v_layout.addLayout(self.h1_layout)
        self.all_v_layout.addLayout(self.h2_layout)
        self.all_v_layout.addWidget(self.list_widget)
        self.all_v_layout.setSizeConstraint(QVBoxLayout.SetFixedSize)

        self.setLayout(self.all_v_layout)

    def signal_init(self):
        self.sound_btn.clicked.connect(lambda: self.btn_func(self.sound_btn))
        self.previous_btn.clicked.connect(lambda: self.btn_func(self.previous_btn))
        self.play_pause_btn.clicked.connect(lambda: self.btn_func(self.play_pause_btn))
        self.next_btn.clicked.connect(lambda: self.btn_func(self.next_btn))
        self.mode_btn.clicked.connect(lambda: self.btn_func(self.mode_btn))
        self.list_btn.clicked.connect(lambda: self.btn_func(self.list_btn))
        self.volume_slider.valueChanged.connect(self.volume_slider_func)
        self.list_widget.doubleClicked.connect(self.list_play_func)
        self.player.durationChanged.connect(self.get_duration_func)
        self.player.positionChanged.connect(self.get_position_func)
        self.progress_slider.sliderMoved.connect(self.update_position_func)

    def btn_func(self, btn):
        if btn == self.sound_btn:              
            if self.player.isMuted():
                self.player.setMuted(False)
                self.sound_btn.setIcon(QIcon('images/sound_on'))
            else:
                self.player.setMuted(True)
                self.sound_btn.setIcon(QIcon('images/sound_off'))

        elif btn == self.previous_btn:          
            if self.playlist.currentIndex() == 0:
                self.playlist.setCurrentIndex(self.playlist.mediaCount() - 1)
            else:
                self.playlist.previous()

        elif btn == self.play_pause_btn:      
            if self.player.state() == 1:
                self.player.pause()
                self.play_pause_btn.setIcon(QIcon('images/play.png'))
            else:
                self.player.play()
                self.play_pause_btn.setIcon(QIcon('images/pause.png'))

        elif btn == self.next_btn:              
            if self.playlist.currentIndex() == self.playlist.mediaCount() - 1:
                self.playlist.setCurrentIndex(0)
            else:
                self.playlist.next()

        elif btn == self.mode_btn:             
            if self.playlist.playbackMode() == 2:
                self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
                self.mode_btn.setIcon(QIcon('images/item_loop.png'))

            elif self.playlist.playbackMode() == 3:
                self.playlist.setPlaybackMode(QMediaPlaylist.Random)
                self.mode_btn.setIcon(QIcon('images/random.png'))

            elif self.playlist.playbackMode() == 4:
                self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
                self.mode_btn.setIcon(QIcon('images/list_loop.png'))

        elif btn == self.list_btn:             
            if self.list_widget.isHidden():
                self.list_widget.show()
                self.list_btn.setIcon(QIcon('images/show.png'))
            else:
                self.list_widget.hide()
                self.list_btn.setIcon(QIcon('images/hide.png'))

    def volume_slider_func(self, value):
        self.player.setVolume(value)
        if value == 0:
            self.sound_btn.setIcon(QIcon('images/sound_off.png'))
        else:
            self.sound_btn.setIcon(QIcon('images/sound_on.png'))

    def list_play_func(self):
        self.playlist.setCurrentIndex(self.list_widget.currentRow())
        self.player.play()
        self.play_pause_btn.setIcon(QIcon('images/pause.png'))

    def get_duration_func(self, d):
        self.progress_slider.setRange(0, d)
        self.progress_slider.setEnabled(True)
        self.get_time_func(d)

    def get_time_func(self, d):
        seconds = int(d / 1000)
        minutes = int(seconds / 60)
        seconds -= minutes * 60
        if minutes == 0 and seconds == 0:
            self.time_label.setText('--/--')
            self.play_pause_btn.setIcon(QIcon('images/play.png'))
        else:
            self.time_label.setText('{}:{}'.format(minutes, seconds))

    def get_position_func(self, p):
        self.progress_slider.setValue(p)

    def update_position_func(self, v):
        self.player.setPosition(v)
        d = self.progress_slider.maximum() - v
        self.get_time_func(d)
class PostProcessor(QMainWindow):

    sim_results_changed = pyqtSignal()
    post_results_changed = pyqtSignal()

    figures_changed = pyqtSignal(list, str)

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self._settings = QSettings()
        self._logger = logging.getLogger(self.__class__.__name__)

        self.setWindowTitle("Processing")
        self.setWindowIcon(QIcon(get_resource("processing.png")))
        self.mainFrame = QWidget(self)
        self.resize(1000, 600)

        # toolbar
        self.toolBar = QToolBar("file control")
        self.toolBar.setIconSize(QSize(24, 24))
        self.addToolBar(self.toolBar)

        self.actLoad = QAction(self)
        self.actLoad.setText("load result file")
        self.actLoad.setIcon(QIcon(get_resource("load.png")))
        self.actLoad.setDisabled(False)
        self.actLoad.triggered.connect(self.load_result_files)

        self.actPostLoad = QAction(self)
        self.actPostLoad.setText("load post-result file")
        self.actPostLoad.setIcon(QIcon(get_resource("load.png")))
        self.actPostLoad.setDisabled(False)
        self.actPostLoad.triggered.connect(self.load_post_result_files)

        self.actSwitch = QAction(self)
        self.actSwitch.setText("switch display mode")
        self.actSwitch.setIcon(QIcon(get_resource("left_mode.png")))
        self.actSwitch.setDisabled(False)
        self.actSwitch.triggered.connect(self.switch_sides)
        self.displayLeft = True

        self.spacer1 = QWidget()
        self.spacer2 = QWidget()
        self.spacer1.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.actReloadMethods = QAction(self)
        self.actReloadMethods.setText("reload methods")
        self.actReloadMethods.setIcon(QIcon(get_resource("reload.png")))
        self.actReloadMethods.setDisabled(False)
        self.actReloadMethods.triggered.connect(self.update_post_method_list)

        self.actReloadMetaMethods = QAction(self)
        self.actReloadMetaMethods.setText("reload meta methods")
        self.actReloadMetaMethods.setIcon(QIcon(get_resource("reload.png")))
        self.actReloadMetaMethods.setDisabled(False)
        self.actReloadMetaMethods.triggered.connect(
            self.update_meta_method_list)
        
        self.toolBar.addAction(self.actLoad)
        self.toolBar.addAction(self.actReloadMethods)
        
        self.toolBar.addWidget(self.spacer1)
        self.toolBar.addAction(self.actSwitch)
        self.toolBar.addWidget(self.spacer2)

        self.toolBar.addAction(self.actReloadMetaMethods)
        self.toolBar.addAction(self.actPostLoad)

        # main window
        self.grid = QGridLayout(self.mainFrame)
        self.grid.setColumnMinimumWidth(0, 70)
        self.grid.setColumnStretch(0, 0)
        self.grid.setColumnStretch(1, 1)

        self.methodList = QListWidget(self)
        self.methodList.itemDoubleClicked.connect(
            self.post_processor_clicked)
        self.update_post_method_list()
        self.metaMethodList = QListWidget(self)
        self.metaMethodList.itemDoubleClicked.connect(
            self.meta_processor_clicked)
        self.update_meta_method_list()

        self.sim_result_list = QListWidget(self)
        self.sim_results_changed.connect(self.update_result_list)
        self.results = []

        self.delShort = QShortcut(QKeySequence(Qt.Key_Delete),
                                  self.sim_result_list)
        self.delShort.activated.connect(self.remove_result_item)

        # figures
        self._figure_dict = {}
        self.figures_changed.connect(self.update_figure_lists)

        self.post_figure_list = QListWidget(self)
        self.post_figure_list.currentItemChanged.connect(
            self.current_figure_changed)
        self.meta_figure_list = QListWidget(self)
        self.meta_figure_list.currentItemChanged.connect(
            self.current_figure_changed)

        self.plotView = QWidget()
        self.lastFigure = None

        self.post_result_list = QListWidget(self)
        self.post_results_changed.connect(self.update_post_result_list)
        self.post_results = []
        self.delShortPost = QShortcut(QKeySequence(Qt.Key_Backspace),
                                      self.post_result_list)
        self.delShortPost.activated.connect(self.remove_post_result_item)

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

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

        self.grid.addWidget(QLabel("Result Files:"), 0, 0)
        self.grid.addWidget(self.sim_result_list, 1, 0)
        self.grid.addWidget(QLabel("Postprocessors:"), 2, 0)
        self.grid.addWidget(self.methodList, 3, 0)
        self.grid.addWidget(QLabel("Figures:"), 4, 0)
        self.grid.addWidget(self.post_figure_list, 5, 0)
        self.grid.addWidget(QLabel("Selected Figure:"), 0, 1)
        self.grid.addWidget(QLabel("Postprocessor Files:"), 0, 2)
        self.grid.addWidget(self.post_result_list, 1, 2)
        self.grid.addWidget(QLabel("Metaprocessors:"), 2, 2)
        self.grid.addWidget(self.metaMethodList, 3, 2)
        self.grid.addWidget(QLabel("Figures:"), 4, 2)
        self.grid.addWidget(self.meta_figure_list, 5, 2)
        self.grid.addWidget(self.logBox, 6, 0, 1, 3)

        self.mainFrame.setLayout(self.grid)
        self.setCentralWidget(self.mainFrame)

        # status bar
        self.statusBar = QStatusBar(self)
        self.setStatusBar(self.statusBar)

    def load_result_files(self):
        path = self._settings.value("path/simulation_results")

        dialog = QFileDialog(self)
        dialog.setFileMode(QFileDialog.ExistingFiles)
        dialog.setDirectory(path)
        dialog.setNameFilter("PyMoskito Result files (*.pmr)")

        if dialog.exec_():
            files = dialog.selectedFiles()
            for single_file in files:
                if single_file:
                    self._load_result_file(single_file)

    def _load_result_file(self, file_name):
        """
        loads a result file
        """
        self._logger.info("loading result file {}".format(file_name))
        with open(file_name.encode(), "rb") as f:
            self.results.append(pickle.load(f))

        self.sim_results_changed.emit()

    def update_result_list(self):
        self.sim_result_list.clear()
        for res in self.results:
            name = res["regime name"]
            self.sim_result_list.addItem(name)

    def remove_result_item(self):
        if self.sim_result_list.currentRow() >= 0:
            del self.results[self.sim_result_list.currentRow()]
            self.sim_result_list.takeItem(self.sim_result_list.currentRow())

    def load_post_result_files(self):
        path = self._settings.value("path/processing_results")

        dialog = QFileDialog(self)
        dialog.setFileMode(QFileDialog.ExistingFiles)
        dialog.setDirectory(path)
        dialog.setNameFilter("Postprocessing Output files (*.pof)")

        if dialog.exec_():
            files = dialog.selectedFiles()
            for single_file in files:
                if single_file:
                    self._load_post_result_file(single_file)

    def _load_post_result_file(self, file_name):
        """
        loads a post-result file (.pof)
        """
        name = os.path.split(file_name)[-1][:-4]
        self._logger.info("loading result file {}".format(file_name))
        with open(file_name.encode(), "rb") as f:
            results = pickle.load(f)
            results.update({"name": name})
            self.post_results.append(results)

        self.post_results_changed.emit()

    def update_post_result_list(self):
        self.post_result_list.clear()
        for res in self.post_results:
            name = res["name"]
            self.post_result_list.addItem(name)

    def remove_post_result_item(self):
        if self.post_result_list.currentRow() >= 0:
            del self.post_results[self.post_result_list.currentRow()]
            self.post_result_list.takeItem(self.post_result_list.currentRow())

    def update_post_method_list(self):
        self.methodList.clear()
        modules = pm.get_registered_processing_modules(PostProcessingModule)
        for mod in modules:
            self.methodList.addItem(mod[1])

    def update_meta_method_list(self):
        self.metaMethodList.clear()
        modules = pm.get_registered_processing_modules(MetaProcessingModule)
        for mod in modules:
            self.metaMethodList.addItem(mod[1])

    def post_processor_clicked(self, item):
        self.run_processor(str(item.text()), "post")

    def meta_processor_clicked(self, item):
        self.run_processor(str(item.text()), "meta")

    def run_processor(self, name, processor_type):
        if processor_type == "post":
            result_files = self.results
            base_cls = PostProcessingModule
        elif processor_type == "meta":
            result_files = self.post_results
            base_cls = MetaProcessingModule
        else:
            self._logger.error("unknown processor type {0}".format(
                processor_type))
            raise ValueError("unknown processor type {0}".format(
                processor_type))

        if not result_files:
            self._logger.warning("run_processor() Error: no result file loaded")
            return

        processor_cls = pm.get_processing_module_class_by_name(base_cls, name)
        processor = processor_cls()

        figs = []
        try:
            self._logger.info("executing processor '{0}'".format(name))
            figs = processor.process(result_files)
        except Exception as err:
            self._logger.exception("Error in processor")

        self.figures_changed.emit(figs, processor_type)
        self._logger.info("finished postprocessing")

    def update_figure_lists(self, figures, target_type):
        # remove no longer needed elements
        for item, fig in [(key, val[0])
                          for key, val in self._figure_dict.items()
                          if val[1] == target_type]:
            if fig not in [new_fig["figure"] for new_fig in figures]:
                if target_type == "post":
                    old_item = self.post_figure_list.takeItem(
                        self.post_figure_list.row(item))
                    del old_item
                elif target_type == "meta":
                    old_item = self.meta_figure_list.takeItem(
                        self.meta_figure_list.row(item))
                    del old_item

                del self._figure_dict[item]

        # add new ones to internal storage
        for fig in figures:
            if fig["figure"] not in self._figure_dict.values():
                new_entry = [(fig["name"],
                              (QListWidgetItem(fig["name"]),
                               fig["figure"], target_type)
                              )]
                self._figure_dict.update(new_entry)

        # add to display
        for key, val in self._figure_dict.items():
            if val[2] == "post":
                self.post_figure_list.addItem(val[0])
            elif val[2] == "meta":
                self.meta_figure_list.addItem(val[0])

        self.post_figure_list.setCurrentItem(self.post_figure_list.item(0))
        self.meta_figure_list.setCurrentItem(self.meta_figure_list.item(0))

    def current_figure_changed(self, current_item, last_item=None):
        if current_item is None:
            return

        figures = self._figure_dict

        if self.lastFigure:
            self.grid.removeWidget(self.lastFigure)
            self.lastFigure.setVisible(False)

        if current_item.text() in figures:
            figure_widget = figures[current_item.text()][1]
            self.grid.addWidget(figure_widget, 1, 1, 5, 1)
            figure_widget.setVisible(True)
            self.lastFigure = figure_widget
        
    def switch_sides(self):
        self.displayLeft = not self.displayLeft
        if self.displayLeft:
            self.actSwitch.setIcon(QIcon(get_resource("left_mode.png")))
            self.post_figure_list.setFocus()
            self.current_figure_changed(self.post_figure_list.currentItem())
        else:
            self.actSwitch.setIcon(QIcon(get_resource("right_mode.png")))
            self.meta_figure_list.setFocus()
            self.current_figure_changed(self.meta_figure_list.currentItem())
class ListWindow(QWidget):
    def __init__(self):
        super().__init__()

        # subject data stored as a list
        #     each subject is also a list containing
        #         name (string)
        #         year of birth (number)
        #         gender (string 'm', 'f' or '?')
        #         symptoms (string)
        self.data = [['Ravinder Kaur', 1990, 'f', 'Telepathy'],
                     ['Bruce Schultz', 1989, 'm', 'Adamantium skeleton']]

        # some buttons
        button_add = QPushButton('Add')

        button_edit = QPushButton('Edit')

        button_delete = QPushButton('Delete')

        # horizontal box for the buttons
        hbox = QHBoxLayout()
        hbox.addWidget(button_add)
        hbox.addWidget(button_edit)
        hbox.addWidget(button_delete)

        # TODO add 'Load' and 'Save' buttons
        button_load = QPushButton('Load')
        button_save = QPushButton('Save')

        hbox.addWidget(button_save)
        hbox.addWidget(button_load)

        # TODO connect functions to the buttons,
        # so they are called when the user clicks a button
        button_edit.clicked.connect(self.onEditClicked)
        button_delete.clicked.connect(self.onDeleteClicked)
        button_add.clicked.connect(self.onAddClicked)
        button_save.clicked.connect(self.onSaveClicked)
        button_load.clicked.connect(self.onLoadClicked)

        # list of the subject's names
        self.list = QListWidget()
        self.updateList()

        # vertical layout: buttons below the list
        vbox = QVBoxLayout()
        vbox.addWidget(self.list)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

        self.setGeometry(50, 50, 350, 300)
        self.setWindowTitle('Subject List')
        self.show()

    def updateList(self):
        # populate list with the subject's names
        self.list.clear()
        for entry in self.data:
            self.list.addItem(entry[0])

    def onAddClicked(self):
        # create the subject dialog box
        dlg = SubjectDialog(self)

        # don't allow any user input as long as the dialog box is visible
        self.setEnabled(False)

        # run the dialog
        if dlg.exec_() == dlg.Accepted:

            # Add the new data from the dialog box to the data list
            self.data.append(dlg.getData())
            self.updateList()

        # ok, user input again
        self.setEnabled(True)

    def onEditClicked(self):
        # which row is selected/current?
        current_row = self.list.currentRow()
        if current_row < 0:
            return

        # create the subject dialog box
        dlg = SubjectDialog(self)

        # fill the dialog box with our data
        dlg.setData(self.data[current_row])

        # don't allow any user input as long as the dialog box is visible
        self.setEnabled(False)

        # run the dialog
        if dlg.exec_() == dlg.Accepted:

            # get the edited data from the dialog box
            self.data[current_row] = dlg.getData()
            self.updateList()

        # ok, user input again
        self.setEnabled(True)

    def onDeleteClicked(self):
        current_row = self.list.currentRow(
        )  #Select row and index item in self.data
        if current_row < 0:
            return
        del self.data[current_row]  #Remove entry from self.data
        self.updateList()  #Update list to show removed entry

    def onSaveClicked(self):
        save_directory = QFileDialog.getSaveFileName()
        with open(str(save_directory[0]), 'w', newline='') as results:
            csv_data = csv.writer(results, delimiter=',')
            header = ['Patient Name', 'Year of Birth', 'Gender', 'Symptoms']
            csv_data.writerow(header)  #Who doesn't love a header?
            for patient in self.data:  #To write patients as rows
                csv_data.writerow(patient)

    def onLoadClicked(self):
        load_file = QFileDialog.getOpenFileName(
        )  #File name must contain extension
        with open(str(load_file[0]), 'r') as input_file:
            new_data = pd.read_csv(input_file, sep=',', header=0)
            self.data = new_data.values
            self.updateList()
Beispiel #59
0
class CityListDlg(QDialog):
    citieslist_signal = pyqtSignal([list])

    def __init__(self, citylist, accurate_url, appid, parent=None):
        super(CityListDlg, self).__init__(parent)
        self.citylist = citylist
        self.accurate_url = accurate_url
        self.appid = appid
        self.listWidget = QListWidget()
        self.listWidget.addItems(self.citylist)
        buttonLayout = QVBoxLayout()
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.buttonBox.rejected.connect(self.reject)
        self.buttonBox.accepted.connect(self.accept)
        layoutT = QVBoxLayout()
        layout = QHBoxLayout()
        layout.addWidget(self.listWidget)
        layout.addLayout(buttonLayout)
        for text, slot in ((self.tr("&Add..."), self.add),
                           (self.tr("&Remove..."), self.remove),
                           (self.tr("&Up"), self.up),
                           (self.tr("&Down"), self.down),
                           (self.tr("De&fault"), self.default),
                           (self.tr("&Sort"), self.listWidget.sortItems)):
            button = QPushButton(text)
            buttonLayout.addWidget(button)
            button.clicked.connect(slot)
        buttonLayout.addWidget(self.buttonBox)
        self.status = QLabel()
        layoutT.addLayout(layout)
        layoutT.addWidget(self.status)
        self.setLayout(layoutT)
        self.checklength()

    def add(self):
        self.status.setText('')
        lista = []
        newitem = ''
        self.citytoadd = ''
        self.countrytoadd = ''
        self._idtoadd = ''
        dialog = searchcity.SearchCity(self.accurate_url, self.appid, self)
        dialog.id_signal.connect(self.addcity)
        dialog.city_signal.connect(self.addcity)
        dialog.country_signal.connect(self.addcity)
        if dialog.exec_() == 1:
            newitem = (self.citytoadd + '_' + self.countrytoadd + '_' +
                       self._idtoadd)
            for row in range(self.listWidget.count()):
                lista.append(self.listWidget.item(row).text())
            if newitem in lista:
                self.status.setText(QCoreApplication.translate('Status bar message',
                                    'The city already exists in the list', 'Cities list dialogue'))
                return
            else:
                self.listWidget.addItem(newitem)
                self.checklength()

    def addcity(self, what):
        self.status.setText('')
        if what[0] == 'ID':
            self._idtoadd = what[1]
        elif what[0] == 'City':
            self.citytoadd = what[1]
        elif what[0] == 'Country':
            self.countrytoadd = what[1]

    def remove(self):
        self.status.setText('')
        if self.listWidget.count() == 0:
            self.status.setText(self.tr('The list is empty'))
            return
        row = self.listWidget.currentRow()
        item = self.listWidget.item(row)
        if item is None:
            return
        message = self.tr('The city "{0}" has been removed').format(
            self.listWidget.item(row).text())
        item = self.listWidget.takeItem(row)
        del item
        self.status.setText(message)

    def up(self):
        self.status.setText('')
        row = self.listWidget.currentRow()
        if row >= 1:
            item = self.listWidget.takeItem(row)
            self.listWidget.insertItem(row - 1, item)
            self.listWidget.setCurrentItem(item)

    def down(self):
        self.status.setText('')
        row = self.listWidget.currentRow()
        if row < self.listWidget.count() - 1:
            item = self.listWidget.takeItem(row)
            self.listWidget.insertItem(row + 1, item)
            self.listWidget.setCurrentItem(item)

    def default(self):
        self.status.setText('')
        row = self.listWidget.currentRow()
        if row >= 1:
            item = self.listWidget.takeItem(row)
            self.listWidget.insertItem(0, item)
            self.listWidget.setCurrentItem(item)

    def checklength(self):
        listtosend = []
        for row in range(self.listWidget.count()):
            listtosend.append(self.listWidget.item(row).text())
        if len(listtosend) == 0:
            return
        self.listWidget.setMinimumWidth(self.listWidget.sizeHintForColumn(0))

    def accept(self):
        listtosend = []
        for row in range(self.listWidget.count()):
            listtosend.append(self.listWidget.item(row).text())
        self.citieslist_signal[list].emit(listtosend)
        QDialog.accept(self)
Beispiel #60
0
class Listspace(QSplitter, ViewManager):
    """
    Class implementing the listspace viewmanager class.
    
    @signal changeCaption(str) emitted if a change of the caption is necessary
    @signal editorChanged(str) emitted when the current editor has changed
    @signal editorChangedEd(Editor) emitted when the current editor has changed
    @signal lastEditorClosed() emitted after the last editor window was closed
    @signal editorOpened(str) emitted after an editor window was opened
    @signal editorOpenedEd(Editor) emitted after an editor window was opened
    @signal editorClosed(str) emitted just before an editor window gets closed
    @signal editorClosedEd(Editor) emitted just before an editor window gets
        closed
    @signal editorRenamed(str) emitted after an editor was renamed
    @signal editorRenamedEd(Editor) emitted after an editor was renamed
    @signal editorSaved(str) emitted after an editor window was saved
    @signal editorSavedEd(Editor) emitted after an editor window was saved
    @signal checkActions(Editor) emitted when some actions should be checked
        for their status
    @signal cursorChanged(Editor) emitted after the cursor position of the
        active window has changed
    @signal breakpointToggled(Editor) emitted when a breakpoint is toggled.
    @signal bookmarkToggled(Editor) emitted when a bookmark is toggled.
    @signal syntaxerrorToggled(Editor) emitted when a syntax error is toggled.
    @signal previewStateChanged(bool) emitted to signal a change in the
        preview state
    @signal editorLanguageChanged(Editor) emitted to signal a change of an
        editors language
    @signal editorTextChanged(Editor) emitted to signal a change of an
        editor's text
    @signal editorLineChanged(str,int) emitted to signal a change of an
        editor's current line (line is given one based)
    """
    changeCaption = pyqtSignal(str)
    editorChanged = pyqtSignal(str)
    editorChangedEd = pyqtSignal(Editor)
    lastEditorClosed = pyqtSignal()
    editorOpened = pyqtSignal(str)
    editorOpenedEd = pyqtSignal(Editor)
    editorClosed = pyqtSignal(str)
    editorClosedEd = pyqtSignal(Editor)
    editorRenamed = pyqtSignal(str)
    editorRenamedEd = pyqtSignal(Editor)
    editorSaved = pyqtSignal(str)
    editorSavedEd = pyqtSignal(Editor)
    checkActions = pyqtSignal(Editor)
    cursorChanged = pyqtSignal(Editor)
    breakpointToggled = pyqtSignal(Editor)
    bookmarkToggled = pyqtSignal(Editor)
    syntaxerrorToggled = pyqtSignal(Editor)
    previewStateChanged = pyqtSignal(bool)
    editorLanguageChanged = pyqtSignal(Editor)
    editorTextChanged = pyqtSignal(Editor)
    editorLineChanged = pyqtSignal(str, int)
    
    def __init__(self, parent):
        """
        Constructor
        
        @param parent parent widget (QWidget)
        """
        self.stacks = []
        
        QSplitter.__init__(self, parent)
        ViewManager.__init__(self)
        self.setChildrenCollapsible(False)
        
        self.viewlist = QListWidget(self)
        policy = self.viewlist.sizePolicy()
        policy.setHorizontalPolicy(QSizePolicy.Ignored)
        self.viewlist.setSizePolicy(policy)
        self.addWidget(self.viewlist)
        self.viewlist.setContextMenuPolicy(Qt.CustomContextMenu)
        self.viewlist.currentRowChanged.connect(self.__showSelectedView)
        self.viewlist.customContextMenuRequested.connect(self.__showMenu)
        
        self.stackArea = QSplitter(self)
        self.stackArea.setChildrenCollapsible(False)
        self.addWidget(self.stackArea)
        self.stackArea.setOrientation(Qt.Vertical)
        stack = StackedWidget(self.stackArea)
        self.stackArea.addWidget(stack)
        self.stacks.append(stack)
        self.currentStack = stack
        stack.currentChanged.connect(self.__currentChanged)
        stack.installEventFilter(self)
        self.setSizes([int(self.width() * 0.2), int(self.width() * 0.8)])
        # 20% for viewlist, 80% for the editors
        self.__inRemoveView = False
        
        self.__initMenu()
        self.contextMenuEditor = None
        self.contextMenuIndex = -1
        
    def __initMenu(self):
        """
        Private method to initialize the viewlist context menu.
        """
        self.__menu = QMenu(self)
        self.__menu.addAction(
            UI.PixmapCache.getIcon("tabClose.png"),
            self.tr('Close'), self.__contextMenuClose)
        self.closeOthersMenuAct = self.__menu.addAction(
            UI.PixmapCache.getIcon("tabCloseOther.png"),
            self.tr("Close Others"),
            self.__contextMenuCloseOthers)
        self.__menu.addAction(
            self.tr('Close All'), self.__contextMenuCloseAll)
        self.__menu.addSeparator()
        self.saveMenuAct = self.__menu.addAction(
            UI.PixmapCache.getIcon("fileSave.png"),
            self.tr('Save'), self.__contextMenuSave)
        self.__menu.addAction(
            UI.PixmapCache.getIcon("fileSaveAs.png"),
            self.tr('Save As...'), self.__contextMenuSaveAs)
        self.__menu.addAction(
            UI.PixmapCache.getIcon("fileSaveAll.png"),
            self.tr('Save All'), self.__contextMenuSaveAll)
        self.__menu.addSeparator()
        self.openRejectionsMenuAct = self.__menu.addAction(
            self.tr("Open 'rejection' file"),
            self.__contextMenuOpenRejections)
        self.__menu.addSeparator()
        self.__menu.addAction(
            UI.PixmapCache.getIcon("print.png"),
            self.tr('Print'), self.__contextMenuPrintFile)
        self.__menu.addSeparator()
        self.copyPathAct = self.__menu.addAction(
            self.tr("Copy Path to Clipboard"),
            self.__contextMenuCopyPathToClipboard)
        
    def __showMenu(self, point):
        """
        Private slot to handle the customContextMenuRequested signal of
        the viewlist.
        
        @param point position to open the menu at (QPoint)
        """
        if self.editors:
            itm = self.viewlist.itemAt(point)
            if itm is not None:
                row = self.viewlist.row(itm)
                self.contextMenuEditor = self.editors[row]
                self.contextMenuIndex = row
                if self.contextMenuEditor:
                    self.saveMenuAct.setEnabled(
                        self.contextMenuEditor.isModified())
                    fileName = self.contextMenuEditor.getFileName()
                    self.copyPathAct.setEnabled(bool(fileName))
                    if fileName:
                        rej = "{0}.rej".format(fileName)
                        self.openRejectionsMenuAct.setEnabled(
                            os.path.exists(rej))
                    else:
                        self.openRejectionsMenuAct.setEnabled(False)
                    
                    self.closeOthersMenuAct.setEnabled(
                        self.viewlist.count() > 1)
                    
                    self.__menu.popup(self.viewlist.mapToGlobal(point))
        
    def canCascade(self):
        """
        Public method to signal if cascading of managed windows is available.
        
        @return flag indicating cascading of windows is available
        """
        return False
        
    def canTile(self):
        """
        Public method to signal if tiling of managed windows is available.
        
        @return flag indicating tiling of windows is available
        """
        return False
    
    def canSplit(self):
        """
        public method to signal if splitting of the view is available.
        
        @return flag indicating splitting of the view is available.
        """
        return True
        
    def tile(self):
        """
        Public method to tile the managed windows.
        """
        pass
        
    def cascade(self):
        """
        Public method to cascade the managed windows.
        """
        pass
        
    def _removeAllViews(self):
        """
        Protected method to remove all views (i.e. windows).
        """
        self.viewlist.clear()
        for win in self.editors:
            for stack in self.stacks:
                if stack.hasEditor(win):
                    stack.removeWidget(win)
                    break
            win.closeIt()
        
    def _removeView(self, win):
        """
        Protected method to remove a view (i.e. window).
        
        @param win editor window to be removed
        """
        self.__inRemoveView = True
        ind = self.editors.index(win)
        itm = self.viewlist.takeItem(ind)
        if itm:
            del itm
        for stack in self.stacks:
            if stack.hasEditor(win):
                stack.removeWidget(win)
                break
        win.closeIt()
        self.__inRemoveView = False
        if ind > 0:
            ind -= 1
        else:
            if len(self.editors) > 1:
                ind = 1
            else:
                return
        stack.setCurrentWidget(stack.firstEditor())
        self._showView(self.editors[ind].parent())
        
        aw = self.activeWindow()
        fn = aw and aw.getFileName() or None
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, aw.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(aw)
        
    def _addView(self, win, fn=None, noName="", next=False):
        """
        Protected method to add a view (i.e. window).
        
        @param win editor assembly to be added
        @param fn filename of this editor (string)
        @param noName name to be used for an unnamed editor (string)
        @param next flag indicating to add the view next to the current
            view (bool)
        """
        editor = win.getEditor()
        if fn is None:
            if not noName:
                self.untitledCount += 1
                noName = self.tr("Untitled {0}").format(self.untitledCount)
            self.viewlist.addItem(noName)
            editor.setNoName(noName)
        else:
            txt = os.path.basename(fn)
            if not QFileInfo(fn).isWritable():
                txt = self.tr("{0} (ro)").format(txt)
            itm = QListWidgetItem(txt)
            itm.setToolTip(fn)
            self.viewlist.addItem(itm)
        self.currentStack.addWidget(win)
        self.currentStack.setCurrentWidget(win)
        editor.captionChanged.connect(self.__captionChange)
        editor.cursorLineChanged.connect(self.__cursorLineChanged)
        
        index = self.editors.index(editor)
        self.viewlist.setCurrentRow(index)
        editor.setFocus()
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)
        
    def __captionChange(self, cap, editor):
        """
        Private method to handle caption change signals from the editor.
        
        Updates the listwidget text to reflect the new caption information.
        
        @param cap Caption for the editor (string)
        @param editor Editor to update the caption for
        """
        fn = editor.getFileName()
        if fn:
            self.setEditorName(editor, fn)
        
    def __cursorLineChanged(self, lineno):
        """
        Private slot to handle a change of the current editor's cursor line.
        
        @param lineno line number of the current editor's cursor (zero based)
        """
        editor = self.sender()
        if editor:
            fn = editor.getFileName()
            if fn:
                self.editorLineChanged.emit(fn, lineno + 1)
        
    def _showView(self, win, fn=None):
        """
        Protected method to show a view (i.e. window).
        
        @param win editor assembly to be shown
        @param fn filename of this editor (string)
        """
        editor = win.getEditor()
        for stack in self.stacks:
            if stack.hasEditor(editor):
                stack.setCurrentWidget(win)
                self.currentStack = stack
                break
        index = self.editors.index(editor)
        self.viewlist.setCurrentRow(index)
        editor.setFocus()
        fn = editor.getFileName()
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)
        
    def __showSelectedView(self, row):
        """
        Private slot called to show a view selected in the list.
        
        @param row row number of the item clicked on (integer)
        """
        if row != -1:
            self._showView(self.editors[row].parent())
            self._checkActions(self.editors[row])
        
    def activeWindow(self):
        """
        Public method to return the active (i.e. current) window.
        
        @return reference to the active editor
        """
        return self.currentStack.currentWidget()
        
    def showWindowMenu(self, windowMenu):
        """
        Public method to set up the viewmanager part of the Window menu.
        
        @param windowMenu reference to the window menu
        """
        pass
        
    def _initWindowActions(self):
        """
        Protected method to define the user interface actions for window
        handling.
        """
        pass
        
    def setEditorName(self, editor, newName):
        """
        Public method to change the displayed name of the editor.
        
        @param editor editor window to be changed
        @param newName new name to be shown (string)
        """
        if newName:
            currentRow = self.viewlist.currentRow()
            index = self.editors.index(editor)
            txt = os.path.basename(newName)
            if not QFileInfo(newName).isWritable():
                txt = self.tr("{0} (ro)").format(txt)
            itm = self.viewlist.item(index)
            itm.setText(txt)
            itm.setToolTip(newName)
            self.viewlist.setCurrentRow(currentRow)
            self.changeCaption.emit(newName)
            
    def _modificationStatusChanged(self, m, editor):
        """
        Protected slot to handle the modificationStatusChanged signal.
        
        @param m flag indicating the modification status (boolean)
        @param editor editor window changed
        """
        currentRow = self.viewlist.currentRow()
        index = self.editors.index(editor)
        keys = []
        if m:
            keys.append("fileModified.png")
        if editor.hasSyntaxErrors():
            keys.append("syntaxError22.png")
        elif editor.hasWarnings():
            keys.append("warning22.png")
        if not keys:
            keys.append("empty.png")
        self.viewlist.item(index).setIcon(
            UI.PixmapCache.getCombinedIcon(keys))
        self.viewlist.setCurrentRow(currentRow)
        self._checkActions(editor)
        
    def _syntaxErrorToggled(self, editor):
        """
        Protected slot to handle the syntaxerrorToggled signal.
        
        @param editor editor that sent the signal
        """
        currentRow = self.viewlist.currentRow()
        index = self.editors.index(editor)
        keys = []
        if editor.isModified():
            keys.append("fileModified.png")
        if editor.hasSyntaxErrors():
            keys.append("syntaxError22.png")
        elif editor.hasWarnings():
            keys.append("warning22.png")
        if not keys:
            keys.append("empty.png")
        self.viewlist.item(index).setIcon(
            UI.PixmapCache.getCombinedIcon(keys))
        self.viewlist.setCurrentRow(currentRow)
        
        ViewManager._syntaxErrorToggled(self, editor)
        
    def addSplit(self):
        """
        Public method used to split the current view.
        """
        stack = StackedWidget(self.stackArea)
        stack.show()
        self.stackArea.addWidget(stack)
        self.stacks.append(stack)
        self.currentStack = stack
        stack.currentChanged.connect(self.__currentChanged)
        stack.installEventFilter(self)
        if self.stackArea.orientation() == Qt.Horizontal:
            size = self.stackArea.width()
        else:
            size = self.stackArea.height()
        self.stackArea.setSizes(
            [int(size / len(self.stacks))] * len(self.stacks))
        self.splitRemoveAct.setEnabled(True)
        self.nextSplitAct.setEnabled(True)
        self.prevSplitAct.setEnabled(True)
        
    def removeSplit(self):
        """
        Public method used to remove the current split view.
        
        @return flag indicating successfull removal
        """
        if len(self.stacks) > 1:
            stack = self.currentStack
            res = True
            savedEditors = stack.editors[:]
            for editor in savedEditors:
                res &= self.closeEditor(editor)
            if res:
                try:
                    i = self.stacks.index(stack)
                except ValueError:
                    return True
                if i == len(self.stacks) - 1:
                    i -= 1
                self.stacks.remove(stack)
                stack.close()
                self.currentStack = self.stacks[i]
                if len(self.stacks) == 1:
                    self.splitRemoveAct.setEnabled(False)
                    self.nextSplitAct.setEnabled(False)
                    self.prevSplitAct.setEnabled(False)
                return True
        
        return False
        
    def getSplitOrientation(self):
        """
        Public method to get the orientation of the split view.
        
        @return orientation of the split (Qt.Horizontal or Qt.Vertical)
        """
        return self.stackArea.orientation()
        
    def setSplitOrientation(self, orientation):
        """
        Public method used to set the orientation of the split view.
        
        @param orientation orientation of the split
                (Qt.Horizontal or Qt.Vertical)
        """
        self.stackArea.setOrientation(orientation)
        
    def nextSplit(self):
        """
        Public slot used to move to the next split.
        """
        aw = self.activeWindow()
        _hasFocus = aw and aw.hasFocus()
        ind = self.stacks.index(self.currentStack) + 1
        if ind == len(self.stacks):
            ind = 0
        
        self.currentStack = self.stacks[ind]
        if _hasFocus:
            aw = self.activeWindow()
            if aw:
                aw.setFocus()
        
        index = self.editors.index(self.currentStack.currentWidget())
        self.viewlist.setCurrentRow(index)
        
    def prevSplit(self):
        """
        Public slot used to move to the previous split.
        """
        aw = self.activeWindow()
        _hasFocus = aw and aw.hasFocus()
        ind = self.stacks.index(self.currentStack) - 1
        if ind == -1:
            ind = len(self.stacks) - 1
        
        self.currentStack = self.stacks[ind]
        if _hasFocus:
            aw = self.activeWindow()
            if aw:
                aw.setFocus()
        index = self.editors.index(self.currentStack.currentWidget())
        self.viewlist.setCurrentRow(index)
        
    def __contextMenuClose(self):
        """
        Private method to close the selected editor.
        """
        if self.contextMenuEditor:
            self.closeEditorWindow(self.contextMenuEditor)
        
    def __contextMenuCloseOthers(self):
        """
        Private method to close the other editors.
        """
        index = self.contextMenuIndex
        for i in list(range(self.viewlist.count() - 1, index, -1)) + \
                list(range(index - 1, -1, -1)):
            editor = self.editors[i]
            self.closeEditorWindow(editor)
        
    def __contextMenuCloseAll(self):
        """
        Private method to close all editors.
        """
        savedEditors = self.editors[:]
        for editor in savedEditors:
            self.closeEditorWindow(editor)
        
    def __contextMenuSave(self):
        """
        Private method to save the selected editor.
        """
        if self.contextMenuEditor:
            self.saveEditorEd(self.contextMenuEditor)
        
    def __contextMenuSaveAs(self):
        """
        Private method to save the selected editor to a new file.
        """
        if self.contextMenuEditor:
            self.saveAsEditorEd(self.contextMenuEditor)
        
    def __contextMenuSaveAll(self):
        """
        Private method to save all editors.
        """
        self.saveEditorsList(self.editors)
        
    def __contextMenuOpenRejections(self):
        """
        Private slot to open a rejections file associated with the selected
        editor.
        """
        if self.contextMenuEditor:
            fileName = self.contextMenuEditor.getFileName()
            if fileName:
                rej = "{0}.rej".format(fileName)
                if os.path.exists(rej):
                    self.openSourceFile(rej)
        
    def __contextMenuPrintFile(self):
        """
        Private method to print the selected editor.
        """
        if self.contextMenuEditor:
            self.printEditor(self.contextMenuEditor)
        
    def __contextMenuCopyPathToClipboard(self):
        """
        Private method to copy the file name of the selected editor to the
        clipboard.
        """
        if self.contextMenuEditor:
            fn = self.contextMenuEditor.getFileName()
            if fn:
                cb = QApplication.clipboard()
                cb.setText(fn)
        
    def __currentChanged(self, index):
        """
        Private slot to handle the currentChanged signal.
        
        @param index index of the current editor
        """
        if index == -1 or not self.editors:
            return
        
        editor = self.activeWindow()
        if editor is None:
            return
        
        self._checkActions(editor)
        editor.setFocus()
        fn = editor.getFileName()
        if fn:
            self.changeCaption.emit(fn)
            if not self.__inRemoveView:
                self.editorChanged.emit(fn)
                self.editorLineChanged.emit(
                    fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)
        
        cindex = self.editors.index(editor)
        self.viewlist.setCurrentRow(cindex)
        
    def eventFilter(self, watched, event):
        """
        Public method called to filter the event queue.
        
        @param watched the QObject being watched
        @param event the event that occurred
        @return flag indicating, if we handled the event
        """
        if event.type() == QEvent.MouseButtonPress and \
           not event.button() == Qt.RightButton:
            switched = True
            if isinstance(watched, QStackedWidget):
                switched = watched is not self.currentStack
                self.currentStack = watched
            elif isinstance(watched, QScintilla.Editor.Editor):
                for stack in self.stacks:
                    if stack.hasEditor(watched):
                        switched = stack is not self.currentStack
                        self.currentStack = stack
                        break
            currentWidget = self.currentStack.currentWidget()
            if currentWidget:
                index = self.editors.index(currentWidget)
                self.viewlist.setCurrentRow(index)
            
            aw = self.activeWindow()
            if aw is not None:
                self._checkActions(aw)
                aw.setFocus()
                fn = aw.getFileName()
                if fn:
                    self.changeCaption.emit(fn)
                    if switched:
                        self.editorChanged.emit(fn)
                        self.editorLineChanged.emit(
                            fn, aw.getCursorPosition()[0] + 1)
                else:
                    self.changeCaption.emit("")
                self.editorChangedEd.emit(aw)
        
        return False