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)
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
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)
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)
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)
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)
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.""")
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()
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()
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)
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()
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)
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)
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
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)
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)
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)
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())
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()
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()
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)
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()
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
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)
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()
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()
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)
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()
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()
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")
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
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_()
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
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(), ' 삭제')
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()
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)
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
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()
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)
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