def __init__(self, navbar, label, controls, parent=None): super().__init__(parent) vbox = QVBoxLayout() vbox.insertLayout(-1, navbar, stretch=2) vbox.addWidget(StateLabel(label), stretch=1) vbox.insertLayout(-1, controls, stretch=9) self.setLayout(vbox)
class FighterView(QWidget): def __init__(self, parent): super(FighterView, self).__init__(parent) self.initButton() self.initLayout() self.mainWindow = parent def initButton(self): # next map button self.highlight_grid_button = QPushButton("highlight grid") self.highlight_grid_button.clicked.connect(self.highlightGrid) def initLayout(self): self.layout = QVBoxLayout() self.setLayout(self.layout) self.button_combat_box = QGroupBox() self.button_combat_layout = QHBoxLayout() self.button_combat_box.setLayout(self.button_combat_layout) self.button_combat_layout.addWidget(self.highlight_grid_button) self.layout.addWidget(self.button_combat_box) self.layout.insertLayout(0, self.button_combat_layout) def highlightGrid(self, event): self.mainWindow.hide() combat_grid = Grid(env.COMBAT_R, env.VCELLS, env.HCELLS) combat_grid.parse() self.overlay = GridOverlay(combat_grid) self.overlay.highlightEnded.connect(self.mainWindow.show) self.overlay.highlight(2)
class TwinContainer(QWidget): def __init__(self, dock_widget, *args, parent=None, **kwargs): super(TwinContainer, self).__init__(parent) self.parent = parent self.layout = QVBoxLayout(self) self.layouts = {} self.dock_widget = dock_widget def add_to_layout(self, name, widget): if name not in self.layouts: keys = sorted(list(self.layouts) + [name]) idx = keys.index(name) self.layouts[name] = QHBoxLayout() self.layout.insertLayout(idx, self.layouts[name]) self.layouts[name].addWidget(widget) widget.mpl_canvas.sig_twin.connect(self.mouse_twin_event) @pyqtSlot(object) def mouse_twin_event(self, event): if event.button == 1: self.parent.select_tab(self.sender()) elif event.button == 3: plot_widget = self.sender().my_parent plot_widget.trim_widget.sig_set_state.emit(True) def handle_show(self, name, widget, state): widget.setVisible(state) if state: self.layouts[name].addWidget(widget) else: self.layouts[name].removeWidget(widget)
def __init__(self, *args): QWidget.__init__(self, *args) vLayout = QVBoxLayout(self) self.setLayout(vLayout) hLayout = QHBoxLayout() vLayout.insertLayout(0, hLayout) tableModel = Model(self) self.ViewA = QTableView(self) self.ViewA.setModel(tableModel) self.ViewA.clicked.connect(self.viewClicked) self.ViewA.setSelectionBehavior(QTableView.SelectRows) self.ViewA.setSelectionMode(QTableWidget.SingleSelection) hLayout.addWidget(self.ViewA) insertButton = QPushButton('Insert Row Above Selection') insertButton.setObjectName('insertButton') insertButton.clicked.connect(self.buttonClicked) removeButton = QPushButton('Remove Selected Item') removeButton.setObjectName('removeButton') removeButton.clicked.connect(self.buttonClicked) vLayout.addWidget(insertButton) vLayout.addWidget(removeButton)
class MyApp(object): def __init__(self): super(MyApp, self).__init__() self.mainWidget = QWidget() self.mainLayout = QVBoxLayout() self.mainWidget.setLayout(self.mainLayout) self.hLayout = QHBoxLayout() self.mainLayout.insertLayout(0, self.hLayout) self.listA = QTreeWidget() self.listA.setColumnCount(3) self.listA.setHeaderLabels(['Checkbox','Name','Data']) for i in range(3): item = QTreeWidgetItem() item.setCheckState(0,QtCore.Qt.Checked) item.setText(1, 'Item '+str(i)) item.setData(2, QtCore.Qt.UserRole, id(item) ) item.setText(2, str(id(item) ) ) self.listA.addTopLevelItem(item) self.hLayout.addWidget(self.listA) self.buttonGroupbox = QGroupBox() self.buttonlayout = QVBoxLayout() self.buttonGroupbox.setLayout(self.buttonlayout) okButton = QPushButton('Remove Selected') okButton.clicked.connect(self.removeSel) self.buttonlayout.addWidget(okButton) getDataButton = QPushButton('Get Items Data') getDataButton.clicked.connect(self.getItemsData) self.buttonlayout.addWidget(getDataButton) self.mainLayout.addWidget(self.buttonGroupbox) self.mainWidget.show() sys.exit(app.exec_()) def removeSel(self): listItems=self.listA.selectedItems() if not listItems: return for item in listItems: itemIndex=self.listA.indexOfTopLevelItem(item) self.listA.takeTopLevelItem(itemIndex) print('\n\t Number of items remaining', self.listA.topLevelItemCount()) def getItemsData(self): for i in range(self.listA.topLevelItemCount()): item=self.listA.topLevelItem(i) itmData=item.data(2, QtCore.Qt.UserRole) itemId=itmData.toPyObject() print('\n\t Item Id Stored as Item Data:', itemId, 'Item Checkbox State:', item.checkState(0))
def __init__(self, parent=None): QWidget.__init__(self, parent) def inttocheck(value): if value: return Qt.Checked return Qt.Unchecked cparser = PuddleConfig() self.extpattern = QLineEdit() self.extpattern.setText( cparser.load('playlist', 'extpattern', '%artist% - %title%')) self.extinfo = QCheckBox( translate("Playlist Settings", '&Write extended info'), self) self.extinfo.stateChanged.connect(self.extpattern.setEnabled) self.extinfo.setCheckState( inttocheck(cparser.load('playlist', 'extinfo', 1, True))) self.extpattern.setEnabled(self.extinfo.checkState()) self.reldir = QCheckBox( translate("Playlist Settings", 'Entries &relative to working directory')) self.reldir.setCheckState( inttocheck(cparser.load('playlist', 'reldir', 0, True))) self.windows_separator = QCheckBox( translate("Playlist Settings", 'Use windows path separator (\\)')) self.windows_separator.setCheckState( inttocheck(cparser.load('playlist', 'windows_separator', 0, True))) self.filename = QLineEdit() self.filename.setText( cparser.load('playlist', 'filepattern', 'puddletag.m3u')) label = QLabel(translate("Playlist Settings", '&Filename pattern.')) label.setBuddy(self.filename) hbox = QHBoxLayout() hbox.addSpacing(10) hbox.addWidget(self.extpattern) vbox = QVBoxLayout() [ vbox.addWidget(z) for z in (self.extinfo, self.reldir, self.windows_separator, label, self.filename) ] vbox.insertLayout(1, hbox) vbox.addStretch() vbox.insertSpacing(3, 5) vbox.insertSpacing(5, 5) self.setLayout(vbox)
class HtmlDialog(QDialog): """Generate a more complex HTML Viewer dialog window, with Prin/Close Buttons""" def __init__(self, parent=None): """ Constructor of HtmlDialog :param parent: dialog parent :return: """ super().__init__( parent, QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowMinMaxButtonsHint | QtCore.Qt.WindowCloseButtonHint) self.horzLayout = QHBoxLayout() self.vertLayout = QVBoxLayout() self.spacer = QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Fixed) # print button self.btnPrint = QPushButton(self) self.btnPrint.setText(translate("ReportDialog", "Print")) self.btnPrint.setMaximumWidth(100) self.btnClose = QPushButton(self) self.btnClose.setText(translate("ReportDialog", "Close")) self.btnClose.setMaximumWidth(100) # webview for help information self.wv = HtmlView(self) # build structure self.horzLayout.addWidget(self.btnPrint) self.horzLayout.addWidget(self.btnClose) self.horzLayout.addSpacerItem(self.spacer) self.vertLayout.insertLayout(0, self.horzLayout) self.vertLayout.addWidget(self.wv) self.setLayout(self.vertLayout) self.btnPrint.clicked.connect(self.wv.execpreview) self.btnClose.clicked.connect(self.close) def showHtml(self, html): """ Show rendered value of ``html`` :param html: raw html data """ print(html) self.wv.setHtml_(html) self.show()
def __init__(self, label_text): super(GetMapField, self).__init__() self.chooseStartMapLyt = QHBoxLayout() self.startMapLbl = QLabel(label_text) self.xcoords = MapCoordField("x coord") self.ycoords = MapCoordField("y coord") self.chooseStartMapLyt.addWidget(self.startMapLbl) self.chooseStartMapLyt.addWidget(self.xcoords) self.chooseStartMapLyt.addWidget(self.ycoords) self.set_button = QPushButton("set") self.chooseStartMapLyt.addWidget(self.set_button) window_layout = QVBoxLayout() window_layout.insertLayout(0, self.chooseStartMapLyt) self.setLayout(window_layout)
class HtmlDialog(QDialog): """Generate a more complex HTML Viewer dialog window, with Prin/Close Buttons""" def __init__(self, parent = None): """ Constructor of HtmlDialog :param parent: dialog parent :return: """ super().__init__(parent, QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowMinMaxButtonsHint | QtCore.Qt.WindowCloseButtonHint) self.horzLayout = QHBoxLayout() self.vertLayout = QVBoxLayout() self.spacer = QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Fixed) # print button self.btnPrint = QPushButton(self) self.btnPrint.setText(translate("ReportDialog", "Print")) self.btnPrint.setMaximumWidth(100) self.btnClose = QPushButton(self) self.btnClose.setText(translate("ReportDialog", "Close")) self.btnClose.setMaximumWidth(100) # webview for help information self.wv = HtmlView(self) # build structure self.horzLayout.addWidget(self.btnPrint) self.horzLayout.addWidget(self.btnClose) self.horzLayout.addSpacerItem(self.spacer) self.vertLayout.insertLayout(0, self.horzLayout) self.vertLayout.addWidget(self.wv) self.setLayout(self.vertLayout) self.btnPrint.clicked.connect(self.wv.execpreview) self.btnClose.clicked.connect(self.close) def showHtml(self, html): """ Show rendered value of ``html`` :param html: raw html data """ print(html) self.wv.setHtml_(html) self.show()
class ViewTab(QWidget): LAYERS = ('original', 'model', 'decontaminated residual', 'model residual') def __init__(self, inspector, *args): super().__init__(*args) self.inspector = inspector self.current_detector = 1 self.current_dither = 1 self.boxes_visible = False self.pixmap_item = {} # {dither: {detector: {layer: pixmap}} for dither in (1, 2, 3, 4): self.pixmap_item[dither] = {} for detector in range(1, 17): self.pixmap_item[dither][detector] = { layer: None for layer in self.LAYERS } self._layout = QVBoxLayout() self._layout.setContentsMargins(5, 0, 5, 5) self._layout.setSpacing(0) self.selection_area = ObjectSelectionArea() self._layout.insertLayout(0, self.selection_area) # create and add the view area self.view = View(self) self.scene = QGraphicsScene() self._background = QBrush(QColor('#56595e')) executable_directory = os.path.dirname( os.path.abspath(inspect.stack()[0][1])) self._blank_image = QPixmap( QImage(executable_directory + '/load-exposure-message.svg')) self.scene.setBackgroundBrush(self._background) self._blank_pixmap = self.scene.addPixmap(self._blank_image) self.view.setScene(self.scene) self._layout.addWidget(self.view) self.setLayout(self._layout) self.current_layer = 'original' def init_view(self): # display dither 1, detector 1 in single view dithers = list(self.inspector.exposures.keys()) self.current_dither = dithers[0] self.update_view() self.selection_area.dither_selector.setEnabled(True) self.selection_area.detector_selector.setEnabled(True) for i, dither in enumerate(dithers): self.selection_area.dither_selector.addItem(str(dither), dither) self.selection_area.dither_selector.activated[int].connect( self.change_dither) for detector in range(1, 17): self.selection_area.detector_selector.addItem( str(detector), detector) self.selection_area.detector_selector.activated[int].connect( self.change_detector) for layer in self.LAYERS: self.selection_area.data_selector.addItem(layer) self.selection_area.data_selector.activated[str].connect( self.change_layer) self.boxes_visible = False def change_dither(self, dither_index): self.current_dither = self.selection_area.dither_selector.itemData( dither_index) if dither_index != self.selection_area.dither_selector.currentIndex(): self.selection_area.dither_selector.blockSignals(True) self.selection_area.dither_selector.setCurrentIndex(dither_index) self.selection_area.dither_selector.blockSignals(False) self.update_view() def change_detector(self, detector_index): self.current_detector = self.selection_area.detector_selector.itemData( detector_index) if detector_index != self.selection_area.detector_selector.currentIndex( ): self.selection_area.detector_selector.blockSignals(True) self.selection_area.detector_selector.setCurrentIndex( detector_index) self.selection_area.detector_selector.blockSignals(False) self.update_view() def update_view(self): if self.current_dither is None or self.current_detector is None: return data = self.inspector.exposures[self.current_dither][ self.current_detector].data pixmap, _ = utils.np_to_pixmap(data, data.max()) self.scene.clear() self.current_layer = 'original' original_index = self.selection_area.data_selector.findText( self.current_layer) self.selection_area.data_selector.setCurrentIndex(original_index) self.pixmap_item[self.current_dither][self.current_detector][ self.current_layer] = self.scene.addPixmap(pixmap) self.pixmap_item[self.current_dither][self.current_detector][ self.current_layer].setZValue(-1.0) self.boxes_visible = False self.inspector.rename_tab(self) self.inspector.detector_info_window.update_detector( self.current_dither, self.current_detector) if self.inspector.collection is not None: collection = self.inspector.collection dither = self.current_dither detector = self.current_detector if dither in collection.get_dithers( ) and detector in collection.get_detectors(dither): self.selection_area.data_selector.setEnabled(True) else: self.selection_area.data_selector.setDisabled(True) else: self.selection_area.data_selector.setDisabled(True) def show_bounding_box(self, dither, detector, object_id): spec = self.inspector.collection.get_spectrum(dither, detector, object_id) return self.draw_spec_box(spec) def draw_spec_box(self, spec): if spec is not None: left = spec.x_offset height, width = spec.science.shape top = spec.y_offset rect = SpecBox(left, top, width, height) rect.spec = spec model = self.inspector.collection.get_model(self.current_dither, self.current_detector, spec.id, order=1) if model is not None: rect.model = model.pixels self.scene.addItem(rect) return rect, QPointF(left, top) def show_bounding_boxes(self, dither, detector): for spec in self.inspector.collection.get_spectra(dither, detector): self.draw_spec_box(spec) def toggle_bounding_boxes(self): if not self.boxes_visible: self.show_boxes_in_view() else: self.remove_boxes_in_view() def toggle_decontaminated(self): if not self.decontamination_visible: self.show_decontaminated_in_view() else: self.remove_decontaminated_in_view() def show_boxes_in_view(self): self.show_bounding_boxes(self.current_dither, self.current_detector) self.boxes_visible = True def remove_boxes_in_view(self): for item in self.scene.items(): if isinstance(item, SpecBox) and not item.pinned: self.scene.removeItem(item) self.boxes_visible = False def active_detector_has_spectral_data(self): if self.inspector.exposures is None or self.inspector.collection is None: return False dith = self.current_dither det = self.current_detector return dith in self.inspector.collection.get_dithers( ) and det in self.inspector.collection.get_detectors(dith) def select_spectrum(self): object_id = self.selection_area.searchbox.text() spec = self.select_spectrum_by_id(object_id) if spec is None: self.selection_area.searchbox.setText('Not found') def select_spectrum_by_id(self, object_id): if self.inspector.collection is None or self.inspector.exposures is None: return None spec = self.inspector.collection.get_spectrum(self.current_dither, self.current_detector, object_id) if spec is not None: # make sure that the spec is not already pinned for pinned_item in self.get_pinned_spectra(): if pinned_item.spec.id == object_id: pinned_item.grabKeyboard() return None # this point is only reached if the specified spectrum is not already pinned. Pin the spec spec_box, pos = self.show_bounding_box(self.current_dither, self.current_detector, object_id) spec_box.pin(pos) spec_box.grabKeyboard() return spec def unselect_spectrum_by_id(self, object_id): for item in self.get_pinned_spectra(): if item.spec.id == object_id: self.scene.removeItem(item) def get_pinned_spectra(self): items = [] for item in self.scene.items(): if isinstance(item, SpecBox) and item.pinned: items.append(item) return items def get_model_residual_image(self): """removes all model contaminants from the detector and returns the model residual""" data = self.inspector.exposures[self.current_dither][ self.current_detector].data return data - self.get_model_image() def get_model_image(self): data = self.inspector.exposures[self.current_dither][ self.current_detector].data sim = np.zeros_like(data) for object_id in self.inspector.collection.get_object_ids( self.current_dither, self.current_detector): model = self.inspector.collection.get_model( self.current_dither, self.current_detector, object_id, 1) if model is not None: height, width = model.pixels.shape region = (slice(model.y_offset, model.y_offset + height), slice(model.x_offset, model.x_offset + width)) sim[region] += model.pixels else: # there is no model because this was not contaminated. The spectrum itself is essentially the model. model = self.inspector.collection.get_spectrum( self.current_dither, self.current_detector, object_id) height, width = model.science.shape region = (slice(model.y_offset, model.y_offset + height), slice(model.x_offset, model.x_offset + width)) sim[region] += model.science return sim def get_residual_image(self): data = self.inspector.exposures[self.current_dither][ self.current_detector].data decon = np.zeros_like(data) for object_id in self.inspector.collection.get_object_ids( self.current_dither, self.current_detector): spec = self.inspector.collection.get_spectrum( self.current_dither, self.current_detector, object_id) height, width = spec.science.shape region = (slice(spec.y_offset, spec.y_offset + height), slice(spec.x_offset, spec.x_offset + width)) decon[region] += spec.science return data - decon def get_pixmap(self, image_data): data = self.inspector.exposures[self.current_dither][ self.current_detector].data _, pixmap = utils.np_to_pixmap(data, data.max(), data.min(), image_data) return pixmap def remove_pinned_boxes(self): for item in self.scene.items(): if isinstance(item, SpecBox) and item.pinned: self.scene.removeItem(item) if len(self.scene.items()) == 0: self.boxes_visible = False def n_pinned_boxes(self): n = 0 for item in self.scene.items(): if isinstance(item, SpecBox) and item.pinned: n += 1 return n def change_layer(self, layer): if self.current_layer == layer: return print('switching to', layer) if layer not in self.LAYERS: return self.scene.removeItem(self.pixmap_item[self.current_dither][ self.current_detector][self.current_layer]) if self.pixmap_item[self.current_dither][ self.current_detector][layer] is None: if layer == 'original': image_data = self.inspector.exposures[self.current_dither][ self.current_detector].data pixmap = utils.np_to_pixmap(image_data, image_data.max()) elif layer == 'model residual': image_data = self.get_model_residual_image() pixmap = self.get_pixmap(image_data) elif layer == 'decontaminated residual': image_data = self.get_residual_image() pixmap = self.get_pixmap(image_data) elif layer == 'model': print('switching to the model layer') image_data = self.get_model_image() pixmap = self.get_pixmap(image_data) pixmap_item = self.scene.addPixmap(pixmap) pixmap_item.setZValue(-1.0) self.pixmap_item[self.current_dither][ self.current_detector][layer] = pixmap_item else: self.scene.addItem(self.pixmap_item[self.current_dither][ self.current_detector][layer]) self.current_layer = layer
class TrackWindow(QMainWindow): def __init__(self, people): super().__init__() self.people = people self.setWindowTitle("Tracking stats") self.result = [] self.central = QWidget(self) self.vlayout = QVBoxLayout() self.button_names = [] for person in self.people: self.upadte_column(person) self.add_button = QPushButton('Add new people') self.add_button.setMinimumSize(int((QDesktopWidget().screenGeometry(-1).width())/4), int((QDesktopWidget().screenGeometry(-1).height())/15)) self.vlayout.addWidget(self.add_button) self.central.setLayout(self.vlayout) self.setCentralWidget(self.central) self.add_button.clicked.connect(self.add_person) def add_person(self): self.name_person, okPressed = QInputDialog.getText(self, "Name","Name of a person:",QLineEdit.Normal, "") if okPressed and self.name_person != '': self.window = QWidget() self.window.setWindowTitle('Doubleclick your image') okButtons = QHBoxLayout() windowLayout = QVBoxLayout() ''' self.ok_button = QPushButton('Ok') self.cancel_button = QPushButton('Cancel') okButtons.addWidget(self.ok_button) okButtons.addWidget(self.cancel_button) self.ok_button.setEnabled(False) self.ok_button.setCheckable(True) ''' self.model = QFileSystemModel() self.model.setRootPath('') self.tree = QTreeView() self.tree.setModel(self.model) self.tree.setIndentation(20) self.tree.setSortingEnabled(True) self.tree.setWindowTitle("Dir View") self.tree.setMinimumSize(int((QDesktopWidget().screenGeometry(-1).width())/2), int((QDesktopWidget().screenGeometry(-1).height())/2)) windowLayout.addWidget(self.tree) windowLayout.addLayout(okButtons) self.window.setLayout(windowLayout) self.tree.doubleClicked.connect(self.on_treeView_clicked) self.window.show() else: print('Enter something') @pyqtSlot(QModelIndex) def on_treeView_clicked(self, index): indexItem = self.model.index(index.row(), 0, index.parent()) values = [] fileName = self.model.fileName(indexItem) filePath = self.model.filePath(indexItem) values.append(filePath) self.result.append(self.name_person) self.result.append(values) def update_stats(self): for person in self.people: ind = str(self.people.index(person)) for key, value in person.__dict__.items(): parametr = getattr(self, key+ind) if key != 'name': time_val = float(value.split(' ')[-2]) indx = value.find(str(time_val)) parametr.setText(key+' time: '+value[:indx]+str(int(time_val))+value[value.find('sec')-1:]) def upadte_column(self, person): ind = self.people.index(person) setattr(self, 'button'+str(ind), QVBoxLayout()) self.button_names.append('button'+str(ind)) if ind%2 == 0: setattr(self, 'buttons'+str(ind), QHBoxLayout()) for key, value in person.__dict__.items(): setattr(self, key+str(ind), QLabel(self)) parametr = getattr(self, key+str(ind)) if key != 'name': time_val = float(value.split(' ')[-2]) indx = value.find(str(time_val)) parametr.setText(key+' time: '+value[:indx]+str(int(time_val))+value[value.find('sec')-1:]) else: parametr.setText(value) but_ind = getattr(self, 'button'+str(ind)) but_ind.addWidget(parametr) if ind%2 == 0: box_lay = getattr(self, 'buttons'+str(ind)) else: box_lay = getattr(self, 'buttons'+str(ind-1)) box_lay.addLayout(but_ind) self.vlayout.insertLayout(-2,box_lay) def update_row(self): key = list(self.people[-1].__dict__.keys())[-1] count = 0 for name in self.button_names: but_ind = getattr(self, name) setattr(self, key+str(count), QLabel(self)) parametr = getattr(self, key+str(count)) parametr.setText(key+' time: 0 hrs 0 min 0.0 sec') but_ind.addWidget(parametr) count += 1
class AutoEditor(TableInfoChanger): """Ух, шайтан класс, в которых запихнули кишки из PathEditor, который основан на ViewShower, со всеми вытекающими для совместимости""" combo_update = ViewInfoChanger.combo_update def __init__(self, header, info, parent): super().__init__(header, info, parent) self.combo_change_idx = {"Водитель": {}} self.slave_drivers_layout = QVBoxLayout() push_btn = self.main_layout.takeAt(self.main_layout.count() - 1).widget() self.auto_id = info[0] add_btn = QPushButton("Добавить") add_btn.clicked.connect(lambda e: self.add_cell(-1)) self.main_layout.addWidget(add_btn) way = self.db.execute( f"SELECT CONCAT(`Фамилия`,' ', `Имя`,' ',`Отчество`), `назначение автомобиля водителю`.`Табельный номер` FROM `назначение автомобиля водителю` join `водитель` on `водитель`.`Табельный номер` = `назначение автомобиля водителю`.`Табельный номер` where `Номер автомобиля` = '{self.auto_id}'" ) for pos, point in enumerate(way, start=-1): auto_info = str(point[0]) self.combo_change_idx["Водитель"][auto_info] = point[1] self.add_cell(pos, auto_info) self.main_layout.addLayout(self.slave_drivers_layout) self.main_layout.addWidget(push_btn) def add_cell(self, pos: int, txt=""): """Вставляет ячейку ниже активирующей кнопки для вставки на уровне надо передать ::pos:: = -1""" cell = QHBoxLayout() edi = QLineEdit() edi.setText(txt) add_btn = QPushButton("Добавить") del_btn = QPushButton("Удалить") cmb = QComboBox() cmb.addItem(txt) edi.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) cmb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) add_btn.clicked.connect(lambda e, c=cell: self.add_cell(c.pos)) del_btn.clicked.connect(lambda e, c=cell: self.del_cell(c.pos)) edi.editingFinished.connect( lambda c=cmb, t=edi.text: self.combo_update("Водитель", c, t() )) # le-kostyl cell.pos = pos cell.addWidget(edi) cell.addWidget(cmb) cell.addWidget(add_btn) cell.addWidget(del_btn) for i in range(pos + 1, self.slave_drivers_layout.count()): cell_to_move = self.slave_drivers_layout.itemAt(i) cell_to_move.pos += 1 cell.pos += 1 # для вставки ниже активированной кнопки self.slave_drivers_layout.insertLayout(cell.pos, cell) def del_cell(self, pos): cell: QVBoxLayout cell = self.slave_drivers_layout.takeAt(pos) for i in range(cell.count()): w = cell.takeAt(0).widget() w.deleteLater() cell.deleteLater() for i in range(pos, self.slave_drivers_layout.count()): cell_to_move = self.slave_drivers_layout.itemAt(i) cell_to_move.pos -= 1 def push_point_changes(self): params = [] for i in range(self.slave_drivers_layout.count()): cell = self.slave_drivers_layout.itemAt(i) w = cell.itemAt(1).widget() driver = w.currentText() if driver: driver_id = self.combo_change_idx["Водитель"][driver] params.append((driver_id, self.auto_id)) query = f" insert into `назначение автомобиля водителю` values(%s, %s)" self.db.execute( "delete from `назначение автомобиля водителю` where `Номер автомобиля` = %s", (self.auto_id, )) self.db.executemany(query, params) self.db.commit() def push_changes(self): self.push_point_changes() super().push_changes()
class GUI_HyperParameters(QWidget): def __init__(self, gui_play, cur_hps=None): super().__init__() self.gui_play = gui_play self.name_inpt_dict = {} self.structured_hyperparams = {} self.substructered_items = [] self.reference_hps = hp.AgentsHyperparameters() if cur_hps is None: self.hp = hp.AgentsHyperparameters() else: self.hp = cur_hps self.initUI() def initUI(self): self.resize(800, 600) self.center() self.setWindowTitle('Icon') self.setWindowIcon(QIcon('test.png')) self.v_main = QVBoxLayout() idx = 0 for attr in dir(self.hp): attr_name = str(attr) if not callable(getattr(self.hp, attr)) and not attr.startswith("__"): idx += 1 hbox = QHBoxLayout() lab = QLabel(attr_name, self) hbox.addWidget(lab) the_attrib = getattr(self.hp, attr_name) if isinstance(the_attrib, dict): inp = QComboBox() inp.addItems(the_attrib.keys()) self.structured_hyperparams[attr_name] = (idx, the_attrib) inp.activated.connect(self.activated_combobox) hbox.addWidget(inp) elif isinstance(the_attrib, list): list_key = the_attrib[0] reference = getattr(self.reference_hps, attr_name) if isinstance(reference, dict): if list_key in reference: reference[list_key] = the_attrib[1] inp = QComboBox() inp.addItems(reference.keys()) self.structured_hyperparams[attr_name] = ( idx, reference) inp.activated.connect(self.activated_combobox) hbox.addWidget(inp) else: inp = None else: inp = None else: inp = QLineEdit(str(the_attrib), self) hbox.addWidget(inp) if inp is not None: self.name_inpt_dict[attr_name] = inp self.v_main.addLayout(hbox) self.add_sub_inputs() self.start_btn = QPushButton("start", self) self.start_btn.clicked.connect(self.train_click) self.v_main.addWidget(self.start_btn) self.setLayout(self.v_main) self.show() def add_sub_inputs(self): items_per_row = 3 for sub_struct in self.substructered_items: for i in range(self.v_main.count()): layout_item = self.v_main.itemAt(i) if isinstance( layout_item, QHBoxLayout) and layout_item.layout() == sub_struct: clear_layout(layout_item) self.v_main.removeItem(layout_item) self.substructered_items.clear() for struct_hyper_key in self.structured_hyperparams.keys(): combo_box = self.name_inpt_dict[struct_hyper_key] new_items_dict = self.structured_hyperparams[struct_hyper_key][1][ combo_box.currentText()] new_item_keys = list(new_items_dict.keys()) for i in range(0, len(new_item_keys), items_per_row): h_outer = QHBoxLayout() for j in range(items_per_row): if i + j < len(new_item_keys): cur_key = new_item_keys[i + j] hbox = QHBoxLayout() hbox.addWidget(QLabel(cur_key, self)) inp = QLineEdit(str(new_items_dict[cur_key]), self) hbox.addWidget(inp) self.name_inpt_dict[cur_key] = inp h_outer.addLayout(hbox) self.substructered_items.append(h_outer) self.v_main.insertLayout( self.structured_hyperparams[struct_hyper_key][0], h_outer) def activated_combobox(self): print("connected") self.add_sub_inputs() def closeEvent(self, event): event.accept() def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) @pyqtSlot() def train_click(self): for attr in dir(self.hp): if not callable(getattr(self.hp, attr)) and not attr.startswith("__"): attr_nam = str(attr) if isinstance(self.name_inpt_dict[attr_nam], QComboBox): combo_box = self.name_inpt_dict[attr_nam] selected_item = combo_box.currentText() items_dict = self.structured_hyperparams[attr_nam][1][ selected_item] for cur_key in items_dict.keys(): value = get_int_or_else_float_from_str( self.name_inpt_dict[cur_key].text()) if value is not None: items_dict[cur_key] = value print(items_dict) setattr(self.hp, attr_nam, (selected_item, items_dict)) print(getattr(self.hp, attr_nam)) else: value = get_int_or_else_float_from_str( self.name_inpt_dict[attr_nam].text()) if value is not None: setattr(self.hp, attr_nam, value) self.gui_play.new_handler_and_start(self.hp) self.hide()
class GUI(QWidget): def __init__(self): super().__init__() try: urlopen('http://maia.usno.navy.mil/ser7/finals2000A.all') except HTTPError as e: print("Main IERS link not working, using mirror") iers.conf.iers_auto_url = 'http://toshi.nofs.navy.mil/ser7/finals2000A.all' except URLError as e: print("Main IERS link not working, using mirror") iers.conf.iers_auto_url = 'http://toshi.nofs.navy.mil/ser7/finals2000A.all' #download_IERS_A() plt.style.use(astropy_mpl_style) irbeneLocation = EarthLocation(lat=57.5535171694 * u.deg, lon=21.8545525000 * u.deg, height=87.30 * u.m) self.irbene = Observer(location=irbeneLocation, name="Irbene", timezone="Europe/Riga") observe_time = Time(['2019-02-05 15:30:00']) self.targets = [] self.targetsDict = {} with open("config/config.csv", "r") as csvfile: next(csvfile) reader = csv.reader(csvfile, delimiter=",", quotechar="|") for row in reader: sourceName = row[0] raText = row[1] raText = insert(raText, 'h', 2) #Nolasa targets no faila un ievieto targetsDict un targets raText = insert(raText, 'm', 5) raText = insert(raText, 's', len(raText)) decText = row[2] if (decText[0] != "-"): decText = insert(decText, 'd', 2) decText = insert(decText, 'm', 5) decText = insert(decText, 's', len(decText)) else: decText = insert(decText, 'd', 3) decText = insert(decText, 'm', 6) decText = insert(decText, 's', len(decText)) ra = Angle(raText) dec = Angle(decText) targetCoord = SkyCoord(frame='icrs', ra=ra, dec=dec, obstime="J2000") target = FixedTarget(coord=targetCoord, name=sourceName) plannedObs = PlannedObs(target, int(row[4]), int(row[3]), int(row[5])) self.targets.append(plannedObs) # target / obs per_week / priority / scans per obs coords = {"ra": ra, "dec": dec} self.targetsDict[sourceName] = coords self.targets = sorted(self.targets, key=lambda x: x.priority) # sort targets by priority self.calibrators = [] self.calibratorsDict = {} with open("config/calibrators.csv", "r") as csvfile: next(csvfile) reader = csv.reader(csvfile, delimiter=";", quotechar="|") for row in reader: sourceName = row[0] raText = str(row[1]).replace(" ", "") raText = insert(raText, 'h', 2) raText = insert(raText, 'm', 5) raText = insert(raText, 's', len(raText)) decText = str(row[2]).replace(" ", "") if (decText[0] != "-"): decText = insert(decText, 'd', 3) decText = insert(decText, 'm', 6) decText = insert(decText, 's', len(decText)) else: decText = insert(decText, 'd', 3) decText = insert(decText, 'm', 6) decText = insert(decText, 's', len(decText)) #Nolasa no faila calibratorus un ievieto calibratorsDict un calibrators ra = Angle(raText) dec = Angle(decText) coords = {"ra": ra, "dec": dec} self.calibratorsDict[sourceName] = coords calibratorCoord = SkyCoord(frame='icrs', ra=ra, dec=dec, obstime="J2000") calibrator = FixedTarget(coord=calibratorCoord, name=sourceName) self.calibrators.append(calibrator) startArray, endArray, summaryArray = get_all_events() #No google calendar sanem noverosanas datumus un laikus self.dateList = QListWidget() tempCheck = True for i in range(len(startArray)): dayStart = parse(startArray[i]) dayEnd = parse(endArray[i]) daySummary = summaryArray[i] daySummary = daySummary + " " + str(dayStart.date()) + " " + str(dayStart.time()) + "-" + str(dayEnd.time()) item = QListWidgetItem(daySummary, self.dateList) item.setData(Qt.UserRole, [dayStart, dayEnd]) #Izveido listwidget item no datuma un laika un to ievieto listwidget item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(Qt.Unchecked) if tempCheck and "maser" in daySummary: item.setCheckState(Qt.Checked) tempCheck = False self.dateList.addItem(item) config = configparser.ConfigParser() config.read('config/config.ini') self.config = config._sections['Default'] self.config['calibration'] = config['Default'].getboolean('calibration') #Nolasa config failu self.layout = QGridLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0,0,0,0) self.setLayout(self.layout) self.resize(1000, 600) self.dateBoxList = [] self.targetTimesCount = 0 self.load_ui() def load_ui(self): #Funkcija kas ielade galveno skatu self.observationList = QListWidget() self.plannedTargets = [] for target in self.targets[:10]: item = QListWidgetItem(str(target), self.observationList) #Aizpilda planotaju ar 10 targets kurus ieprieks nolasija no faila item.setData(Qt.UserRole, target) self.observationList.addItem(item) self.plannedTargets.append(target.name) self.layout.addWidget(self.observationList, 0, 0, 10, 2) self.observationList.itemSelectionChanged.connect(self.obsChanged) #Connect savieno kada UI elementa action (piemeram click) ar funkciju koda #Seit mainot izveleto elemntu listwidget izsauksies funkcija obsChanged for index in range(self.observationList.count()): item = self.observationList.item(index) self.targetLayout = QVBoxLayout() targetBox = QGroupBox() targetBox.setMaximumSize(350, 250) line = QHBoxLayout() nameLabel = QLabel("Target:") self.nameBox = QLineEdit() self.nameBox.setEnabled(False) nameLabel.setParent(targetBox) self.nameBox.setParent(targetBox) line.addWidget(nameLabel) line.addWidget(self.nameBox) self.targetLayout.addLayout(line) line = QHBoxLayout() priorityLabel = QLabel("Priority:") self.priorityBox = QLineEdit() priorityLabel.setParent(targetBox) self.priorityBox.setParent(targetBox) line.addWidget(priorityLabel) line.addWidget(self.priorityBox) self.targetLayout.addLayout(line) line = QHBoxLayout() obsLabel = QLabel("Obs per week:") self.obsBox = QLineEdit() obsLabel.setParent(targetBox) self.obsBox.setParent(targetBox) line.addWidget(obsLabel) line.addWidget(self.obsBox) self.targetLayout.addLayout(line) line = QHBoxLayout() scanLabel = QLabel("Scans per obs:") self.scanBox = QLineEdit() scanLabel.setParent(targetBox) self.scanBox.setParent(targetBox) line.addWidget(scanLabel) line.addWidget(self.scanBox) self.targetLayout.addLayout(line) line = QHBoxLayout() globalLabel = QLabel("Global time:") self.globalTimeBox = QLineEdit() line.addWidget(globalLabel) line.addWidget(self.globalTimeBox) self.targetLayout.addLayout(line) line = QHBoxLayout() specificLabel = QLabel("Specific times:") addTime = QPushButton("Add specific time") addTime.clicked.connect(self.add_time) line.addWidget(specificLabel) line.addWidget(addTime) self.targetLayout.addLayout(line) saveButton = QPushButton("Save changes") saveButton.clicked.connect(self.save_obs_changes) self.targetLayout.addWidget(saveButton) removeButton = QPushButton("Remove target") removeButton.clicked.connect(self.remove_obs) self.targetLayout.addWidget(removeButton) targetBox.setLayout(self.targetLayout) self.layout.addWidget(targetBox, 0, 2, 2, 1) self.targetComboBox = QComboBox() for key in self.targetsDict: if key not in self.plannedTargets: self.targetComboBox.addItem(key) self.layout.addWidget(self.targetComboBox, 2, 2) addButton = QPushButton("Add observation") addButton.clicked.connect(self.add_obs) self.layout.addWidget(addButton, 3, 2) nextButton = QPushButton("Schedule") nextButton.clicked.connect(self.prepare_schedule) self.layout.addWidget(nextButton, 0, 3) datesButton = QPushButton("Dates") datesButton.clicked.connect(self.edit_dates) self.layout.addWidget(datesButton, 1, 3) targetsButton = QPushButton("Targets") targetsButton.clicked.connect(self.edit_targets) self.layout.addWidget(targetsButton, 2, 3) calibratorsButton = QPushButton("Calibrators") calibratorsButton.clicked.connect(self.edit_calibrators) self.layout.addWidget(calibratorsButton, 3, 3) settingsButton = QPushButton("Settings") settingsButton.clicked.connect(self.load_settings) self.layout.addWidget(settingsButton, 4, 3) saveObsButton = QPushButton("Save observation") saveObsButton.clicked.connect(self.save_obs) self.layout.addWidget(saveObsButton, 5, 3) loadObsButton = QPushButton("Load observation") loadObsButton.clicked.connect(self.load_obs_new) self.layout.addWidget(loadObsButton, 6, 3) def add_time(self): #Pievieno combobox ar izveletajiem datumiem datesChecked = 0 for index in range(self.dateList.count()): if self.dateList.item(index).checkState() == Qt.Checked: datesChecked = datesChecked + 1 if datesChecked > self.targetTimesCount: line = QHBoxLayout() dateBox = QComboBox() for index in range(self.dateList.count()): if self.dateList.item(index).checkState() == Qt.Checked: dateBox.addItem(self.dateList.item(index).text(), self.dateList.item(index).data(Qt.UserRole)) dateBox.addItem("Remove") dateBox.currentTextChanged.connect(self.timeChanged) self.targetTimesCount+= 1 dateBox.sizePolicy().setHorizontalStretch(1) timeBox = QLineEdit() timeBox.sizePolicy().setHorizontalStretch(3) line.addWidget(dateBox) line.addWidget(timeBox) self.dateBoxList.append(line) self.targetLayout.insertLayout(self.targetLayout.count()-2, line) else: self.show_error("Date error", "Can't select more times than selected dates") def timeChanged(self, item): #Ja pie specifiskajiem laikiem izvelas remove tad iznem to if item == "Remove": for line in self.dateBoxList: if line is not None: item = line.itemAt(0) widget = item.widget() if type(widget) == type(QComboBox()): if widget.currentText() == "Remove": break self.targetTimesCount -= 1 widget.disconnect() self.deleteItemsOfLayout(line) self.targetLayout.removeItem(line) self.dateBoxList.remove(line) def obsChanged(self): #Nomainot observation nomaina visus texta laukus if len(self.observationList.selectedItems()) > 0: item = self.observationList.currentItem() plannedObs = item.data(Qt.UserRole) self.nameBox.setText(plannedObs.name) self.priorityBox.setText(str(plannedObs.priority)) self.obsBox.setText(str(plannedObs.obs_per_week)) self.scanBox.setText(str(plannedObs.scans_per_obs)) i = 0 maxi = self.targetLayout.count() while(i < maxi): layout_item = self.targetLayout.itemAt(i) if layout_item in self.dateBoxList: self.deleteItemsOfLayout(layout_item.layout()) self.targetLayout.removeItem(layout_item) self.dateBoxList.remove(layout_item) maxi = self.targetLayout.count() i = i -1 i=i+1 self.dateBoxList.clear() self.targetTimesCount = 0 if plannedObs.times: checkedDates = [] for index in range(self.dateList.count()): if self.dateList.item(index).checkState() == Qt.Checked: checkedDates.append(self.dateList.item(index).text()) print(checkedDates) for time in list(plannedObs.times): print(time) if time not in checkedDates: #Ja observation pievienots specifisks laiks datumam kurs vairs netiks izmantots to pazino lietotajam self.show_error("Date mismatch", "Date "+time+" is not checked, removing it") plannedObs.times.remove(time) for time in list(plannedObs.times): line = QHBoxLayout() dateBox = QComboBox() for index in range(self.dateList.count()): if self.dateList.item(index).checkState() == Qt.Checked: #Specific laikiem pievieno tikai datumus kas izveleti pie dates dateBox.addItem(self.dateList.item(index).text(), self.dateList.item(index).data(Qt.UserRole)) dateBox.addItem("Remove") dateBox.currentTextChanged.connect(self.timeChanged) self.targetTimesCount += 1 dateBox.sizePolicy().setHorizontalStretch(1) timeBox = QLineEdit(plannedObs.times[time]) timeBox.sizePolicy().setHorizontalStretch(3) line.addWidget(dateBox) line.addWidget(timeBox) self.dateBoxList.append(line) self.targetLayout.insertLayout(self.targetLayout.count() - 2, line) dateBox.setCurrentIndex(dateBox.findText(time)) else: self.nameBox.setText("") self.priorityBox.setText("") self.obsBox.setText("") self.scanBox.setText("") self.targetTimesCount = 0 def remove_obs(self): if len(self.observationList.selectedItems()) > 0: self.plannedTargets.remove(self.observationList.currentItem().data(Qt.UserRole).name) self.targetComboBox.addItem(self.observationList.currentItem().data(Qt.UserRole).name) self.observationList.takeItem(self.observationList.currentRow()) else: self.show_error("Observation error","Select an observation to remove it") def save_obs_changes(self): #Ja visi teksta lauki atbilst parbaudem tad saglaba datus, ja ne tad pazino lietotajam if not self.priorityBox.text().isdigit(): self.show_error("Priority error", "Priority must be from 1 to 4") elif int(self.priorityBox.text()) > 4 or int(self.priorityBox.text()) < 0: self.show_error("Priority error", "Priority must be from 1 to 4") elif not self.obsBox.text().isdigit(): self.show_error("Obs error", "Obs must be from 1 to 7") elif int(self.obsBox.text()) > 7 or int(self.obsBox.text()) < 0: self.show_error("Obs error", "Obs must be from 1 to 7") elif not self.scanBox.text().isdigit(): self.show_error("Scan error", "Scan must be from 1 to 120") elif int(self.scanBox.text()) > 120 or int(self.scanBox.text()) < 0: self.show_error("Scan error", "Scan must be from 1 to 120") elif len(self.dateBoxList) != len(set(self.dateBoxList)): self.show_error("Date error", "Make sure specified times don't use same dates") else: times = {} timeCheck = True for line in self.dateBoxList: if line.count() > 0: item = line.itemAt(0) widget = item.widget() date = widget.currentText() time = date[-17:] time = time.split('-') timeStart = time[0] timeStart = timeStart[:-3] timeEnd = time[1] timeEnd = timeEnd[:-3] time = line.itemAt(1).widget().text() timeCheck = self.time_check(time, timeStart, timeEnd) if timeCheck == False: break else: times[date] = line.itemAt(1).widget().text() if timeCheck: #Ja visas parbaudes izietas tad saglaba datus self.observationList.currentItem().data(Qt.UserRole).times = times self.observationList.currentItem().data(Qt.UserRole).priority = int(self.priorityBox.text()) self.observationList.currentItem().data(Qt.UserRole).obs_per_week = int(self.obsBox.text()) self.observationList.currentItem().data(Qt.UserRole).scans_per_obs = int(self.scanBox.text()) self.observationList.currentItem().data(Qt.UserRole).global_time = self.globalTimeBox.text() self.observationList.currentItem().setText(str(self.observationList.currentItem().data(Qt.UserRole))) else: self.show_error("Specific time error", "Make sure the specific times fit the dates selected") def add_obs(self): #Izveido jaunu observation if self.targetComboBox.count() > 0: targetName = self.targetComboBox.currentText() ra = self.targetsDict[targetName]["ra"] dec = self.targetsDict[targetName]["dec"] coord = SkyCoord(frame='icrs', ra=ra, dec=dec, obstime="J2000") target = FixedTarget(coord=coord, name=targetName) data = PlannedObs(target, 1, 1, 1) item = QListWidgetItem(str(data), self.observationList) item.setData(Qt.UserRole, data) self.observationList.addItem(item) self.plannedTargets.append(data.name) self.targetComboBox.removeItem(self.targetComboBox.currentIndex()) def edit_dates(self): #Atver dates skatu self.clear_window() self.layout.addWidget(self.dateList, 0, 0, 5, 2) backButton = QPushButton("Back to planner") self.layout.addWidget(backButton, 1, 2) backButton.clicked.connect(self.to_start) def edit_targets(self): #Atver targets skatu self.clear_window() self.targetList = QListWidget() for key in self.targetsDict: self.targetList.addItem(key) self.targetList.itemClicked.connect(self.targetChanged) self.layout.addWidget(self.targetList, 0, 0, 3, 1) targetLayout = QGridLayout() targetLayout.addWidget(QLabel("Ra:"), 0, 0) self.raBox = QLineEdit() targetLayout.addWidget(self.raBox, 0, 1) targetLayout.addWidget(QLabel("Dec:"), 1, 0) self.decBox = QLineEdit() targetLayout.addWidget(self.decBox, 1, 1) self.saveButton = QPushButton("Save changes") self.saveButton.clicked.connect(self.save_target_changes) targetLayout.addWidget(self.saveButton, 2, 0, 1, 2) targetBox = QGroupBox() targetBox.setLayout(targetLayout) self.layout.addWidget(targetBox, 0, 1) self.saveButton.setEnabled(False) addTargetLayout = QGridLayout() addTargetLayout.addWidget(QLabel("Name:"), 0, 0) self.addNameBox = QLineEdit() addTargetLayout.addWidget(self.addNameBox, 0, 1) addTargetLayout.addWidget(QLabel("Ra:"), 1, 0) self.addRaBox = QLineEdit() addTargetLayout.addWidget(self.addRaBox, 1, 1) addTargetLayout.addWidget(QLabel("Dec:"), 2, 0) self.addDecBox = QLineEdit() addTargetLayout.addWidget(self.addDecBox, 2, 1) self.addSaveButton = QPushButton("Save changes") self.addSaveButton.clicked.connect(self.add_target) addTargetLayout.addWidget(self.addSaveButton, 3, 0, 1, 2) addTargetBox = QGroupBox() addTargetBox.setLayout(addTargetLayout) self.layout.addWidget(addTargetBox, 1, 1) backButton = QPushButton("Back to planner") self.layout.addWidget(backButton, 0, 3) backButton.clicked.connect(self.to_start) def targetChanged(self, item): if not self.saveButton.isEnabled(): self.saveButton.setEnabled(True) targetName = item.text() target = self.targetsDict[targetName] self.raBox.setText(target["ra"].to_string()) self.decBox.setText(target["dec"].to_string(unit=u.degree)) def save_target_changes(self): if len(self.targetList.selectedItems()) != 1: self.show_error("Target error", "Make sure you have selected only 1 target") else: targetName = self.targetList.selectedItems()[0].text() raPattern = re.compile("[0-9]{1,2}h[0-9]{1,2}m[0-9]{1,2}(\.[0-9]{1,3})?s") decPattern = re.compile("-?[0-9]{1,2}d[0-9]{1,2}m[0-9]{1,2}(\.[0-9]{1,3})?s") ra = self.raBox.text() dec = self.decBox.text() if not raPattern.match(ra): self.show_error("Ra error", "Ra coordinates don't match pattern 00h00m00.00s") elif not decPattern.match(dec): self.show_error("Dec error", "Dec coordinates don't match pattern 00d00m00.00s") else: self.targetsDict[targetName]["ra"] = Angle(ra) self.targetsDict[targetName]["dec"] = Angle(dec) def add_target(self): #Pievieno jaunu target raPattern = re.compile("[0-9]{1,2}h[0-9]{1,2}m[0-9]{1,2}(\.[0-9]{1,3})?s") decPattern = re.compile("-?[0-9]{1,2}d[0-9]{1,2}m[0-9]{1,2}(\.[0-9]{1,3})?s") ra = self.addRaBox.text() dec = self.addDecBox.text() name = self.addNameBox.text() print(self.targetsDict.keys()) if ra == "" or dec == "" or name == "": self.show_error("Empty box", "Please fill all boxes") elif name in self.targetsDict.keys(): self.show_error("Existing target", "Target already exists, please edit it") elif not raPattern.match(ra): self.show_error("Ra error", "Ra coordinates don't match pattern 00h00m00.00s") elif not decPattern.match(dec): self.show_error("Dec error", "Dec coordinates don't match pattern 00d00m00.00s") else: self.targetsDict[name] = {} self.targetsDict[name]["ra"] = Angle(ra) self.targetsDict[name]["dec"] = Angle(dec) self.edit_targets() def edit_calibrators(self): #Atver calibrators skatu self.clear_window() self.calibratorList = QListWidget() for key in self.calibratorsDict: self.calibratorList.addItem(key) self.calibratorList.itemClicked.connect(self.calibratorChanged) self.layout.addWidget(self.calibratorList, 0, 0, 3, 1) calibratorLayout = QGridLayout() calibratorLayout.addWidget(QLabel("Ra:"), 0, 0) self.raBox = QLineEdit() calibratorLayout.addWidget(self.raBox, 0, 1) calibratorLayout.addWidget(QLabel("Dec:"), 1, 0) self.decBox = QLineEdit() calibratorLayout.addWidget(self.decBox, 1, 1) self.saveButton = QPushButton("Save changes") self.saveButton.clicked.connect(self.save_calibrator_changes) calibratorLayout.addWidget(self.saveButton, 2, 0, 1, 2) calibratorBox = QGroupBox() calibratorBox.setLayout(calibratorLayout) self.layout.addWidget(calibratorBox, 0, 1) self.saveButton.setEnabled(False) addcalibratorLayout = QGridLayout() addcalibratorLayout.addWidget(QLabel("Name:"), 0, 0) self.addNameBox = QLineEdit() addcalibratorLayout.addWidget(self.addNameBox, 0, 1) addcalibratorLayout.addWidget(QLabel("Ra:"), 1, 0) self.addRaBox = QLineEdit() addcalibratorLayout.addWidget(self.addRaBox, 1, 1) addcalibratorLayout.addWidget(QLabel("Dec:"), 2, 0) self.addDecBox = QLineEdit() addcalibratorLayout.addWidget(self.addDecBox, 2, 1) self.addSaveButton = QPushButton("Save changes") self.addSaveButton.clicked.connect(self.add_calibrator) addcalibratorLayout.addWidget(self.addSaveButton, 3, 0, 1, 2) addcalibratorBox = QGroupBox() addcalibratorBox.setLayout(addcalibratorLayout) self.layout.addWidget(addcalibratorBox, 1, 1) backButton = QPushButton("Back to planner") self.layout.addWidget(backButton, 0, 3) backButton.clicked.connect(self.to_start) def calibratorChanged(self, item): if not self.saveButton.isEnabled(): self.saveButton.setEnabled(True) calibratorName = item.text() calibrator = self.calibratorsDict[calibratorName] self.raBox.setText(calibrator["ra"].to_string()) self.decBox.setText(calibrator["dec"].to_string(unit=u.degree)) def save_calibrator_changes(self): if len(self.calibratorList.selectedItems()) != 1: self.show_error("calibrator error", "Make sure you have selected only 1 calibrator") else: calibratorName = self.calibratorList.selectedItems()[0].text() raPattern = re.compile("[0-9]{1,2}h[0-9]{1,2}m[0-9]{1,2}(\.[0-9]{1,5})?s") decPattern = re.compile("-?[0-9]{1,2}d[0-9]{1,2}m[0-9]{1,2}(\.[0-9]{1,5})?s") ra = self.raBox.text() dec = self.decBox.text() if not raPattern.match(ra): self.show_error("Ra error", "Ra coordinates don't match pattern 00h00m00.00s") elif not decPattern.match(dec): self.show_error("Dec error", "Dec coordinates don't match pattern 00d00m00.00s") else: self.calibratorsDict[calibratorName]["ra"] = Angle(ra) self.calibratorsDict[calibratorName]["dec"] = Angle(dec) def add_calibrator(self): raPattern = re.compile("[0-9]{1,2}h[0-9]{1,2}m[0-9]{1,2}(\.[0-9]{1,5})?s") decPattern = re.compile("-?[0-9]{1,2}d[0-9]{1,2}m[0-9]{1,2}(\.[0-9]{1,5})?s") ra = self.addRaBox.text() dec = self.addDecBox.text() name = self.addNameBox.text() print(self.calibratorsDict.keys()) if ra == "" or dec == "" or name == "": self.show_error("Empty box", "Please fill all boxes") elif name in self.calibratorsDict.keys(): self.show_error("Existing calibrator", "calibrator already exists, please edit it") elif not raPattern.match(ra): self.show_error("Ra error", "Ra coordinates don't match pattern 00h00m00.00s") elif not decPattern.match(dec): self.show_error("Dec error", "Dec coordinates don't match pattern 00d00m00.00s") else: self.calibratorsDict[name] = {} self.calibratorsDict[name]["ra"] = Angle(ra) self.calibratorsDict[name]["dec"] = Angle(dec) self.edit_calibrators() def load_settings(self): #Atver settings skatu self.clear_window() targetLayout = QFormLayout() targetBox = QGroupBox() targetBox.setMaximumSize(350, 250) calibLabel = QLabel("Calib every X min:") self.calibBox = QLineEdit() calibLabel.setParent(targetBox) self.calibBox.setParent(targetBox) self.calibBox.setText(self.config['maxtimewithoutcalibration']) targetLayout.addRow(calibLabel, self.calibBox) calibDurLabel = QLabel("Calib duration:") self.calibDurBox = QLineEdit() calibDurLabel.setParent(targetBox) self.calibDurBox.setParent(targetBox) self.calibDurBox.setText(self.config['calibrationlength']) targetLayout.addRow(calibDurLabel, self.calibDurBox) minAltLabel = QLabel("Min alt:") self.minAltBox = QLineEdit() minAltLabel.setParent(targetBox) self.minAltBox.setParent(targetBox) self.minAltBox.setText(self.config['minaltitude']) targetLayout.addRow(minAltLabel, self.minAltBox) maxAltLabel = QLabel("Max alt:") self.maxAltBox = QLineEdit() maxAltLabel.setParent(targetBox) self.maxAltBox.setParent(targetBox) self.maxAltBox.setText(self.config['maxaltitude']) targetLayout.addRow(maxAltLabel, self.maxAltBox) calibToggleLabel = QLabel("Calibration on/off") self.calibCheckBox = QCheckBox() calibToggleLabel.setParent(targetBox) self.calibCheckBox.setParent(targetBox) if (self.config['calibration']): self.calibCheckBox.setChecked(True) else: self.calibCheckBox.setChecked(False) targetLayout.addRow(calibToggleLabel, self.calibCheckBox) saveButton = QPushButton("Save settings") saveButton.clicked.connect(self.save_settings) targetLayout.addRow(saveButton) targetBox.setLayout(targetLayout) self.layout.addWidget(targetBox, 0, 2, 2, 1) def save_settings(self): if not (self.calibBox.text().isdigit() and int(self.calibBox.text()) > 0): self.show_error("Input error","Max time without calib must be positive number") elif not (self.calibDurBox.text().isdigit() and int(self.calibDurBox.text()) > 0): self.show_error("Input error","Calib duration must be positive number") elif not (self.minAltBox.text().isdigit() and int(self.minAltBox.text()) > 0): self.show_error("Input error","Min alt must be positive number") elif not (self.maxAltBox.text().isdigit() and int(self.maxAltBox.text()) > 0): self.show_error("Input error","Max alt must be positive number") else: self.config['maxtimewithoutcalibration'] = self.calibBox.text() self.config['calibrationlength'] = self.calibDurBox.text() self.config['minaltitude'] = self.minAltBox.text() self.config['maxaltitude'] = self.maxAltBox.text() self.config['calibration'] = self.calibCheckBox.isChecked() self.to_start() def prepare_schedule(self): #Pirms uzsak planosanu, parbauda vai ir izvelets kads datums hasDate = False for index in range(self.dateList.count()): if self.dateList.item(index).checkState() == Qt.Checked: hasDate = True break if not hasDate: self.show_error("Date error", "No dates selected for schedule") else: self.start_schedule() def start_schedule(self): #Sak planosanu items = (self.layout.itemAt(i).widget() for i in range(self.layout.count())) self.targets = [] for index in range(self.observationList.count()): item = self.observationList.item(index) target = item.data(Qt.UserRole) self.targets.append(target) #Visus obs ievieto masiva targets targ_to_color = {} color_idx = np.linspace(0, 1, len(self.targets)) for target, ci in zip(set(self.targets), color_idx): #Katram target un calibrator pieskir savu krasu if "split" not in target.name: if target.name not in targ_to_color: targ_to_color[target.name] = plt.cm.jet(ci) calib_to_color = {} color_idx = np.linspace(0, 1, len(self.calibrators)) for calibrator, ci in zip(set(self.calibrators), color_idx): if "split" not in calibrator.name: if calibrator.name not in calib_to_color: calib_to_color[calibrator.name] = plt.cm.brg(ci) self.plots_idx = 0 self.plots = [] week = {} for index in range(self.dateList.count()): if self.dateList.item(index).checkState() == Qt.Checked: #Dates ievieto dict week week[self.dateList.item(index).text()]=[self.dateList.item(index).data(Qt.UserRole)[0], self.dateList.item(index).data(Qt.UserRole)[1]] for daySummary, day in week.items(): dayStart = Time(day[0]) # convert from datetime to astropy.time dayEnd = Time(day[1]) timeDict = {} for target in self.targets: if daySummary in target.times: timeDict[target.name] = target.times[daySummary] #Pievieno specifiskos laikus dict timeDict elif target.global_time != "": timeDict[target.name] = target.global_time minalt = self.config['minaltitude'] maxalt = self.config['maxaltitude'] constraints = [AltitudeConstraint(minalt * u.deg, maxalt * u.deg)] read_out = 1 * u.second target_exp = 60 * u.second blocks = [] for target in self.targets: n = target.scans_per_obs priority = target.priority if (target.obs_per_week != 0): #Ja observation vel ir janovero tad izveido ObservingBlock b = ObservingBlock.from_exposures(target.target, priority, target_exp, n, read_out) blocks.append(b) slew_rate = 2 * u.deg / u.second transitioner = Transitioner(slew_rate, {'filter': {'default': 5 * u.second}}) if (self.config['calibration']): #Padod mainigos planotajam prior_scheduler = SequentialScheduler(constraints=constraints, observer=self.irbene, transitioner=transitioner, calibrators=self.calibrators, config=self.config, timeDict=timeDict) priority_schedule = Schedule(dayStart, dayEnd, targColor=targ_to_color, calibColor=calib_to_color, minalt=minalt, maxalt=maxalt) else: prior_scheduler = SequentialScheduler(constraints=constraints, observer=self.irbene, transitioner=transitioner, config=self.config, timeDict=timeDict) priority_schedule = Schedule(dayStart, dayEnd, targColor=targ_to_color, minalt=minalt, maxalt=maxalt) prior_scheduler(blocks, priority_schedule) observations = [] for block in priority_schedule.scheduled_blocks: if hasattr(block, 'target'): observation = Observation(block.target.name, block.start_time.datetime, (block.start_time + block.duration).datetime) observations.append(observation) dict_array = [] for observation in observations: #Saplanotos block nolasa un ieraksta faila for target in self.targets: if target.name == observation.name: print(target.name, " has been observed once") target.obs_per_week -= 1 dict_array.append({ "obs_name": observation.name, "start_time": observation.start_time.strftime("%Y-%m-%d %H:%M:%S"), "end_time": observation.end_time.strftime("%Y-%m-%d %H:%M:%S"), }) json_dict = dict() json_dict["observations"] = dict_array if not os.path.isdir("observations"): os.mkdir("observations") if not os.path.isdir("observations/"): os.mkdir("observations") with open("observations/" + day[0].strftime("%Y-%m-%d-%H-%M") + ".json", 'w') as outfile: json.dump(json_dict, outfile, indent=4) sky = Plot() #Izveido grafikus skyCheck = sky.plot_sky_schedule(priority_schedule) alt = Plot(width=6) alt.plot_altitude_schedule(priority_schedule) if skyCheck is not False: self.plots.append([sky, alt]) else: self.show_error("Empty schedule", "Schedule "+daySummary+"removing it") timeLeft = 0 for target in self.targets: timeLeft += target.obs_per_week * target.scans_per_obs print(target.name, ' observations left ', target.obs_per_week, ' scan size ', target.scans_per_obs, ' priority ', target.priority) print('Total time left to observe ', timeLeft) self.showSky = False self.showAlt = False self.showBoth = True self.show_schedule() def show_schedule(self): #Atver grafiku skatu self.clear_window() sky, alt = self.plots[self.plots_idx] if self.showSky: self.layout.addWidget(sky, 0, 0, 2, 6) elif self.showAlt: self.layout.addWidget(alt, 0, 0, 2, 6) elif self.showBoth: self.layout.addWidget(sky, 0, 0, 1, 6) self.layout.addWidget(alt, 1, 0, 1, 6) self.toolbar = NavigationToolbar(alt, alt.parent) self.layout.addWidget(self.toolbar, 2, 0, 1, 3) self.radioSky = QRadioButton("Show skychart") self.radioAlt = QRadioButton("Show altitude") self.radioBoth = QRadioButton("Show both") self.radioSky.clicked.connect(self.changeScheduleView) self.radioAlt.clicked.connect(self.changeScheduleView) self.radioBoth.clicked.connect(self.changeScheduleView) self.layout.addWidget(self.radioSky, 2, 3) self.layout.addWidget(self.radioAlt, 2, 4) self.layout.addWidget(self.radioBoth, 2, 5) nextButton = QPushButton("Next") nextButton.clicked.connect(self.next_schedule) backButton = QPushButton("Back") backButton.clicked.connect(self.back_schedule) self.layout.addWidget(backButton, 3, 0, 1, 3) self.layout.addWidget(nextButton, 3, 3, 1, 3) startButton = QPushButton("To start") startButton.clicked.connect(self.to_start) self.layout.addWidget(startButton, 4, 0, 1, 3) if self.plots_idx == 0: backButton.hide() if self.plots_idx == (len(self.plots) - 1): nextButton.hide() def next_schedule(self): self.plots_idx += 1 self.show_schedule() def changeScheduleView(self): radioText = self.sender().text() if "sky" in radioText: self.showSky = True self.showAlt = False self.showBoth = False self.show_schedule() self.radioSky.setChecked(True) elif "alt" in radioText: self.showSky = False self.showAlt = True self.showBoth = False self.show_schedule() self.radioAlt.setChecked(True) elif "both" in radioText: self.showSky = False self.showAlt = False self.showBoth = True self.show_schedule() self.radioBoth.setChecked(True) def back_schedule(self): self.plots_idx -= 1 self.show_schedule() def save_obs(self): filename = self.saveFileDialog() if filename != "Fail": obsList = [self.observationList.item(i).data(Qt.UserRole) for i in range(self.observationList.count())] json_dict = dict() for obs in obsList: json_dict[obs.target.name]={ "priority": obs.priority, "obs_per_week": obs.obs_per_week, "scans_per_obs": obs.scans_per_obs, "global_time": obs.global_time, "times": obs.times, } print(json_dict) with open(filename, 'w') as outfile: json.dump(json_dict, outfile, indent=4) def saveFileDialog(self): save = QFileDialog() save.setDefaultSuffix(".json") save.setNameFilter("JSON files (*.json)") save.setAcceptMode(QFileDialog.AcceptSave) save.setOption(QFileDialog.DontUseNativeDialog) if save.exec_() == QFileDialog.Accepted: return save.selectedFiles()[0] else: return "Fail" def load_obs(self): load = QFileDialog() load.setDefaultSuffix(".json") load.setNameFilter("JSON files (*.json)") load.setAcceptMode(QFileDialog.AcceptOpen) load.setOption(QFileDialog.DontUseNativeDialog) if load.exec_() == QFileDialog.Accepted: filename = load.selectedFiles()[0] with open(filename) as json_file: obs_dict = json.load(json_file) self.observationList.clear() for key in obs_dict: if key in self.targetsDict: targetName = key ra = self.targetsDict[targetName]["ra"] dec = self.targetsDict[targetName]["dec"] coord = SkyCoord(frame='icrs', ra=ra, dec=dec, obstime="J2000") target = FixedTarget(coord=coord, name=targetName) for date in obs_dict[key]['times']: print(date) # TODO Te pielikt date parbaudes, vai atkekset ja ir vai iznemt ja nav data = PlannedObs(target, int(obs_dict[key]['priority']), int(obs_dict[key]['obs_per_week']), int(obs_dict[key]['scans_per_obs']), obs_dict[key]['times'], obs_dict[key]['global_time']) item = QListWidgetItem(str(data), self.observationList) item.setData(Qt.UserRole, data) self.observationList.addItem(item) self.plannedTargets.append(target.name) else: self.show_error("Target error", key + " not in targets, skipping it") else: print("Something went wrong") def load_obs_new(self): load = QFileDialog() load.setDefaultSuffix(".conf") load.setNameFilter("CONF files (*.conf)") load.setAcceptMode(QFileDialog.AcceptOpen) load.setOption(QFileDialog.DontUseNativeDialog) if load.exec_() == QFileDialog.Accepted: self.observationList.clear() filename = load.selectedFiles()[0] configObs = configparser.ConfigParser() configObs.read(filename) sections = configObs.sections() for section in sections: target = section.split('_')[0] ra = configObs[section]["RA"] dec = configObs[section]["DEC"] if target not in self.targetsDict.keys(): coords = {"ra": Angle(ra), "dec": Angle(dec)} self.targetsDict[target] = coords else: if Angle(ra) != Angle(self.targetsDict[target]["ra"]) or Angle(dec) != Angle(self.targetsDict[target]["dec"]): qm = QMessageBox ret = qm.question(self,'', target+" has different coords in the load file, would you like to overwrite the current ones?\n"+ "new coords:"+str(ra)+"; "+str(dec)+"\ncurrent coords:"+str(self.targetsDict[target]["ra"])+"; "+str(self.targetsDict[target]["dec"]), qm.Yes | qm.No) if ret == qm.Yes: self.targetsDict[target]["ra"] = Angle(ra) self.targetsDict[target]["dec"] = Angle(dec) targetName = target ra = self.targetsDict[targetName]["ra"] dec = self.targetsDict[targetName]["dec"] coord = SkyCoord(frame='icrs', ra=ra, dec=dec, obstime="J2000") target = FixedTarget(coord=coord, name=targetName) data = PlannedObs(target, 1, 1, int(configObs[section]["n_scans"]), None, None) item = QListWidgetItem(str(data), self.observationList) item.setData(Qt.UserRole, data) self.observationList.addItem(item) self.plannedTargets.append(target.name) else: print("Something went wrong") def show_error(self, title, error_message): error_dialog = QMessageBox.critical(self, title, error_message) def to_start(self): self.clear_window() self.load_ui() def clear_window(self): for i in reversed(range(self.layout.count())): self.layout.itemAt(i).widget().setParent(None) def is_time_format(self, string): p = re.compile('^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$') print(string) res = bool(p.match(string)) return res def time_check(self, time, timeStart, timeEnd): if not self.is_time_format(time): return False else: time = time.split(':') timeStart = timeStart.split(':') timeEnd = timeEnd.split(':') if int(time[0]) < int(timeStart[0]) or int(time[0]) > int(timeEnd[0]): return False elif int(time[0]) > int(timeStart[0]) and int(time[0]) < int(timeEnd[0]): return True elif (int(time[0]) == int(timeStart[0]) and int(time[1]) < int(timeStart[1])) or (int(time[0]) == int(timeEnd[0]) and int(time[1]) > int(timeEnd[1])): return False elif (int(time[0]) == int(timeStart[0]) and int(time[1]) >= int(timeStart[1])) or (int(time[0]) == int(timeEnd[0]) and int(time[1]) < int(timeEnd[1])): return True def deleteItemsOfLayout(self, layout): if layout is not None: while layout.count(): item = layout.takeAt(0) widget = item.widget() if widget is not None: widget.setParent(None) else: self.deleteItemsOfLayout(item.layout())
class E5TextEditSearchWidget(QWidget): """ Class implementing a horizontal search widget for QTextEdit. """ def __init__(self, parent=None, widthForHeight=True): """ Constructor @param parent reference to the parent widget @type QWidget @param widthForHeight flag indicating to prefer width for height. If this parameter is False, some widgets are shown in a third line. @type bool """ super(E5TextEditSearchWidget, self).__init__(parent) self.__setupUi(widthForHeight) self.__textedit = None self.__texteditType = "" self.__findBackwards = True self.__defaultBaseColor = ( self.findtextCombo.lineEdit().palette().color(QPalette.Base) ) self.__defaultTextColor = ( self.findtextCombo.lineEdit().palette().color(QPalette.Text) ) self.findHistory = [] self.findtextCombo.setCompleter(None) self.findtextCombo.lineEdit().returnPressed.connect( self.__findByReturnPressed) self.__setSearchButtons(False) self.infoLabel.hide() self.setFocusProxy(self.findtextCombo) def __setupUi(self, widthForHeight): """ Private method to generate the UI. @param widthForHeight flag indicating to prefer width for height @type bool """ self.setObjectName("E5TextEditSearchWidget") self.verticalLayout = QVBoxLayout(self) self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout.setContentsMargins(0, 0, 0, 0) # row 1 of widgets self.horizontalLayout1 = QHBoxLayout() self.horizontalLayout1.setObjectName("horizontalLayout1") self.label = QLabel(self) self.label.setObjectName("label") self.label.setText(self.tr("Find:")) self.horizontalLayout1.addWidget(self.label) self.findtextCombo = E5ClearableComboBox(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.findtextCombo.sizePolicy().hasHeightForWidth()) self.findtextCombo.setSizePolicy(sizePolicy) self.findtextCombo.setMinimumSize(QSize(100, 0)) self.findtextCombo.setEditable(True) self.findtextCombo.setInsertPolicy(QComboBox.InsertAtTop) self.findtextCombo.setDuplicatesEnabled(False) self.findtextCombo.setObjectName("findtextCombo") self.horizontalLayout1.addWidget(self.findtextCombo) # row 2 (maybe) of widgets self.horizontalLayout2 = QHBoxLayout() self.horizontalLayout2.setObjectName("horizontalLayout2") self.caseCheckBox = QCheckBox(self) self.caseCheckBox.setObjectName("caseCheckBox") self.caseCheckBox.setText(self.tr("Match case")) self.horizontalLayout2.addWidget(self.caseCheckBox) self.wordCheckBox = QCheckBox(self) self.wordCheckBox.setObjectName("wordCheckBox") self.wordCheckBox.setText(self.tr("Whole word")) self.horizontalLayout2.addWidget(self.wordCheckBox) # layout for the navigation buttons self.horizontalLayout3 = QHBoxLayout() self.horizontalLayout3.setSpacing(0) self.horizontalLayout3.setObjectName("horizontalLayout3") self.findPrevButton = QToolButton(self) self.findPrevButton.setObjectName("findPrevButton") self.findPrevButton.setToolTip(self.tr( "Press to find the previous occurrence")) self.findPrevButton.setIcon(UI.PixmapCache.getIcon("1leftarrow.png")) self.horizontalLayout3.addWidget(self.findPrevButton) self.findNextButton = QToolButton(self) self.findNextButton.setObjectName("findNextButton") self.findNextButton.setToolTip(self.tr( "Press to find the next occurrence")) self.findNextButton.setIcon(UI.PixmapCache.getIcon("1rightarrow.png")) self.horizontalLayout3.addWidget(self.findNextButton) self.horizontalLayout2.addLayout(self.horizontalLayout3) # info label (in row 2 or 3) self.infoLabel = QLabel(self) self.infoLabel.setText("") self.infoLabel.setObjectName("infoLabel") # place everything together self.verticalLayout.addLayout(self.horizontalLayout1) self.__addWidthForHeightLayout(widthForHeight) self.verticalLayout.addWidget(self.infoLabel) QMetaObject.connectSlotsByName(self) self.setTabOrder(self.findtextCombo, self.caseCheckBox) self.setTabOrder(self.caseCheckBox, self.wordCheckBox) self.setTabOrder(self.wordCheckBox, self.findPrevButton) self.setTabOrder(self.findPrevButton, self.findNextButton) def setWidthForHeight(self, widthForHeight): """ Public method to set the 'width for height'. @param widthForHeight flag indicating to prefer width @type bool """ if self.__widthForHeight: self.horizontalLayout1.takeAt(self.__widthForHeightLayoutIndex) else: self.verticalLayout.takeAt(self.__widthForHeightLayoutIndex) self.__addWidthForHeightLayout(widthForHeight) def __addWidthForHeightLayout(self, widthForHeight): """ Private method to set the middle part of the layout. @param widthForHeight flag indicating to prefer width @type bool """ if widthForHeight: self.horizontalLayout1.addLayout(self.horizontalLayout2) self.__widthForHeightLayoutIndex = 2 else: self.verticalLayout.insertLayout(1, self.horizontalLayout2) self.__widthForHeightLayoutIndex = 1 self.__widthForHeight = widthForHeight def attachTextEdit(self, textedit, editType="QTextEdit"): """ Public method to attach a QTextEdit widget. @param textedit reference to the edit widget to be attached @type QTextEdit, QWebEngineView or QWebView @param editType type of the attached edit widget @type str (one of "QTextEdit", "QWebEngineView" or "QWebView") """ assert editType in ["QTextEdit", "QWebEngineView", "QWebView"] self.__textedit = textedit self.__texteditType = editType self.wordCheckBox.setVisible(editType == "QTextEdit") def keyPressEvent(self, event): """ Protected slot to handle key press events. @param event reference to the key press event (QKeyEvent) """ if self.__textedit and event.key() == Qt.Key_Escape: self.__textedit.setFocus(Qt.ActiveWindowFocusReason) event.accept() @pyqtSlot(str) def on_findtextCombo_editTextChanged(self, txt): """ Private slot to enable/disable the find buttons. @param txt text of the combobox (string) """ self.__setSearchButtons(txt != "") self.infoLabel.hide() self.__setFindtextComboBackground(False) def __setSearchButtons(self, enabled): """ Private slot to set the state of the search buttons. @param enabled flag indicating the state (boolean) """ self.findPrevButton.setEnabled(enabled) self.findNextButton.setEnabled(enabled) def __findByReturnPressed(self): """ Private slot to handle the returnPressed signal of the findtext combobox. """ self.__find(self.__findBackwards) @pyqtSlot() def on_findPrevButton_clicked(self): """ Private slot to find the previous occurrence. """ self.__find(True) @pyqtSlot() def on_findNextButton_clicked(self): """ Private slot to find the next occurrence. """ self.__find(False) def __find(self, backwards): """ Private method to search the associated text edit. @param backwards flag indicating a backwards search (boolean) """ if not self.__textedit: return self.infoLabel.clear() self.infoLabel.hide() self.__setFindtextComboBackground(False) txt = self.findtextCombo.currentText() if not txt: return self.__findBackwards = backwards # This moves any previous occurrence of this statement to the head # of the list and updates the combobox if txt in self.findHistory: self.findHistory.remove(txt) self.findHistory.insert(0, txt) self.findtextCombo.clear() self.findtextCombo.addItems(self.findHistory) if self.__texteditType == "QTextEdit": ok = self.__findPrevNextQTextEdit(backwards) self.__findNextPrevCallback(ok) elif self.__texteditType == "QWebEngineView": self.__findPrevNextQWebEngineView(backwards) def __findPrevNextQTextEdit(self, backwards): """ Private method to to search the associated edit widget of type QTextEdit. @param backwards flag indicating a backwards search @type bool @return flag indicating the search result @rtype bool """ if backwards: flags = QTextDocument.FindFlags(QTextDocument.FindBackward) else: flags = QTextDocument.FindFlags() if self.caseCheckBox.isChecked(): flags |= QTextDocument.FindCaseSensitively if self.wordCheckBox.isChecked(): flags |= QTextDocument.FindWholeWords ok = self.__textedit.find(self.findtextCombo.currentText(), flags) if not ok: # wrap around once cursor = self.__textedit.textCursor() if backwards: moveOp = QTextCursor.End # move to end of document else: moveOp = QTextCursor.Start # move to start of document cursor.movePosition(moveOp) self.__textedit.setTextCursor(cursor) ok = self.__textedit.find(self.findtextCombo.currentText(), flags) return ok def __findPrevNextQWebEngineView(self, backwards): """ Private method to to search the associated edit widget of type QWebEngineView. @param backwards flag indicating a backwards search @type bool """ from PyQt5.QtWebEngineWidgets import QWebEnginePage findFlags = QWebEnginePage.FindFlags() if self.caseCheckBox.isChecked(): findFlags |= QWebEnginePage.FindCaseSensitively if backwards: findFlags |= QWebEnginePage.FindBackward self.__textedit.findText(self.findtextCombo.currentText(), findFlags, self.__findNextPrevCallback) def __findNextPrevCallback(self, found): """ Private method to process the result of the last search. @param found flag indicating if the last search succeeded @type bool """ if not found: txt = self.findtextCombo.currentText() self.infoLabel.setText( self.tr("'{0}' was not found.").format(txt)) self.infoLabel.show() self.__setFindtextComboBackground(True) def __setFindtextComboBackground(self, error): """ Private slot to change the findtext combo background to indicate errors. @param error flag indicating an error condition (boolean) """ le = self.findtextCombo.lineEdit() p = le.palette() if error: p.setBrush(QPalette.Base, QBrush(QColor("#FF6666"))) p.setBrush(QPalette.Text, QBrush(QColor("#000000"))) else: p.setBrush(QPalette.Base, self.__defaultBaseColor) p.setBrush(QPalette.Text, self.__defaultTextColor) le.setPalette(p) le.update()
class Lines(Window): def __init__(self, reset_func=None): super().__init__() self.size = 0 self.reset_func = reset_func self.layout = QVBoxLayout() self.lines = [] for i in range(10): self.add_line() layout = self.get_layout() add_btn = create_control_btn('Add', self.add_line) clear_btn = create_control_btn('Reset', self.reset) fill_layout(layout, add_btn, clear_btn) def get_layout(self): container = QWidget() container.setLayout(self.layout) scroll = create_scroll_area(container) layout = QVBoxLayout(self) self.setLayout(layout) layout.addWidget(scroll) return layout def add_line(self): hbox = QHBoxLayout() box = create_text_box() self.lines.append(box) btn = create_remove_btn(lambda: self.remove_line(hbox, box, btn)) index = self.size fill_layout(hbox, box, btn) self.layout.insertLayout(index, hbox) self.size += 1 def reset(self): for line in self.lines: line.setText('') if self.reset_func is not None: self.reset_func() def fill(self, lines): index = 0 for line in self.lines: if index == len(lines): return if not line.toPlainText(): line.setText(lines[index]) index += 1 def remove_line(self, line, box, btn): box.deleteLater() btn.deleteLater() line.deleteLater() del self.lines[self.lines.index(box)] self.size -= 1 def get_data(self): result = [] for line in self.lines: text = line.toPlainText() if text: result.append(text) return result
class MainWindow(QWidget): def __init__(self, inList): super().__init__() self.inList = inList self.nameFrom = 'Folder' self.codec = 'utvideo' self.alpha = False self.frameRate = 24 self.defaulStyle = '' self.okIcon = QIcon(self.style().standardIcon(QStyle.SP_CustomBase)) self.okPix = QPixmap(self.okIcon.pixmap(QSize(13, 13))) self.goodIcon = QIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) self.goodPix = QPixmap(self.goodIcon.pixmap(QSize(13, 13))) self.badIcon = QIcon(self.style().standardIcon(QStyle.SP_MessageBoxCritical)) self.badPix = QPixmap(self.badIcon.pixmap(QSize(13, 13))) self.processingIcon = QIcon(self.style().standardIcon(QStyle.SP_ArrowRight)) self.processingPix = QPixmap(self.processingIcon.pixmap(QSize(13, 13))) self.removeIcon = QIcon(self.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) self.removePix = QPixmap(self.removeIcon.pixmap(QSize(19, 19))) self.folderIcon = QIcon(self.style().standardIcon(QStyle.SP_FileDialogNewFolder)) self.folderPix = QPixmap(self.folderIcon.pixmap(QSize(19, 19))) self.pbList = [] self.chList = [] self.lblList = [] self.rmbList = [] #self.newFolders = [] self.initUI() def initUI(self): self.resize(720, 300) self.setWindowTitle('FFMpeg Python Compressor') self.verticalLayout = QVBoxLayout(self) self.verticalLayout.setContentsMargins(11, 11, 11, 11) self.verticalLayout.setSpacing(11) #COMBOBOX LABELS self.gridLayoutControlls = QGridLayout() self.codecLabel = QLabel('Codec', self) self.codecLabel.setMinimumHeight(13) self.alphaLabel = QLabel('Alpha' , self) self.alphaLabel.setMinimumHeight(13) self.frameRateLabel = QLabel('Frame Rate' , self) self.frameRateLabel.setMinimumHeight(13) self.gridLayoutControlls.addWidget(self.codecLabel, 0, 0, 1, 1) self.gridLayoutControlls.addWidget(self.alphaLabel, 0, 1, 1, 1) self.gridLayoutControlls.addWidget(self.frameRateLabel, 0, 2, 1, 1) #COMBOBOXES AND COMPRESS BUTTON self.codecComboBox = QComboBox(self) self.codecComboBox.setMinimumSize(80,23) self.codecComboBox.addItem("UT Video") self.codecComboBox.activated[str].connect(self.chooseCodec) self.alphaComboBox = QComboBox(self) self.alphaComboBox.setMinimumSize(80,23) self.alphaComboBox.addItem("No Alpha") self.alphaComboBox.addItem("with Alpha") self.alphaComboBox.activated[str].connect(self.chooseAlpha) self.frameRateComboBox = QComboBox(self) self.frameRateComboBox.setMinimumSize(80,23) self.frameRateComboBox.addItem("23.976") self.frameRateComboBox.addItem("24.00") self.frameRateComboBox.addItem("29.97") self.frameRateComboBox.addItem("30.00") self.frameRateComboBox.setCurrentIndex(1) self.frameRateComboBox.activated[str].connect(self.chooseFrameRate) self.compressButton = QPushButton('Compress', self) self.compressButton.setMinimumSize(80,23) self.compressButton.clicked[bool].connect(self.compressPress) self.gridLayoutControlls.addWidget(self.codecComboBox, 1, 0, 1, 1) self.gridLayoutControlls.addWidget(self.alphaComboBox, 1, 1, 1, 1) self.gridLayoutControlls.addWidget(self.frameRateComboBox, 1, 2, 1, 1) self.gridLayoutControlls.addWidget(self.compressButton, 1, 3, 1, 1) #RADIO BUTTON GROUP self.groupBox = QButtonGroup(self) self.radio1 = QRadioButton('Output file name from Folder name', self) self.radio1.setMinimumSize(80,25) self.radio2 = QRadioButton('Output file name from File name', self) self.radio2.setMinimumSize(80,25) self.radio1.setChecked(True) self.groupBox.addButton(self.radio1,1) self.groupBox.addButton(self.radio2,2) self.groupBox.buttonClicked[int].connect(self.radioBtnState) self.gridLayoutControlls.addWidget(self.radio1, 2, 0, 1, 2) self.gridLayoutControlls.addWidget(self.radio2, 2, 2, 1, 2) #LINE self.line = QFrame(self) self.line.setLineWidth(2) self.line.setMinimumHeight(3) self.line.setFrameShape(QFrame.HLine) self.line.setFrameShadow(QFrame.Sunken) self.gridLayoutControlls.addWidget(self.line, 3, 0, 1, 4) #PROGRESS BAR self.gridLayoutProgress = QGridLayout() self.gridLayoutProgress.setVerticalSpacing(11) self.gridLayoutProgress.setHorizontalSpacing(6) self.gridLayoutProgress.setSizeConstraint(QLayout.SetNoConstraint) self.removeGroupBox = QButtonGroup(self) self.addProgressBarUI(self.inList) self.removeGroupBox.buttonClicked[int].connect(self.removeButtonClicked) #ADD MORE AREA self.gridLayoutAddMore = QGridLayout() self.gridLayoutAddMore.setContentsMargins(0, 0, 0, 0) self.dragAndDropLabel_1 = QLabel("Drag and Drop folders here", self) self.dragAndDropLabel_1.setMinimumSize(QSize(120, 40)) self.dragAndDropLabel_1.setAlignment(Qt.AlignCenter) self.dragAndDropLabel_2 = QLabel("", self) self.dragAndDropLabel_2.setFixedSize(QSize(20, 40)) self.dragAndDropLabel_2.setAlignment(Qt.AlignCenter) self.dragAndDropLabel_2.setPixmap(self.folderPix) sI = QSpacerItem(40, 40,QSizePolicy.Expanding, QSizePolicy.Minimum) sI2 = QSpacerItem(40, 40,QSizePolicy.Expanding, QSizePolicy.Minimum) self.gridLayoutAddMore.addItem(sI, 1, 0, 1, 1) self.gridLayoutAddMore.addWidget(self.dragAndDropLabel_2, 1, 1, 1, 1) self.gridLayoutAddMore.addWidget(self.dragAndDropLabel_1, 1, 2, 1, 1) self.gridLayoutAddMore.addItem(sI2, 1, 3, 1, 1) #DEBUG AREA self.gridLayoutDebug = QGridLayout() self.line_2 = QFrame(self) self.line_2.setLineWidth(2) self.line_2.setFrameShape(QFrame.HLine) self.line_2.setFrameShadow(QFrame.Sunken) self.exploreOutputBtn = QPushButton('Explore Output',self) self.exploreOutputBtn.clicked[bool].connect(self.exploreOutput) self.hideShowLog = QPushButton('Show Log',self) self.hideShowLog.setCheckable(True) self.hideShowLog.clicked[bool].connect(self.showDebugLog) #self.hideShowLog.setMinimumSize(QSize(0, 20)) self.logText = QPlainTextEdit('',self) self.logText.setReadOnly(True) self.logText.hide() self.spacerItem = QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Expanding) self.gridLayoutDebug.addWidget(self.line_2, 0, 0, 1, 1) self.gridLayoutDebug.addWidget(self.exploreOutputBtn, 1, 0, 1, 1) self.gridLayoutDebug.addWidget(self.hideShowLog, 2, 0, 1, 1) self.gridLayoutDebug.addWidget(self.logText, 3, 0, 1, 1) self.gridLayoutDebug.addItem(self.spacerItem, 4, 0, 1, 1) self.verticalLayout.addLayout(self.gridLayoutControlls) self.verticalLayout.addLayout(self.gridLayoutProgress) self.verticalLayout.addLayout(self.gridLayoutAddMore) self.verticalLayout.addLayout(self.gridLayoutDebug) # Enable dragging and dropping onto the GUI self.setAcceptDrops(True) #QtCore.QMetaObject.connectSlotsByName(self) self.show() self.setMinimumSize(self.size()) self.threadpool = QThreadPool() self.threadpool.setMaxThreadCount(1) print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) ''' Progress bar populate function ''' def addProgressBarUI(self, arr): pbCount = len(self.pbList) for i in range(len(arr)): tempCheckBox = QCheckBox(self) tempCheckBox.setChecked(True) tempCheckBox.setMinimumSize(14,21) tempRemoveButton = QPushButton(self) tempRemoveButton.setIcon(self.removeIcon) tempRemoveButton.setFlat(True) tempRemoveButton.setIconSize(QSize(19,19)) tempRemoveButton.setFixedSize(QSize(19,21)) tempPB = QProgressBar(self) tempPB.setMinimumSize(50,21) tempPB.setMinimum(0) tempPB.setMaximum(100) tempPB.setTextVisible(True) tempPB.setFormat(str(arr[i])+" %p%") tempPB.setAlignment(Qt.AlignCenter) tempPB.setValue(0) if i==0: self.defaulStyle = tempPB.styleSheet() tempStatusLabel = QLabel(self) tempStatusLabel.setPixmap(self.okPix) tempStatusLabel.setMinimumSize(13,21) self.gridLayoutProgress.addWidget(tempCheckBox, pbCount+i, 0, 1, 1) self.gridLayoutProgress.addWidget(tempPB, pbCount+i, 1, 1, 1) self.gridLayoutProgress.addWidget(tempStatusLabel, pbCount+i, 2, 1, 1) self.gridLayoutProgress.addWidget(tempRemoveButton, pbCount+i, 3, 1, 1) self.removeGroupBox.addButton(tempRemoveButton, pbCount+i) self.pbList.append(tempPB) self.chList.append(tempCheckBox) self.lblList.append(tempStatusLabel) self.rmbList.append(tempRemoveButton) ''' Drag+Drop Functions ''' # The following three methods set up dragging and dropping for the app def dragEnterEvent(self, e): if e.mimeData().hasUrls: e.accept() else: e.ignore() def dragMoveEvent(self, e): if e.mimeData().hasUrls: e.accept() else: e.ignore() def dropEvent(self, e): """ Drop files directly onto the widget File locations are stored in fname :param e: :return: """ newFolders = [] if e.mimeData().hasUrls: e.setDropAction(Qt.CopyAction) e.accept() # Workaround for OSx dragging and dropping for url in e.mimeData().urls(): if op_sys == 'Darwin': #check for dir here as well fname = str(NSURL.URLWithString_(str(url.toString())).filePathURL().path()) else: fname = str(url.toLocalFile()) if os.path.isdir(fname) == True: newFolders.append(fname) #print(fname) #self.fname = fname #print(self.fname) #self.load_image() self.addNewFolders(newFolders) self.inList = self.inList + newFolders else: e.ignore() def addNewFolders(self, newFolders): self.addProgressBarUI(newFolders) self.setMinimumHeight(self.height()+len(newFolders)*32) #self.resize(self.width(),self.height()+200) self.update() self.adjustSize() self.setMinimumSize(self.size()) ''' Button Functions ''' def chooseAlpha(self, text): switcher={ "No Alpha":False, "with Alpha":True } self.alpha = switcher.get(text,"Invalid day of week") #print (self.alpha) def chooseCodec(self, text): switcher={ "UT Video":"utvideo" } self.codec = switcher.get(text,"Invalid day of week") #print (self.codec) def chooseFrameRate(self, text): self.frameRate = float(text) #print (self.frameRate) def currentData(self, widget): return widget.currentText() def radioBtnState(self, text): switcher={ 1:'Folder', 2:'File' } self.nameFrom = switcher.get(text,"Invalid day of week") #print(self.nameFrom) def removeButtonClicked(self, i): #print('remove trigger on id '+str(i)) #self.pbList.pop(i) ''' self.pbList[i].hide() self.chList[i].setChecked(False) self.chList[i].hide() self.lblList[i].hide() self.rmbList[i].hide() ''' print(self.gridLayoutProgress.rowCount()) self.pbList[i].setParent(None) self.chList[i].setChecked(False) self.chList[i].setParent(None) self.lblList[i].setParent(None) self.rmbList[i].setParent(None) self.removeGroupBox.removeButton(self.rmbList[i]) self.gridLayoutProgress.removeWidget(self.pbList[i]) self.gridLayoutProgress.removeWidget(self.chList[i]) self.gridLayoutProgress.removeWidget(self.lblList[i]) self.gridLayoutProgress.removeWidget(self.rmbList[i]) self.gridLayoutProgress.invalidate() self.gridLayoutProgress = QGridLayout() self.gridLayoutProgress.setVerticalSpacing(11) self.gridLayoutProgress.setHorizontalSpacing(6) self.gridLayoutProgress.setSizeConstraint(QLayout.SetDefaultConstraint) self.verticalLayout.insertLayout(1,self.gridLayoutProgress) print(self.gridLayoutProgress.rowCount()) ''' print(self.pbList) print(self.chList) print(self.lblList) print(self.rmbList) ''' self.pbList.pop(i) self.chList.pop(i) self.lblList.pop(i) self.rmbList.pop(i) self.inList.pop(i) #clear the gridLayout for j in reversed(range(len(self.pbList))): self.pbList[j].setParent(None) self.chList[j].setParent(None) self.lblList[j].setParent(None) self.rmbList[j].setParent(None) #reorder the gridLayout for j in range(len(self.pbList)): self.gridLayoutProgress.addWidget(self.chList[j], j, 0, 1, 1) self.gridLayoutProgress.addWidget(self.pbList[j], j, 1, 1, 1) self.gridLayoutProgress.addWidget(self.lblList[j], j, 2, 1, 1) self.gridLayoutProgress.addWidget(self.rmbList[j], j, 3, 1, 1) self.removeGroupBox.setId(self.rmbList[j],j) self.setMinimumHeight(self.height()-30) #self.setMinimumHeight(100) self.adjustSize() self.setMinimumSize(self.size()) print(self.gridLayoutProgress.rowCount()) #self.correctSize() ''' for j in range(len(self.removeGroupBox.buttons())): button = self.removeGroupBox.buttons()[j] #print(button) #print('original id '+str(self.removeGroupBox.id(button))) #print('new id '+str(j)) self.removeGroupBox.setId(button,j) ''' def correctSize(self): self.logText.show() self.gridLayoutDebug.removeItem(self.spacerItem) self.adjustSize() self.setMinimumSize(self.size()) self.repaint() self.gridLayoutDebug.addItem(self.spacerItem) self.logText.hide() self.setMinimumHeight(100) self.adjustSize() self.setMinimumSize(self.size()) def showDebugLog(self, bol): if bol: self.logText.show() self.hideShowLog.setText('Hide Log') self.gridLayoutDebug.removeItem(self.spacerItem) self.adjustSize() #self.resize(self.width(), self.height()+80) self.setMinimumSize(self.size()) else: self.gridLayoutDebug.addItem(self.spacerItem) self.logText.hide() self.hideShowLog.setText('Show Log') self.setMinimumHeight(100) self.adjustSize() self.setMinimumSize(self.size()) #self.resize(self.width(), 100) #self.setGeometry(0,0,self.width(),100) ''' Execution Functions ''' def execute_this_fn(self, path, codec, alpha, frameRate, nameFrom, i, progress_callback, errorFFMPEG_callback): #print(path) pyCompression = pyFFMEGCompress(path, codec, alpha, frameRate, nameFrom) ffProcess = pyCompression.ffmpegCompress() self.lblList[i].setPixmap(self.processingPix) #with kwargs kwargs = {'progress_callback':progress_callback, 'errorFFMPEG_callback':errorFFMPEG_callback} pyCompression.printProcess(ffProcess, **kwargs) return (pyCompression.debugString,pyCompression.error,i) def printOutput(self, s): print("Printing output "+ str(s)) def threadComplete(self, r): #print("THREAD COMPLETE! WITH ERROR " + str(r[2]) ) if r[1] == False: self.lblList[r[2]].setPixmap(self.goodPix) self.pbList[r[2]].setStyleSheet("QProgressBar::chunk {background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #44dd14,stop: 0.4999 #39c10f,stop: 0.5 #39c10f,stop: 1 #39c10f );border-radius: 3px; border: 1px solid #29880b;}QProgressBar{color:white}") self.pbList[r[2]].setValue(100) def errorPB(self, err): for i in range(len(self.pbList)): if self.pbList[i].format() == err: self.pbList[i].setValue(100) self.pbList[i].setStyleSheet("QProgressBar::chunk {background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #FF0350,stop: 0.4999 #FF0020,stop: 0.5 #FF0019,stop: 1 #FF0000 );border-radius: 3px; border: 1px solid #a60233;}QProgressBar{color:white}") self.pbList[i].setFormat(self.pbList[i].format()+" - Error") self.chList[i].setChecked(False) self.lblList[i].setPixmap(self.badPix) def resetProgressBar(self, pb, text, lbl): pb.setValue(0) pb.setFormat(text + ' %p%') pb.setStyleSheet(self.defaulStyle) lbl.setPixmap(self.okPix) def compressPress(self): for i in range(len(self.pbList)): if self.chList[i].isChecked(): self.resetProgressBar(self.pbList[i],self.inList[i],self.lblList[i]) worker = Worker(self.execute_this_fn, self.inList[i], self.codec, self.alpha, self.frameRate, self.nameFrom, i) # Any other args, kwargs are passed to the run function #worker.signals.result.connect(self.printOutput) worker.signals.result.connect(self.logText.appendPlainText) worker.signals.progress.connect(self.pbList[i].setValue) worker.signals.errorFFMPEG.connect(self.errorPB) worker.signals.error.connect(self.errorPB) worker.signals.finished.connect(self.threadComplete) #worker.signals.finished.connect(self.logText.appendPlainText) # Execute self.threadpool.start(worker) def exploreOutput(self): FILEBROWSER_PATH = os.path.join(os.getenv('WINDIR'), 'explorer.exe') explorePath = os.path.normpath('C:\\ExportedMOVs') subprocess.run([FILEBROWSER_PATH, explorePath])
class MainView(QMainWindow): select_module = pyqtSignal(str) new_module = pyqtSignal() test_module = pyqtSignal(str, int, int) new_puzzle = pyqtSignal(str) review_puzzle = pyqtSignal(str, str) self_compat = pyqtSignal() closed = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("PuyoTrainer v1.0.0") main_widget = QWidget() self.setCentralWidget(main_widget) self.layout = QVBoxLayout(main_widget) self._createStatusBar() self._createPuzzleSelector() self._hlineSeparator() self._createModuleControls() self._hlineSeparator() self._createSettingSelector() def closeEvent(self, event): self.closed.emit() super().closeEvent(event) def show(self): self.select_module.emit(self.module()) return super().show() def setModuleMetadata(self, module): new_module_layout = ViewModuleFormLayout(module) def transfer_widget(row, col): widget = self.module_layout.itemAtPosition(row, col).widget() new_module_layout.addWidget(widget) transfer_widget(1, 0) transfer_widget(1, 1) transfer_widget(2, 0) transfer_widget(2, 1) deleteItemOfLayout(self.layout, 2) self.layout.insertLayout(2, new_module_layout) self.module_layout = new_module_layout if module is None: self._updatePuzzleSelector(empty=True) def addModuleSelector(self, modulename): self.module_selector.addItem(modulename, userData=modulename) def _createStatusBar(self): status_bar = QStatusBar() def addLink(link, text): label = QLabel('<a href="' + link + '">' + text + "</a>") label.setOpenExternalLinks(True) status_bar.addWidget(label) addLink(link="https://twitter.com/terramyst1", text="by terramyst, ") addLink(link="https://github.com/amosborne/puyo-trainer", text="github.") status_bar.addWidget( QLabel("(keyboard usage: arrow keys, x, z, spacebar)")) self.setStatusBar(status_bar) def _hlineSeparator(self): hline = QFrame() hline.setFrameShape(QFrame.HLine) self.layout.addWidget(hline) def _createSettingSelector(self): sublayout = QHBoxLayout() skins = [f for f in os.listdir(SKIN_DIRECTORY) if f.endswith(".png")] label = QLabel("Puyo Skin:") label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) label.setStyleSheet("font-weight: bold") combo_box = QComboBox() combo_box.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) for skin in skins: root, _ = os.path.splitext(skin) combo_box.addItem(root, userData=skin) sublayout.addWidget(label) sublayout.addWidget(combo_box) self.skin = combo_box.currentData label = QLabel("Test Moves:") label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) label.setStyleSheet("font-weight: bold") combo_box = QComboBox() combo_box.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) for val in range(1, 11): combo_box.addItem(str(val), userData=val) sublayout.addWidget(label) sublayout.addWidget(combo_box) self.movelen = combo_box.currentData label = QLabel("Test Period:") label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) label.setStyleSheet("font-weight: bold") combo_box = QComboBox() combo_box.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) for val in range(1, 11): combo_box.addItem(str(val), userData=val) sublayout.addWidget(label) sublayout.addWidget(combo_box) self.fbdelay = combo_box.currentData self.layout.addLayout(sublayout) def _createPuzzleSelector(self): modlabel = QLabel("Module:") modlabel.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) modlabel.setStyleSheet("font-weight: bold") modcombo = QComboBox() modcombo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) modules = [ d for d in os.listdir(MODULE_DIRECTORY) if os.path.isdir(os.path.join(MODULE_DIRECTORY, d)) ] for module in modules: modcombo.addItem(module, userData=module) self.module = modcombo.currentData def module_select(): self._updatePuzzleSelector() self.select_module.emit(self.module()) modcombo.currentIndexChanged.connect(module_select) self.module_selector = modcombo puzlabel = QLabel("Puzzle:") puzlabel.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) puzlabel.setStyleSheet("font-weight: bold") puzcombo = QComboBox() puzcombo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.puzzle = puzcombo.currentData self.puzzle_selector = puzcombo self._updatePuzzleSelector() self.selfcompat_button = QPushButton("Self-Compat") self.selfcompat_button.clicked.connect(self.self_compat) puzzle_select_layout = QHBoxLayout() puzzle_select_layout.addWidget(modlabel) puzzle_select_layout.addWidget(modcombo) puzzle_select_layout.addWidget(puzlabel) puzzle_select_layout.addWidget(puzcombo) puzzle_select_layout.addWidget(self.selfcompat_button) self.layout.addLayout(puzzle_select_layout) def setCompatStatus(self, isactive): if isactive: self.selfcompat_button.setStyleSheet("font: bold; color: red") else: self.selfcompat_button.setStyleSheet("font: bold; color: blue") def _updatePuzzleSelector(self, empty=False): self.puzzle_selector.clear() if empty or self.module() is None: return _, _, filenames = next(os.walk(MODULE_DIRECTORY + self.module())) puzzle_files = [ filename for filename in filenames if filename.startswith(PUZZLE_FILE_ROOT) and filename.endswith(PUZZLE_FILE_EXT) ] for filename in sorted(puzzle_files): root, _ = os.path.splitext(filename) self.puzzle_selector.addItem(root, userData=root) def _createModuleControls(self): new_module_button = QPushButton("New Module") new_module_button.clicked.connect(lambda: self.new_module.emit()) new_module_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) test_module_button = QPushButton("Test Module") test_module_button.clicked.connect(lambda: self.test_module.emit( self.skin(), self.movelen(), self.fbdelay())) test_module_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) new_puzzle_button = QPushButton("New Puzzle") new_puzzle_button.clicked.connect( lambda: self.new_puzzle.emit(self.skin())) new_puzzle_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) review_puzzle_button = QPushButton("Review Puzzle") review_puzzle_button.clicked.connect( lambda: self.review_puzzle.emit(self.skin(), self.puzzle())) review_puzzle_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) module_control_layout = ViewModuleFormLayout() module_control_layout.addWidget(new_module_button, 1, 0) module_control_layout.addWidget(test_module_button, 1, 1) module_control_layout.addWidget(new_puzzle_button, 2, 0) module_control_layout.addWidget(review_puzzle_button, 2, 1) self.layout.addLayout(module_control_layout) self.module_layout = module_control_layout
class MemoryGui(QWidget): def __init__(self, parent=None): super(MemoryGui, self).__init__(parent) self.title = "Memory Game" self.left = 200 self.top = 100 self.width = 500 self.height = 300 self.initialize_match_settings() self.initUI() def initUI(self): #create widgets self.current_playing_player = QLabel("Current playing player: "+str(get_game_setting("player_one_name"))) self.player_one_score = QLabel("Player: "+str(get_game_setting("player_one_name"))+", score: "+str(self.memory_match.get_player_one_score())) self.player_two_score = QLabel("Player: "+str(get_game_setting("player_two_name"))+", score: "+str(self.memory_match.get_player_two_score())) self.next_turn_button = QPushButton("Next turn") self.next_turn_button.setEnabled(False) self.next_turn_button.clicked.connect(self.next_turn) self.go_back_to_setup_button = QPushButton("Go to Setup") self.finished_game_message = QMessageBox() self.finished_game_message.setWindowTitle("The memory game is finished") self.finished_game_message.setText("") self.finished_game_message.setStandardButtons(QMessageBox.Ok) horizontal_go_back_to_setup_box = QHBoxLayout() horizontal_go_back_to_setup_box.addWidget(self.go_back_to_setup_button) horizontal_go_back_to_setup_box.addStretch() self.grid = self.create_playfield() horizontal_playing_player_box = QHBoxLayout() horizontal_playing_player_box.addWidget(self.current_playing_player) horizontal_playing_player_box.addStretch() horizontal_playing_player_box.addWidget(self.next_turn_button) horizontal_player_scores_box = QHBoxLayout() horizontal_player_scores_box.addWidget(self.player_one_score) horizontal_player_scores_box.addStretch() horizontal_player_scores_box.addWidget(self.player_two_score) self.vertical_box = QVBoxLayout() self.vertical_box.insertLayout(1, horizontal_go_back_to_setup_box) self.vertical_box.insertLayout(2, self.grid) self.vertical_box.insertLayout(3, horizontal_playing_player_box) self.vertical_box.insertLayout(4, horizontal_player_scores_box) self.setWindowTitle(self.title) self.setLayout(self.vertical_box) self.setGeometry(self.left, self.top, self.width, self.height) def create_playfield(self): #create a matrix of buttons which will be the playing field. self.grid = QGridLayout() columns, rows = get_row_and_colum(int(get_game_setting("playfield_size"))) positions = [(column, row) for column in range(int(columns)) for row in range(int(rows))] self.add_buttons_to_grid(positions) return self.grid def initialize_match_settings(self): #set the memory_match object with the default beginning values and initialize the playfield names column_count, row_count = get_row_and_colum(int(get_game_setting("playfield_size"))) self.memory_match = MemoryMatch("player_one") self.registered_buttons = RegisterButtons(column_count, row_count) self.registered_buttons.initialize_button_dict() #accumulate +1 for started games. update_game_statistics("started_games") def button_pressed(self, button): #display the "hidden" text of the button and disable it. #when a button is pressed,(1) check which button it was (2) do some logic rules coordinates = button.text() hidden_name = self.registered_buttons.get_playfield_name(coordinates) button.setText(hidden_name) button.setEnabled(False) print("Button: "+coordinates+" was pressed") #accumulate the turn and save the guess of the player. self.memory_match.accumulate_turn() self.memory_match.save_clicked_button(coordinates, hidden_name) #check if the current player has chosen two cards if is_turn_over(self.memory_match.get_turn()) == False: #the turn is not over return else: #the turn is over. Disable all buttons and check if the player has guessed correctly. self.disable_all_buttons() self.process_turn() self.has_game_ended() def process_turn(self): #did the player guess two cards correctly or did he not? first_clicked_button = self.memory_match.get_clicked_buttons()[0] second_clicked_button = self.memory_match.get_clicked_buttons()[1] if first_clicked_button["card_name"] == second_clicked_button["card_name"]: #the guess was correct :-) print("The current player guessed two cards correctly. ") #add a point to the current player self.accumulate_point_to_current_player() #Make the correctly guessed buttons no longer clickable. self.registered_buttons.set_button_status(first_clicked_button["position"], False) self.registered_buttons.set_button_status(second_clicked_button["position"], False) self.outcome = True else: #The guess was not correct :-( print("The current player did not guess two cards correctly. ") self.outcome = False self.next_turn_button.setEnabled(True) def set_original_button_text(self): for index in range(0, 2): button_index = self.memory_match.get_clicked_buttons()[index]["position"] self.registered_buttons.get_button_instance(button_index)["button_instance"].setText(button_index) def accumulate_point_to_current_player(self): if self.memory_match.get_current_player() == "player_one": self.memory_match.accumulate_player_one_score() self.player_one_score.setText("Player: " + str(get_game_setting("player_one_name")) + ", score: " + str( self.memory_match.get_player_one_score())) else: self.memory_match.accumulate_player_two_score() self.player_two_score.setText("Player: " + str(get_game_setting("player_two_name")) + ", score: " + str( self.memory_match.get_player_two_score())) def next_turn(self): #keep the correctly guessed buttons disabled and make everything ready for a new turn #disable the next_turn_button self.next_turn_button.setEnabled(False) #set the turn back to zero self.memory_match.set_turn_to_zero() #set the current player self.set_current_player(self.memory_match.get_current_player()) #If the previous player did not guess correctly, the buttons are reset to there original message. if self.outcome == False: self.set_original_button_text() #remove the clicked buttons from the previous turn self.memory_match.remove_clicked_buttons() #enable the buttons for the next turn. self.enabled_buttons() def enabled_buttons(self): #enable the buttons which need to be enabled. for button in self.registered_buttons.get_button_dict().values(): button["button_instance"].setEnabled(button["status"]) def disable_all_buttons(self): for button in self.registered_buttons.get_button_dict().values(): button["button_instance"].setEnabled(False) def set_current_player(self, current_player): if current_player == "player_one": #time to set the new current player to player_two self.memory_match.set_current_player("player_two") self.current_playing_player.setText("Current playing player: " + str(get_game_setting("player_two_name"))) elif current_player == "player_two": #time to set the new current player to player_one self.memory_match.set_current_player("player_one") self.current_playing_player.setText("Current playing player: " + str(get_game_setting("player_one_name"))) def has_game_ended(self): #check if the game has ended. status_list = [button["status"] for button in self.registered_buttons.get_button_dict().values()] if True in status_list: #the game has not ended yet return else: #the game has ended. Show a message with the game outcome. game_outcome = analyze_game_outcome(str(get_game_setting("player_one_name")), str(get_game_setting("player_two_name")), str(self.memory_match.get_player_one_score()),str(self.memory_match.get_player_two_score())) self.finished_game_message.setText(game_outcome) self.finished_game_message.show() # disable the next_turn_button self.next_turn_button.setEnabled(False) # set the turn back to zero self.memory_match.set_turn_to_zero() # remove the clicked buttons from the previous turn self.memory_match.remove_clicked_buttons() #set the scores 0 for both player_one and player_two and update the labels to this new score. self.memory_match.set_player_one_score_to_zero() self.memory_match.set_player_two_score_to_zero() self.player_one_score.setText("Player: "+str(get_game_setting("player_one_name"))+", score: "+str(self.memory_match.get_player_one_score())) self.player_two_score.setText("Player: "+str(get_game_setting("player_two_name"))+", score: "+str(self.memory_match.get_player_two_score())) #make player_one the first player for coming game self.memory_match.set_current_player("player_one") #Add plus one to the finished games statistic update_game_statistics("finished_games") #Refresh the contents of the buttons self.update_content_buttons() def update_content_buttons(self): # All buttons need to be clickable again, have a new hidden_name and have a viewable message. names = self.registered_buttons.get_X_different_names(self.grid.columnCount(), self.grid.rowCount()) index = 0 for position, button in self.registered_buttons.get_button_dict().items(): button["status"] = True button["hidden_name"] = names[index] button["button_instance"].setText(position) button["button_instance"].setEnabled(True) index += 1 def update_grid(self): current_column_count, current_row_count = get_row_and_colum(int(get_game_setting("playfield_size"))) current_positions = [(column, row) for column in range(int(current_column_count)) for row in range(int(current_row_count))] self.registered_buttons.initialize_button_dict() self.remove_grid_buttons() self.add_buttons_to_grid(current_positions) def remove_grid_buttons(self): for i in reversed(range(self.grid.count())): widgetToRemove = self.grid.itemAt(i).widget() # remove it from the layout list self.grid.removeWidget(widgetToRemove) # remove it from the gui widgetToRemove.setParent(None) def add_buttons_to_grid(self, positions): for position in positions: coordinates = str(position[0])+"_"+str(position[1]) button = QPushButton(coordinates) button.setFixedHeight(50) self.registered_buttons.set_button_instance(coordinates, button) self.registered_buttons.get_button_instance(coordinates)["button_instance"].clicked.connect(lambda state, button=button: self.button_pressed(button)) self.grid.addWidget(self.registered_buttons.get_button_instance(coordinates)["button_instance"], position[0], position[1]) print("There are "+str(self.grid.count())+" buttons/cards in the grid")
class DangerConfEditor(ViewInfoChanger): def __init__(self, header, info, parent: ViewShower): super().__init__(header, info, parent) self.way_layout = QVBoxLayout() push_btn = self.main_layout.takeAt(self.main_layout.count() - 1).widget() self.worker_id = info[0] self.slave_combo_config = { "название_параметра": ("параметры", "*", "название_параметра", "код_параметра") } self.combo_change_idx["название_параметра"] = {} add_btn = QPushButton("Добавить") add_btn.clicked.connect(lambda e: self.add_cell(-1)) self.main_layout.addWidget(add_btn) way = self.db.execute( f"SELECT код_параметра, название_параметра, нижний_допустимый_порог, верхний_допустимый_порог FROM `подчинённые_допустимые_значения_параметров_view` where `код_категории_вредности` = {self.worker_id}" ) for pos, point in enumerate(way, start=-1): param_name = str(point[1]) self.combo_change_idx["название_параметра"][param_name] = point[0] self.add_cell(pos, param_name, point[2], point[3]) self.main_layout.addLayout(self.way_layout) self.main_layout.addWidget(push_btn) def add_cell(self, pos: int, txt="", dnw_val="", up_val=""): """Вставляет ячейку ниже активирующей кнопки для вставки на уровне надо передать ::pos:: = -1""" cell = QHBoxLayout() edi = QLineEdit() edi.setText(txt) dwn_val_edt = QLineEdit() dwn_val_edt.setText(str(dnw_val)) up_val_edt = QLineEdit(str(up_val)) add_btn = QPushButton("Добавить") del_btn = QPushButton("Удалить") cmb = QComboBox() cmb.addItem(txt) edi.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) cmb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) dwn_val_edt.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) up_val_edt.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) add_btn.clicked.connect(lambda e, c=cell: self.add_cell(c.pos)) del_btn.clicked.connect(lambda e, c=cell: self.del_cell(c.pos)) edi.editingFinished.connect( lambda c=cmb, t=edi.text: self.slave_combo_update( "название_параметра", c, t())) # le-kostyl cell.pos = pos cell.addWidget(edi) cell.addWidget(cmb) cell.addWidget(dwn_val_edt) cell.addWidget(up_val_edt) cell.addWidget(add_btn) cell.addWidget(del_btn) for i in range(pos + 1, self.way_layout.count()): cell_to_move = self.way_layout.itemAt(i) cell_to_move.pos += 1 cell.pos += 1 # для вставки ниже активированной кнопки self.way_layout.insertLayout(cell.pos, cell) def slave_combo_update(self, c_name: str, c: QComboBox, text: str): # grand le-kostyl tmp = self.combo_config self.combo_config = self.slave_combo_config self.combo_update(c_name, c, text) self.combo_config = tmp def del_cell(self, pos): cell: QVBoxLayout cell = self.way_layout.takeAt(pos) for i in range(cell.count()): w = cell.takeAt(0).widget() w.deleteLater() cell.deleteLater() for i in range(pos, self.way_layout.count()): cell_to_move = self.way_layout.itemAt(i) cell_to_move.pos -= 1 def push_slave_changes(self): params = [] kakoito_set = set() try: for i in range(self.way_layout.count()): cell = self.way_layout.itemAt(i) w = cell.itemAt(1).widget() param = w.currentText() if param: if param in kakoito_set: raise KeyError kakoito_set.add(param) param_id = self.combo_change_idx["название_параметра"][ param] dwn_w = cell.itemAt(2).widget() up_w = cell.itemAt(3).widget() dwn_val = dwn_w.text() up_val = up_w.text() if not dwn_val: dwn_val = 0 if not up_val: up_val = 0 params.append((self.worker_id, param_id, dwn_val, up_val)) query = f" insert into `допустимые_значения_параметров` values(%s, %s, %s, %s)" self.db.execute( "delete from `допустимые_значения_параметров` where код_категории_вредности = %s", (self.worker_id, )) self.db.executemany(query, params) self.db.commit() except KeyError as er: from PyQt5.QtWidgets import QErrorMessage error_widget = QErrorMessage() error_widget.showMessage(f"Дубликат параметра") error_widget.exec() def push_changes(self): self.push_slave_changes() super().push_changes()
class PowerBIThemeGeneratorWindow(QMainWindow): _powerBIThemeGenerator = None _pbiFilePath = None _horizontalLayoutTabVisualsTop = None _horizontalLayoutWelcomeScreen = None _groupBoxSelectedVisualPropertiesTree = None _listWidgetReportPages = None _listWidgetReportPageVisuals = None _verticalLayoutVisualsPropertiesTab = None _verticalLayoutMainWindow = None _treeWidgetSelectedVisualProperties = None _tabWidgetMainWindow = None _tabGeneralProperties = None _generalProperties = {} def __init__(self): super().__init__() # Windows Settings self.setWindowTitle(AppInfo.Name + ' - ' + AppInfo.Version) self.setGeometry(100, 100, 960, 720) # Creating central widget element self.centralWidget = QWidget(self) self.setCentralWidget(self.centralWidget) # Creating status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) # Creating menu bar self._createMenuBar() # Creating main window layout and adding widgets to them self._verticalLayoutMainWindow = QVBoxLayout(self.centralWidget) self._verticalLayoutMainWindow.addWidget(self._tabWidgetMainWindow) # for testing # self.__testOpenFileMethod() # end for testing self._horizontalLayoutWelcomeScreen = QHBoxLayout() self._createTabs() self.show() def _createMenuBar(self): # Create menu bar menuBar = self.menuBar() # Create root menu such as File, Edit, Help etc. menuBarFile = menuBar.addMenu('&File') menuBarHelp = menuBar.addMenu('&Help') # Create actions for Menu actionOpenPowerBIFile = QAction('Open &Power BI File', self) actionOpenPowerBIFile.setShortcut('Ctrl+O') actionOpenPowerBIFile.setStatusTip('Import Power BI file from which you want to export the visual settings to ' 'create theme') # actionReloadPowerBIFile = QAction('&Reload Power BI File', self) # actionReloadPowerBIFile.setShortcut('Ctrl+R') # actionReloadPowerBIFile.setStatusTip('Click to reload Power BI file if there are changes made in Power BI # file') actionGenerateTheme = QAction('&Generate Theme', self) actionGenerateTheme.setShortcut('Ctrl+G') actionGenerateTheme.setStatusTip('Click to generate theme and save theme file') actionQuit = QAction('&Quit', self) actionQuit.setShortcut('Ctrl+Q') actionQuit.setStatusTip('Click to quit application') actionAbout = QAction('&About', self) actionAbout.setStatusTip('Click to see detail application information and useful links') # Add actions to menu menuBarFile.addAction(actionOpenPowerBIFile) # menuBarFile.addAction(actionReloadPowerBIFile) menuBarFile.addAction(actionGenerateTheme) menuBarFile.addSeparator() menuBarFile.addAction(actionQuit) menuBarHelp.addAction(actionAbout) # Add global events i.e. what will happen when option is selected from menu bar actionOpenPowerBIFile.triggered.connect(self._openPowerBIFileDialog) actionQuit.triggered.connect(self.close) actionGenerateTheme.triggered.connect(self.generateTheme) actionAbout.triggered.connect(self._showAboutDialog) def _openPowerBIFileDialog(self): try: directory = '/' if self._pbiFilePath is not None: directory = self._pbiFilePath.split('/')[0:-1] directory = "/".join(directory) self._pbiFilePath = QFileDialog.getOpenFileName( self, caption='Select Power BI File', directory=directory, filter='Power BI Files(*.pbix)' )[0] if self._pbiFilePath is not '' and not None: if self._pbiFilePath.split('/')[-1].split('.')[-1] == 'pbix': self._powerBIThemeGenerator = PowerBIThemeGenerator(self._pbiFilePath) else: raise ValueError('Invalid Power BI File') self._reportVisualData = self._powerBIThemeGenerator.modifiedDataStructure() self._populateTabVisualProperties() except Exception as e: ShowErrorDialog(LogException(e)) def __testOpenFileMethod(self): self._pbiFilePath = 'G:/Power BI Reports/Theme Template.pbix' self._powerBIThemeGenerator = PowerBIThemeGenerator(self._pbiFilePath) self._reportVisualData = self._powerBIThemeGenerator.modifiedDataStructure() def _createTabs(self): # Creating tabs self._tabWidgetMainWindow = QTabWidget(self.centralWidget) self._tabGeneralProperties = QWidget() self._tabVisualProperties = QWidget() self._tabWidgetMainWindow.addTab(self._tabGeneralProperties, 'General Properties') self._tabWidgetMainWindow.addTab(self._tabVisualProperties, 'Visuals Properties') tabBarMainWindow: QTabBar = self._tabWidgetMainWindow.tabBar() tabBarMainWindow.setTabToolTip(0, 'Add optional general properties of the theme') tabBarMainWindow.setTabToolTip(1, 'All the Power BI File visuals related properties will be visible here') self._verticalLayoutVisualsPropertiesTab = QVBoxLayout(self._tabVisualProperties) self._verticalLayoutMainWindow.addWidget(self._tabWidgetMainWindow) self._populateTabGeneralProperties() self._populateTabVisualProperties() def _populateTabVisualProperties(self): if self._pbiFilePath is None: self._showWelcomeScreenTabVisualProperties() self._verticalLayoutVisualsPropertiesTab.addLayout(self._horizontalLayoutWelcomeScreen) else: self._clearTabVisualProperties() self._horizontalLayoutTabVisualsTop = QHBoxLayout() self._verticalLayoutVisualsPropertiesTab.insertLayout(0, self._horizontalLayoutTabVisualsTop) self._createReportPageList() # self._create_extra_options() def _populateTabGeneralProperties(self): try: dataColors = [] def __showMessageBoxInvalidHexColor(hexColor): messageBoxInvalidColor = QMessageBox() messageBoxInvalidColor.setIcon(QMessageBox.Critical) messageBoxInvalidColor.setWindowTitle('Invalid Hex Color Code') messageBoxInvalidColor.setText('"' + hexColor + '"' + ' is not valid hex color code') messageBoxInvalidColor.exec_() def __validateDataColors(): try: dataColors = [dataColor.strip() for dataColor in lineEditDataColors.text().split(',')] for dataColor in dataColors: if ColorUtil.IsValidHexColor(dataColor) is False and dataColor is not '': dataColors.remove(dataColor) __showMessageBoxInvalidHexColor(dataColor) elif dataColor is '': dataColors.remove(dataColor) lineEditDataColors.setText(','.join(dataColors)) except Exception as e: ShowErrorDialog(LogException(e)) def __showColorPickerDialog(): try: sender = self.sender().objectName() color: QColor = QColorDialog().getColor() if color.isValid(): hexColor = color.name() buttonBackgroundColor = 'background-color: ' + hexColor if sender == 'push_button_data_color': dataColors.append(hexColor) self._generalProperties['dataColors'] = dataColors lineEditDataColors.setText(','.join(dataColors)) elif sender == 'push_button_background_color': pushButtonBackgroundColorPicker.setStyleSheet(buttonBackgroundColor) lineEditBackgroundColor.setText(hexColor) self._generalProperties['background'] = hexColor elif sender == 'push_button_foreground_color': pushButtonForegroundColorPicker.setStyleSheet(buttonBackgroundColor) lineEditForegroundColor.setText(hexColor) self._generalProperties['foreground'] = hexColor elif sender == 'push_button_table_accent_color': pushButtonTableAccentColorPicker.setStyleSheet(buttonBackgroundColor) lineEditTableAccent.setText(hexColor) self._generalProperties['tableAccent'] = hexColor except Exception as e: ShowErrorDialog(LogException(e)) def __lineEditEditingFinished(): try: sender = self.sender().objectName() if sender == 'line_edit_data_color': __validateDataColors() nonlocal dataColors dataColors = lineEditDataColors.text().split(",") self._generalProperties['dataColors'] = dataColors return elif sender == 'line_edit_background_color': textColor = lineEditBackgroundColor.text() elif sender == 'line_edit_foreground_color': textColor = lineEditForegroundColor.text() elif sender == 'line_edit_table_accent_color': textColor = lineEditTableAccent.text() elif sender == 'line_edit_theme_name': self._generalProperties['name'] = lineEditThemeName.text() return validColor = ColorUtil.IsValidHexColor(textColor) if validColor is False and textColor is not '': __showMessageBoxInvalidHexColor(textColor) if sender == 'line_edit_background_color': lineEditBackgroundColor.setText('') elif sender == 'line_edit_foreground_color': lineEditForegroundColor.setText('') elif sender == 'line_edit_table_accent_color': lineEditTableAccent.setText('') else: buttonBackgroundColor = 'background-color: ' + textColor if sender == 'line_edit_background_color': self._generalProperties['background'] = textColor pushButtonBackgroundColorPicker.setStyleSheet(buttonBackgroundColor) elif sender == 'line_edit_foreground_color': pushButtonForegroundColorPicker.setStyleSheet(buttonBackgroundColor) self._generalProperties['foreground'] = textColor elif sender == 'line_edit_table_accent_color': pushButtonTableAccentColorPicker.setStyleSheet(buttonBackgroundColor) self._generalProperties['tableAccent'] = textColor except Exception as e: ShowErrorDialog(LogException(e)) labelThemeName = QLabel('Theme Name: ') lineEditThemeName = QLineEdit() lineEditThemeName.setObjectName('line_edit_theme_name') lineEditThemeName.editingFinished.connect(__lineEditEditingFinished) horizontalLayoutDataColor = QHBoxLayout() labelDataColors = QLabel('Data Colors: ') labelDataColors.setToolTip('These colors will be visible in color picker and will apply as data colors in ' 'charts') lineEditDataColors = QLineEdit() lineEditDataColors.editingFinished.connect(__lineEditEditingFinished) lineEditDataColors.setObjectName('line_edit_data_color') pushButtonDataColorPicker = QPushButton() pushButtonDataColorPicker.setToolTip('Click to pick colors for Data Colors') pushButtonDataColorPicker.setObjectName('push_button_data_color') pushButtonDataColorPicker.clicked.connect(__showColorPickerDialog) horizontalLayoutDataColor.addWidget(lineEditDataColors) horizontalLayoutDataColor.addWidget(pushButtonDataColorPicker) # TO DO: Add generate colors button horizontalLayoutBackgroundColor = QHBoxLayout() labelBackgroundColor = QLabel('Background Color: ') labelBackgroundColor.setToolTip('Applies to button fill and combo chart label background. How these colors ' 'are used depends on the specific visual style that\'s applied.') lineEditBackgroundColor = QLineEdit() lineEditBackgroundColor.setObjectName('line_edit_background_color') lineEditBackgroundColor.editingFinished.connect(__lineEditEditingFinished) pushButtonBackgroundColorPicker = QPushButton() pushButtonBackgroundColorPicker.setToolTip('Click to pick color for Background Color') pushButtonBackgroundColorPicker.setObjectName('push_button_background_color') pushButtonBackgroundColorPicker.clicked.connect(__showColorPickerDialog) horizontalLayoutBackgroundColor.addWidget(lineEditBackgroundColor) horizontalLayoutBackgroundColor.addWidget(pushButtonBackgroundColorPicker) horizontalLayoutForegroundColor = QHBoxLayout() labelForegroundColor = QLabel('Foreground Color: ') labelForegroundColor.setToolTip('Applies to textbox text, KPI goal text, multi-row card text, card value ' 'text, gauge callout text, vertical slicer element text, and table and ' 'matrix total and values text.') lineEditForegroundColor = QLineEdit() lineEditForegroundColor.setObjectName('line_edit_foreground_color') lineEditForegroundColor.editingFinished.connect(__lineEditEditingFinished) pushButtonForegroundColorPicker = QPushButton() pushButtonForegroundColorPicker.setToolTip('Click to pick color for Foreground Color') pushButtonForegroundColorPicker.setObjectName('push_button_foreground_color') pushButtonForegroundColorPicker.clicked.connect(__showColorPickerDialog) horizontalLayoutForegroundColor.addWidget(lineEditForegroundColor) horizontalLayoutForegroundColor.addWidget(pushButtonForegroundColorPicker) horizontalLayoutTableAccent = QHBoxLayout() labelTableAccent = QLabel('Table Accent: ') labelTableAccent.setToolTip('Table and matrix visuals apply these styles by default.') lineEditTableAccent = QLineEdit() lineEditTableAccent.setObjectName('line_edit_table_accent_color') lineEditTableAccent.editingFinished.connect(__lineEditEditingFinished) pushButtonTableAccentColorPicker = QPushButton() pushButtonTableAccentColorPicker.setToolTip('Click to pick color for Table Accent Color') pushButtonTableAccentColorPicker.setObjectName('push_button_table_accent_color') pushButtonTableAccentColorPicker.clicked.connect(__showColorPickerDialog) horizontalLayoutTableAccent.addWidget(lineEditTableAccent) horizontalLayoutTableAccent.addWidget(pushButtonTableAccentColorPicker) formLayoutGeneralProperties = QFormLayout(self._tabGeneralProperties) formLayoutGeneralProperties.addRow(labelThemeName, lineEditThemeName) formLayoutGeneralProperties.addRow(labelDataColors, horizontalLayoutDataColor) formLayoutGeneralProperties.addRow(labelBackgroundColor, horizontalLayoutBackgroundColor) formLayoutGeneralProperties.addRow(labelForegroundColor, horizontalLayoutForegroundColor) formLayoutGeneralProperties.addRow(labelTableAccent, horizontalLayoutTableAccent) except Exception as e: ShowErrorDialog(LogException(e)) def _showWelcomeScreenTabVisualProperties(self): welcomeLabel = QLabel('Go to File menu or press Ctrl+O to select Power BI File') self._horizontalLayoutWelcomeScreen.addStretch() self._horizontalLayoutWelcomeScreen.addWidget(welcomeLabel) self._horizontalLayoutWelcomeScreen.addStretch() def _removeWelcomeScreenFromTabVisualProperties(self): self._deleteLayout(self._horizontalLayoutWelcomeScreen) def _clearTabVisualProperties(self): self._removeWelcomeScreenFromTabVisualProperties() self._deleteLayout(self._verticalLayoutVisualsPropertiesTab) self._listWidgetReportPages = None self._listWidgetReportPageVisuals = None self._treeWidgetSelectedVisualProperties = None def _deleteLayout(self, layout): if layout is not None: while layout.count(): item = layout.takeAt(0) widget = item.widget() if widget is not None: widget.deleteLater() else: self._deleteLayout(item.layout()) # sip.delete(layout) def _createReportPageList(self): try: def __reportPageListValueSelected(item): try: self._createReportPageVisualsList(item.GetMetaData()['reportPageSection']) except Exception as e: ShowErrorDialog(LogException(e)) if self._listWidgetReportPages is not None: self._listWidgetReportPages.clear() groupBoxReportPageList = QGroupBox(self.centralWidget) groupBoxReportPageList.setTitle("Report Pages") verticalLayoutReportPageList = QVBoxLayout(groupBoxReportPageList) self._listWidgetReportPages = QListWidget() i = 0 for reportSection in self._powerBIThemeGenerator.get_report_sections(): reportListWidgetItem = CQListWidgetItemReportPages(str(reportSection['displayName'])) reportListWidgetItem.SetMetaData(reportSection) reportListWidgetItem.setToolTip(reportSection['name']) self._listWidgetReportPages.insertItem(i, reportListWidgetItem) i += 1 verticalLayoutReportPageList.addWidget(self._listWidgetReportPages) self._horizontalLayoutTabVisualsTop.addWidget(groupBoxReportPageList) # __reportPageListValueSelected(self._listWidgetReportPages.item(0)) self._listWidgetReportPages.itemClicked.connect(__reportPageListValueSelected) except Exception as e: ShowErrorDialog(LogException(e)) def _createReportPageVisualsList(self, reportPageSection=None): try: # print(self._reportVisualData[reportPageSection]) # print('inside report page visual list', reportPageSection) def __updateStateInDataStructure(item): vizProperties = item.GetVisualProperties() if item.checkState() == Qt.Checked: (self._reportVisualData[vizProperties['report_section']] .get('visuals')[vizProperties['visual_index']]['__selected']) = True else: (self._reportVisualData[vizProperties['report_section']] .get('visuals')[vizProperties['visual_index']]['__selected']) = False def __getVisualProperties(item: CQListWidgetItemReportPageVisuals): try: __updateStateInDataStructure(item) self._createSelectedVisualPropertiesTree(item.GetVisualProperties()) except Exception as e: ShowErrorDialog(LogException(e)) def __selectDeselectAll(): try: if self._listWidgetReportPageVisuals is not None: for i in range(self._listWidgetReportPageVisuals.count()): item = self._listWidgetReportPageVisuals.item(i) if self.sender().objectName() == 'button_select_all': item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) __updateStateInDataStructure(item) except Exception as e: ShowErrorDialog(LogException(e)) groupBoxReportPageVisualsList = QGroupBox(self.centralWidget) groupBoxReportPageVisualsList.setTitle("Visuals in Selected Report Page") # groupBoxReportPageVisualsList.setToolTip("All visuals present in the selected page will be visible below") verticalLayoutReportPageVisualsList = QVBoxLayout(groupBoxReportPageVisualsList) horizontalLayoutSelectDeselectButton = QHBoxLayout() pushButtonSelectAll = QPushButton('Select All') pushButtonSelectAll.setObjectName('button_select_all') pushButtonDeselectAll = QPushButton('Deselect All') pushButtonDeselectAll.setObjectName('button_deselect_all') horizontalLayoutSelectDeselectButton.addWidget(pushButtonSelectAll) horizontalLayoutSelectDeselectButton.addWidget(pushButtonDeselectAll) verticalLayoutReportPageVisualsList.addLayout(horizontalLayoutSelectDeselectButton) if self._listWidgetReportPageVisuals is None: self._listWidgetReportPageVisuals = QListWidget() verticalLayoutReportPageVisualsList.addWidget(self._listWidgetReportPageVisuals) self._horizontalLayoutTabVisualsTop.addWidget(groupBoxReportPageVisualsList) self._listWidgetReportPageVisuals.itemClicked.connect(__getVisualProperties, Qt.UniqueConnection) else: self._listWidgetReportPageVisuals.clear() i = 0 for visualIndex, visual in enumerate(self._reportVisualData[reportPageSection].get('visuals')): visualType = visual['visual_type'] visualTitle = visual.get('objects').get('title') # print(visualType, visualTitle) if visualTitle is None or visualTitle.get('text') is None: listDisplayText = str(visualType) else: listDisplayText = str(visualType) + " - " + visualTitle.get('text') pageVisualsList = CQListWidgetItemReportPageVisuals(listDisplayText) pageVisualsList.setFlags(pageVisualsList.flags() | Qt.ItemIsUserCheckable) if visual['__selected'] is False: pageVisualsList.setCheckState(Qt.Unchecked) else: pageVisualsList.setCheckState(Qt.Checked) pageVisualsList.SetVisualProperties(visual, reportPageSection, visualIndex) # print(pageVisualsList.GetVisualProperties()) self._listWidgetReportPageVisuals.insertItem(i, pageVisualsList) i += 1 pushButtonSelectAll.clicked.connect(__selectDeselectAll) pushButtonDeselectAll.clicked.connect(__selectDeselectAll) # self._listWidgetReportPageVisuals.setCurrentRow(0) # __getVisualProperties(self._listWidgetReportPageVisuals.item(0)) except Exception as e: ShowErrorDialog(LogException(e)) def _createSelectedVisualPropertiesTree(self, visualProperties=None): try: def __getVisualProperty(item: CQTreeWidgetItemVisualProperties): try: visualProperties = item.GetVisualProperties() if visualProperties is not None: visualProperty = (self._reportVisualData[visualProperties['report_section']] .get('visuals')[visualProperties['visual_index']].get('objects') .get(str(item.data(0, 0)))) return visualProperty except Exception as e: ShowErrorDialog(LogException(e)) def __updateStateInDataStructure(item: CQTreeWidgetItemVisualProperties): try: visualProperty = __getVisualProperty(item) if visualProperty is not None: if item.checkState(0) == Qt.Unchecked: visualProperty['__selected'] = False else: visualProperty['__selected'] = True except Exception as e: ShowErrorDialog(LogException(e)) def __updateWildCardState(state, item=None): try: visualProperty = __getVisualProperty(item) if visualProperty is not None: if state == Qt.Checked: visualProperty['__wildcard'] = True # print(visualProperty) elif visualProperty.get('__wildcard') is not None and state == Qt.Unchecked: visualProperty.pop('__wildcard') except Exception as e: ShowErrorDialog(LogException(e)) def __treeWidgetVisualsPropertiesClicked(item: CQTreeWidgetItemVisualProperties): try: __updateStateInDataStructure(item) except Exception as e: ShowErrorDialog(LogException(e)) def __selectDeselectAll(): try: if self._treeWidgetSelectedVisualProperties is not None: for i in range(self._treeWidgetSelectedVisualProperties.topLevelItemCount()): item = self._treeWidgetSelectedVisualProperties.topLevelItem(i) if self.sender().objectName() == 'button_select_all': item.setCheckState(0, Qt.Checked) else: item.setCheckState(0, Qt.Unchecked) __updateStateInDataStructure(item) except Exception as e: ShowErrorDialog(LogException(e)) self._groupBoxSelectedVisualPropertiesTree = QGroupBox(self.centralWidget) self._groupBoxSelectedVisualPropertiesTree.setTitle("Selected Visual Properties") verticalLayoutSelectedVisualPropertiesTree = QVBoxLayout(self._groupBoxSelectedVisualPropertiesTree) horizontalLayoutSelectDeselectButton = QHBoxLayout() pushButtonSelectAll = QPushButton('Select All') pushButtonSelectAll.setObjectName('button_select_all') pushButtonDeselectAll = QPushButton('Deselect All') pushButtonDeselectAll.setObjectName('button_deselect_all') horizontalLayoutSelectDeselectButton.addWidget(pushButtonSelectAll) horizontalLayoutSelectDeselectButton.addWidget(pushButtonDeselectAll) verticalLayoutSelectedVisualPropertiesTree.addLayout(horizontalLayoutSelectDeselectButton) if self._treeWidgetSelectedVisualProperties is None: self._treeWidgetSelectedVisualProperties = QTreeWidget() verticalLayoutSelectedVisualPropertiesTree.addWidget(self._treeWidgetSelectedVisualProperties) self._verticalLayoutVisualsPropertiesTab.insertWidget(1, self._groupBoxSelectedVisualPropertiesTree) self._treeWidgetSelectedVisualProperties.itemClicked.connect(__treeWidgetVisualsPropertiesClicked) else: self._treeWidgetSelectedVisualProperties.clear() self._treeWidgetSelectedVisualProperties.setHeaderLabels(['Property', 'Value', 'Wild Card']) treeHeader = self._treeWidgetSelectedVisualProperties.header() treeHeader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) # treeHeader.setStretchLastSection(False) visualObjects = visualProperties['visual_properties'].get('objects') for object, objectValues in visualObjects.items(): if len(objectValues.keys()) > 1: # greater than 1 because one default key is __selected parent = CQTreeWidgetItemVisualProperties(self._treeWidgetSelectedVisualProperties, [str(object)]) parent.SetVisualProperties(visualProperties) wildCardCheckBox = QCheckBox() wildCardCheckBox.stateChanged.connect(partial(__updateWildCardState, item=parent)) self._treeWidgetSelectedVisualProperties.setItemWidget(parent, 2, wildCardCheckBox) if objectValues['__selected'] is False: parent.setCheckState(0, Qt.Unchecked) else: parent.setCheckState(0, Qt.Checked) for objectKey, objectValue in objectValues.items(): if objectKey is not '__selected': mObjectV = None if type(objectValue) is dict and objectValue.get('solid') is not None: mObjectV = objectValue.get('solid').get('color') else: mObjectV = objectValue CQTreeWidgetItemVisualProperties(parent, [str(objectKey), str(mObjectV)]) else: objectValues['__selected'] = False pushButtonSelectAll.clicked.connect(__selectDeselectAll) pushButtonDeselectAll.clicked.connect(__selectDeselectAll) except Exception as e: ShowErrorDialog(LogException(e)) def generateTheme(self): try: correctVisualData = True correctWildcardData = True themeName = self._generalProperties.get('name') themeData = { 'name': themeName if themeName is not None and len(themeName.strip()) > 0 else 'My Theme', } if self._generalProperties.get('dataColors') is not None: dataColors = list(filter(None, self._generalProperties.get('dataColors'))) else: dataColors = [] background = self._generalProperties.get('background') foreground = self._generalProperties.get('foreground') tableAccent = self._generalProperties.get('tableAccent') if dataColors is not None and dataColors is not "": themeData['dataColors'] = dataColors if background is not None and background is not "": themeData['background'] = background if foreground is not None and foreground is not "": themeData['foreground'] = foreground if tableAccent is not None and tableAccent is not "": themeData['tableAccent'] = tableAccent selectedVisualsList = [[], [], []] # page name, visual type and visual data or visual objects wildCardPropertiesList = [[], [], []] # page name, property type and property data visualObjectIndex = 0 themeData['visualStyles'] = {} if self._pbiFilePath is not None: for reportPage in self._reportVisualData: for visual in self._reportVisualData[reportPage].get('visuals'): pageName = self._reportVisualData[reportPage]['reportPageDisplayName'] if visual['__selected'] is True: selectedVisualsList[0].append(pageName) selectedVisualsList[1].append(visual['visual_type']) selectedVisualsList[2].append({}) visualObjects = visual.get('objects') for object in visualObjects: if visualObjects[object]['__selected'] is True: selectedVisualsList[2][visualObjectIndex][object] = [visualObjects[object]] if visualObjects[object].get('__wildcard') is not None: # wildCardObjects[object] = [visualObjects[object]] wildCardPropertiesList[0].append(pageName) wildCardPropertiesList[1].append(object) wildCardPropertiesList[2].append(visualObjects[object]) visualObjectIndex += 1 if len(selectedVisualsList[1]) > 0: if len(selectedVisualsList[1]) != len(set(selectedVisualsList[1])): message = 'Multiple visual of same type selected from different report pages. See details below.' \ ' Please select one visual type for only once.' detailMessage = '' seen = set() repeatedVisuals = [] for selectedVisual in selectedVisualsList[1]: if selectedVisual in seen: repeatedVisuals.append(selectedVisual) else: seen.add(selectedVisual) for i, selectedVisual in enumerate(selectedVisualsList[1]): if selectedVisual in repeatedVisuals: detailMessage += selectedVisual + ' is selected in report page'\ + selectedVisualsList[0][i] + '\n' correctVisualData = False self._showDialogAfterThemeGeneration(message, detailMessage=detailMessage) else: # adding wild card data if len(wildCardPropertiesList[1]) > 0: if len(wildCardPropertiesList[1]) != len(set(wildCardPropertiesList[1])): message = 'Multiple wild card properties of same types are selected. Please only select '\ 'unique properties for wild card from all the visuals. As a result this' \ 'properties will not be added as wild card property in them file. ' \ 'See details below' detailMessage = '' seen = set() repeatedWildCardProperties = [] for wildCardProperty in wildCardPropertiesList[1]: if wildCardProperty in seen: repeatedWildCardProperties.append(wildCardProperty) else: seen.add(wildCardProperty) for i, wildCardProperty in enumerate(wildCardPropertiesList[1]): if wildCardProperty in repeatedWildCardProperties: detailMessage += wildCardProperty + 'is selected in report page' +\ wildCardPropertiesList[0][i] correctWildcardData = False self._showDialogAfterThemeGeneration(message, detailMessage=detailMessage) else: themeData['visualStyles']['*'] = { '*': {} } for i in range(len(wildCardPropertiesList[1])): themeData['visualStyles']['*']['*'][wildCardPropertiesList[1][i]] = [wildCardPropertiesList[2][i]] # adding visual data for i in range(len(selectedVisualsList[1])): themeData['visualStyles'][selectedVisualsList[1][i]] = { "*": selectedVisualsList[2][i] } if correctVisualData is True and correctWildcardData is True: self._saveThemeFile(themeData, 'C:/Users/bjadav/Desktop/', themeData['name']) except Exception as e: ShowErrorDialog(LogException(e)) def _showDialogAfterThemeGeneration(self, message, messageIcon=QMessageBox.Information, detailMessage=None): try: messageBoxThemeGeneration = QMessageBox() messageBoxThemeGeneration.setIcon(messageIcon) messageBoxThemeGeneration.setText(message) if detailMessage is not None: messageBoxThemeGeneration.setInformativeText(detailMessage) messageBoxThemeGeneration.setWindowTitle("Theme Generation Information") messageBoxThemeGeneration.setStandardButtons(QMessageBox.Ok) messageBoxThemeGeneration.exec_() except Exception as e: ShowErrorDialog(LogException(e)) def _saveThemeFile(self, themeData, saveFileDirectory, fileName): try: _themeData = json.loads(json.dumps(themeData)) # Removing internal keys such as __selected from final output for visualType, visualProperties in _themeData['visualStyles'].items(): for propertyType, propertyValue in visualProperties['*'].items(): if propertyValue[0].get('__selected') is not None: propertyValue[0].pop('__selected') if propertyValue[0].get('__wildcard') is not None: propertyValue[0].pop('__wildcard') initialSaveFilePath = saveFileDirectory + fileName + '.json' saveFile = QFileDialog.getSaveFileName(self, 'Save Theme File', initialSaveFilePath, filter='JSON file(*.json)')[0] if saveFile is not '': with open(saveFile, 'w') as themeFile: json.dump(_themeData, themeFile, indent=4) message = 'Successfully generated theme' self._showDialogAfterThemeGeneration(message) except Exception as e: ShowErrorDialog(LogException(e)) def _showAboutDialog(self): try: dialogAbout = QDialog() dialogAbout.resize(320, 180) dialogButtonBoxAbout = QDialogButtonBox(dialogAbout) dialogButtonBoxAbout.setLayoutDirection(Qt.LeftToRight) dialogButtonBoxAbout.setAutoFillBackground(False) dialogButtonBoxAbout.setOrientation(Qt.Horizontal) dialogButtonBoxAbout.setStandardButtons(QtWidgets.QDialogButtonBox.Ok) dialogButtonBoxAbout.setCenterButtons(True) dialogButtonBoxAbout.accepted.connect(dialogAbout.accept) verticalLayoutAboutDialog = QVBoxLayout(dialogAbout) dialogAbout.setWindowTitle('About ' + AppInfo.Name) labelPowerBIThemeGeneratorFont = QFont() labelPowerBIThemeGeneratorFont.setPointSize(16) labelPowerBIThemeGeneratorFont.setFamily('Calibri') labelPowerBIThemeGenerator = QLabel(AppInfo.Name) labelPowerBIThemeGenerator.setFont(labelPowerBIThemeGeneratorFont) labelVersionFont = QFont() labelVersionFont.setPointSize(8) labelVersion = QLabel(AppInfo.Version) labelVersion.setContentsMargins(0, 0, 0, 0) labelCreatedBy = QLabel('Created By ' + AppInfo.Author) link = '<a href="' + AppInfo.GitHubRepoIssuesURL + '">Click here to report bugs</a>' labelBugs = QLabel(link) labelBugs.setOpenExternalLinks(True) verticalLayoutAboutDialog.addWidget(labelPowerBIThemeGenerator) verticalLayoutAboutDialog.addWidget(labelVersion) verticalLayoutAboutDialog.addWidget(labelCreatedBy) verticalLayoutAboutDialog.addWidget(labelBugs) verticalLayoutAboutDialog.addStretch() verticalLayoutAboutDialog.addWidget(dialogButtonBoxAbout) dialogAbout.exec_() except Exception as e: ShowErrorDialog(LogException(e))
class PathEditor(ViewInfoChanger): def __init__(self, header, info, parent: ViewShower): super().__init__(header, info, parent) self.way_layout = QVBoxLayout() push_btn = self.main_layout.takeAt(self.main_layout.count() - 1).widget() self.path_id = info[0] add_btn = QPushButton("Добавить") add_btn.clicked.connect(lambda e: self.add_cell(-1)) self.main_layout.addWidget(add_btn) way = self.db.execute( f"SELECT Станция, `Код станции` FROM `состав_маршрута_view` where `Номер маршрута` = {self.path_id}" ) for pos, point in enumerate(way, start=-1): station_info = str(point[0]) self.combo_change_idx["Станция отправления"][station_info] = point[ 1] self.add_cell(pos, station_info) self.main_layout.addLayout(self.way_layout) self.main_layout.addWidget(push_btn) def add_cell(self, pos: int, txt=""): """Вставляет ячейку ниже активирующей кнопки для вставки на уровне надо передать ::pos:: = -1""" cell = QHBoxLayout() edi = QLineEdit() edi.setText(txt) add_btn = QPushButton("Добавить") down_btn = QPushButton("Вниз") up_btn = QPushButton("Вверх") del_btn = QPushButton("Удалить") cmb = QComboBox() cmb.addItem(txt) edi.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) cmb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) up_btn.clicked.connect(lambda e, p=pos, c=cell: self.move_cell_up(c)) down_btn.clicked.connect( lambda e, p=pos, c=cell: self.move_cell_down(c)) add_btn.clicked.connect(lambda e, c=cell: self.add_cell(c.pos)) del_btn.clicked.connect(lambda e, c=cell: self.del_cell(c.pos)) edi.editingFinished.connect( lambda c=cmb, t=edi.text: self.combo_update( "Станция отправления", c, t())) # le-kostyl cell.pos = pos cell.addWidget(edi) cell.addWidget(cmb) cell.addWidget(add_btn) cell.addWidget(down_btn) cell.addWidget(up_btn) cell.addWidget(del_btn) for i in range(pos + 1, self.way_layout.count()): cell_to_move = self.way_layout.itemAt(i) cell_to_move.pos += 1 cell.pos += 1 # для вставки ниже активированной кнопки self.way_layout.insertLayout(cell.pos, cell) def del_cell(self, pos): cell: QVBoxLayout cell = self.way_layout.takeAt(pos) for i in range(cell.count()): w = cell.takeAt(0).widget() w.deleteLater() cell.deleteLater() for i in range(pos, self.way_layout.count()): cell_to_move = self.way_layout.itemAt(i) cell_to_move.pos -= 1 def move_cell_up(self, cell): if cell.pos > 0: old_cell = self.way_layout.itemAt(cell.pos - 1) self.way_layout.takeAt(cell.pos) self.way_layout.insertLayout(cell.pos - 1, cell) old_cell.pos += 1 cell.pos -= 1 def move_cell_down(self, cell): if cell.pos < self.way_layout.count() - 1: old_cell = self.way_layout.itemAt(cell.pos + 1) self.way_layout.takeAt(cell.pos) self.way_layout.insertLayout(cell.pos + 1, cell) old_cell.pos -= 1 cell.pos += 1 def push_point_changes(self): params = [] station_ord_numb_in_path = 0 for i in range(self.way_layout.count()): cell = self.way_layout.itemAt(i) w = cell.itemAt(1).widget() station = w.currentText() if station: station_id = self.combo_change_idx["Станция отправления"][ station] params.append( (self.path_id, station_ord_numb_in_path, station_id)) station_ord_numb_in_path += 1 query = f" insert into `состав маршрута` values(%s, %s, %s)" self.db.execute( "delete from `состав маршрута` where `Номер маршрута` = %s", (self.path_id, )) self.db.executemany(query, params) self.db.commit() def push_changes(self): self.push_point_changes() super().push_changes()
class DropSiteWindow(QWidget): def __init__(self, pageNumber, driver): super().__init__() """ QUICK START IN OPTIONS HANDLE EXCEPTIONS WERES ARE BLOCKED OR NETWORK DOESNT WORK ETC -> WHEN GOING BACK ONLINE LOAD IMAGE REOPNING TAB DOESNT SAVE PAGE STATE REOPNING TAB OPENS IN DIFFERENT BROWSER BETTER WAY TO CHECK WHETHER ITS A STANDLONE EXEC OR LOCALLY RUN HAVE A LOOK AT QSETTINGS TO REMEMBER WINDOW SIZES AND SESSION STATE ETC.. FUTURE FEATURES: - MORE FREEDOM WITH REARRANGING TABS - SUPPORT OTHER DATA TYPES E.G. PDF FILE ETC.. - FAVOURITE TABS -> MOVES THEM HIGHER - ANIMATE CLOSE BUTTON FADE IN/OUT - IMPLEMENT THEMES (ONLY DARK MODE RIGHT NOW) """ # SETUP THUMBNAIL FOLDER if not (os.path.isdir(tabsettings.absImageFolderPath())): os.mkdir(tabsettings.absImageFolderPath()) self.clearingTabs = False self.acceptDrops = True self.options = tabsettings.TabSettings() self.pageNumber = pageNumber self.pageName = 'Page ' + str(self.pageNumber) self.driver = driver self.setPageName() # SETUP LAYOUTS self.mainLayout = QVBoxLayout() self.scroll = QScrollArea() self.scrollWidget = QWidget() self.scrollWidgetLayout = QVBoxLayout() self.scroll.setWidgetResizable(True) self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.scroll.setFrameStyle(QFrame.NoFrame) self.scrollWidgetLayout.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.scrollWidgetLayout.setContentsMargins(3, 3, 3, 3) self.scrollWidgetLayout.setSpacing(3) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.scrollWidget.setLayout(self.scrollWidgetLayout) self.scroll.setWidget(self.scrollWidget) self.mainLayout.addWidget(self.scroll) self.setLayout(self.mainLayout) # IF TAB FILE EXISTS REMAKE TABS self.tabFilePath = os.path.join(os.getcwd(), tabsettings.tabFileName(self)) self.tabFileName = tabsettings.tabFileName(self) self.archiveTabFileName = tabsettings.archiveTabFileName(self) exists = os.path.isfile(self.tabFilePath) size = 0 if exists: size = os.path.getsize(self.tabFilePath) if size > 0 and exists: self.loadTabs(self.tabFilePath) else: self.newTab() self.updatePageNames() self.checkArchiveTabFileSize() # LOAD TABS FROM CURRENT PAGE TAB FILE -> CREATE NEW TAB -> LOAD TAB WITH CONTENT OF TAB FILE ENTRY def loadTabs(self, path): with open(path, 'r') as f: data = f.readlines() for index, line in enumerate(data): l = line.split('\n')[0] currentItems = l.split(' ') url = currentItems[0] imagePath = currentItems[1] tabNumber = currentItems[2] tab = self.newTab() self.loadTabData(tab, url, imagePath) # IF ITS LAST TAB ADD EMPTY TAB AFTER if index == len(data) - 1: self.newTab() # CREATE NEW TAB -> IF TABCOUNT > ALLOWED -> CREATE NEW ROW THEN ADD TAB def newTab(self): if (self.options.tabCount % self.options.tabsPerRow) == 0: self.currentRow = QHBoxLayout() self.currentRow.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.currentRow.setSpacing(3) self.currentRow.setSizeConstraint(QLayout.SetMinimumSize) self.scrollWidgetLayout.insertLayout( self.scrollWidgetLayout.layout().count(), self.currentRow) self.options.tabRows.append(self.currentRow) self.options.tabCount += 1 drp = DropArea(self.options, self.options.tabCount, self.driver, tabsettings.MIN_TAB_WIDTH, tabsettings.MIN_TAB_HEIGHT, tabsettings.ASPECT_RATIO, tabsettings.absImageFolderPath(), self.tabFileName, self.archiveTabFileName) drp.imageLoaded.connect(self.newTab) drp.tabDeleted.connect(lambda: self.deleteTab(drp)) drp.tabAdded.connect(self.addTabToList) drp.tabReordered.connect(self.reorderTab) drp.destroyed.connect(self.reorganizeTabFile) self.options.emptyTab = drp self.currentRow.addWidget(drp) return drp # SHORTCUT METHOD TO LOAD TAB def loadTabData(self, tab, url, imagePath): tab.load(url, imagePath) # DELETE TAB -> SET PARENT NONE -> REMOVE FROM TAB LIST -> DELETE ENTRY IN TAB FILE -> ARCHIVE IF NOT DUPLICATE def deleteTab(self, tab): if tab.taken: self.options.tabCount -= 1 self.options.loadedTabCount -= 1 self.options.tabList.remove(tab) tab.setParent(None) tab.deleteLater() # REMOVE ENTRY FROM SAVED TABS FILE fileData = open(self.tabFileName, 'r').readlines() with open(self.tabFileName, 'w') as out: # FILE LAYOUT EACH LINE -> (URL IMAGE_PATH TAB_NUMBER) for line in fileData: currentItems = line.split(' ') tabNum = int(currentItems[2]) # IF CURRENT LINE IS NOT TAB TO BE DELETED WRITE LINE if not (tabNum == tab.tabNumber): out.write(line) # CHECK IF THERE IS DUPLICATE TAB IF SO DON'T ARCHIVE dontArchiveImage = tab.checkDuplicateTab(tab.url, self.tabFileName) if not dontArchiveImage: self.archiveTab(tab.url, tab.imagePath) # ARCHIVE TAB IN ARCHIVE TAB FILE NAME OF CURRENT PAGE -> RENAME IMAGE TO .IMAGE IF ITS AVAILABLE def archiveTab(self, url, imagePath): try: os.rename( os.path.normpath( os.path.join(tabsettings.IMAGE_FOLDER_PATH, imagePath)), os.path.normpath( os.path.join(tabsettings.IMAGE_FOLDER_PATH, '.' + imagePath))) except FileNotFoundError: pass with open(self.archiveTabFileName, 'a+') as f: f.seek(0) lines = f.readlines() duplicate = False for line in lines: if line.split(' ')[0] == url: duplicate = True if not duplicate: f.write(url + ' ' + '.' + imagePath + '\n') # ADD TAB TO TABLIST -> IF NUMBER GREATER THEN LIST SIZE -> APPEND -> ELSE REPLACE TAB IN LIST def addTabToList(self, tab, tabNum): if len(self.options.tabList) <= tabNum: self.options.tabList.append(tab) else: self.options.tabList[tabNum] = tab # REORDER TAB IN TABLIST AND MOVE WIDGET -> REORGANIZE TAB FILE TO SHOW CHANGES MADE def reorderTab(self, tab, swapValue): # GET INDEX OF TAB IN TAB LIST index = self.options.tabList.index(tab) # SWAP VALUE = SHIFT IN TABS E.G. -1 FOR ONE LEFT newIndex = index + swapValue self.options.tabList.remove(tab) self.options.tabList.insert(newIndex, tab) self.reorganizeTabFile() # REORGANIZE TAB FILE -> FROM DELETED TAB (TAB NUMBERS WRONG) -> OR FROM REORGANIZING TAB def reorganizeTabFile(self): if os.path.isfile( self.tabFilePath) and os.path.getsize(self.tabFilePath) > 0: if not self.clearingTabs: for index, tab in enumerate(self.options.tabList): tab.tabNumber = index + 1 self.options.emptyTab.tabNumber = len(self.options.tabList) + 1 # REORDER TABS IN THE SAVED TABS FILE SO THEY HAVE CORRECT TAB NUMBER with open(self.tabFileName, 'r+') as f: lines = f.readlines() currentTab = 1 finalStringData = '' for line in lines: currentItems = line.split('\n')[0].split(' ') # REORGANIZING AFTER DELETING A TAB if (currentTab != int(currentItems[2])): finalStringData += currentItems[ 0] + ' ' + self.options.tabList[ currentTab - 1].imagePath + ' ' + str(currentTab) + '\n' # REORGANIZING AFTER REORDERING A TAB elif (currentItems[0] != self.options.tabList[currentTab - 1].url): current = self.options.tabList[currentTab - 1] finalStringData += current.url + ' ' + current.imagePath + ' ' + str( current.tabNumber) + '\n' else: finalStringData += line currentTab += 1 # SEEK BEGINNING -> CLEAR FILE -> WRITE DATA (TRUNCATE SETS FILE SIZE TO MAX (0)) f.seek(0) f.truncate(0) f.write(finalStringData) self.checkLayouts() # CHECK IF ANY LAYOUTS HAVE < 3 CHILDREN APART FROM LAST -> MOVE WIDGETS TO PREVIOUS LAYOUT/ROW ::: IF CURRENT ROW EMPTY -> SET TO PREVIOUS ROW def checkLayouts(self): # IF ANY LAYOUTS HAVE < 3 CHILDREN REORGANIZE THE WIDGETS complete = False while not complete: for i in range(0, len(self.options.tabRows) - 1): if self.options.tabRows[i].count() < 3: child = self.options.tabRows[i + 1].itemAt(0) self.options.tabRows[i + 1].removeItem(child) self.options.tabRows[i].addItem(child) complete = True # IF CURRENT ROW HAS NO CHILDREN REMOVE IT AND SET IT TO PREVIOUS ROW if self.currentRow.count() == 0: self.options.tabRows.remove(self.currentRow) self.currentRow.deleteLater() self.currentRow = self.options.tabRows[len(self.options.tabRows) - 1] # CLEAR ALL TABS IN CURRENT PAGE -> EMPTY TABLIST AND TABROWS -> ARCHIVE ALL DELETED TABS def clear(self): self.clearingTabs = True # REMOVE ENTRY FROM SAVED TABS FILE fileData = [] with open(self.tabFileName, 'r') as inp: fileData += inp.readlines() for line in fileData: currentItems = line.split(' ') self.archiveTab(currentItems[0], currentItems[1]) for i in range(len(self.options.tabList) - 1, -1, -1): self.options.tabList[i].setParent(None) self.options.tabList[i].deleteLater() self.options.tabList.remove(self.options.tabList[i]) os.remove(self.tabFilePath) self.options.tabCount = 0 # REMOVE ALL ROWS FROM LIST EXCEPT FIRST AND CLEAR TABS for x in range(len(self.options.tabRows) - 1, -1, -1): if self.options.tabRows[x].count() > 0: self.options.tabRows[x].itemAt(0).widget().deleteLater() if x == 0: self.options.tabRows[x].destroyed.connect(self.newTab) self.options.tabRows[x].deleteLater() self.options.tabRows.remove(self.options.tabRows[x]) self.clearingTabs = False # CLEARS ALL TABS BUT DOES NOT REMOVE THEM OR ARCHIVE THEM AND RESETS VARIABLES def cleanClear(self): for i in reversed(range(len(self.options.tabRows))): for j in reversed(range(self.options.tabRows[i].count())): self.options.tabRows[i].itemAt(j).widget().setParent(None) self.options.tabRows.clear() self.options.tabList.clear() self.options.emptyTab = None self.options.tabCount = 0 self.options.loadedTabCount = 0 # RUN AFTER CLEAN CLEAR JUST RELOADS ALL TABS INTO WINDOW def cleanLoad(self): with open(self.tabFileName, 'r') as f: for line in f: current = line.split('\n')[0].split(' ') url = current[0] imagePath = current[1] tabNumber = current[2] drp = self.newTab() drp.load(url, imagePath) self.newTab() # CHANGE TABS PER ROW IN CURRENT PAGE OPTIONS -> CHANGE MIN WIDTH/HEIGHT -> RELOAD ALL TABS def changeTabsPerRow(self, number): self.clearingTabs = True tabsettings.MIN_TAB_WIDTH *= (self.options.tabsPerRow / number) tabsettings.MIN_TAB_HEIGHT *= (self.options.tabsPerRow / number) self.options.tabsPerRow = number self.cleanClear() self.cleanLoad() self.clearingTabs = False # SET PAGE NAME FROM ENTRY IN PAGENAMES.TXT BY PAGENUMBER def setPageName(self): if os.path.exists(os.path.join(os.getcwd(), 'pagenames.txt')): with open('pagenames.txt', 'r+') as f: for index, line in enumerate(f.readlines()): if index + 1 == self.pageNumber: self.pageName = line.split('\n')[0] # UPDATE PAGENAMES.TXT -> IF DOESN'T EXIST -> WRITE PAGENAME -> ELSE REWRITE ENTRY TO PAGENAME -> OR IF NEW PAGE WRITE NEW LINE def updatePageNames(self): if not os.path.exists(os.path.join(os.getcwd(), 'pagenames.txt')): with open('pagenames.txt', 'a+') as f: f.write(self.pageName + '\n') else: data = open('pagenames.txt', 'r').readlines() # IF NUMBER OF LINES >= NUMBER THEN NAME WILL BE IN FILE if len(data) >= self.pageNumber: with open('pagenames.txt', 'w') as f: for index, line in enumerate(data): if index + 1 == self.pageNumber: f.write(self.pageName + '\n') else: f.write(line) # NAME IS NOT IN FILE SO MUST APPEND IT TO FILE else: with open('pagenames.txt', 'a') as f: f.write(self.pageName + '\n') # SET PAGENAME AND UPDATE PAGENAMES.TXT WITH NEW NAME def rename(self, string): self.pageName = string self.updatePageNames() # CHECK IF ARCHIVE TAB FILE SIZE IS GREATER THEN VALUE -> REWRITE OUT FIRST LINES UNTIL ITS DESIRED SIZE def checkArchiveTabFileSize(self): if os.path.isfile(os.path.join(os.getcwd(), self.archiveTabFileName)): data = open(self.archiveTabFileName, 'r').readlines() if len(data) > tabsettings.ARCHIVE_TAB_FILE_MAX_SIZE: with open(self.archiveTabFileName, 'w') as f: for index, line in enumerate(data): if index != 0 and index <= tabsettings.ARCHIVE_TAB_FILE_MAX_SIZE: f.write(line) def close(self): self.driver.close() self.driver.quit() print('Exited') sys.exit(0)
class DualEntry(QDialog): """ Just a simple Dialog for retrieving 2 values """ def __init__(self, entry1, entry2, input_type, text, min, max, parent=None): """ Note: Use the static method """ super().__init__(parent=parent) self.initUI(entry1, entry2, input_type, text, min, max) def initUI(self, entry1, entry2, input_type, text, min, max): self.setObjectName('Dialog') self.setStyleSheet("font: 10pt \"Arial\";") self.setWindowTitle('Input dialog') # Setting the parent makes it the default layout self.vlayout = QVBoxLayout(self) self.vlayout.setObjectName('vlayout') self.main_text = QLabel(self) self.main_text.setText(text) self.vlayout.insertWidget(0, self.main_text) self.hlayout = QHBoxLayout() self.hlayout.setObjectName('hlayout') self.vlayout.insertLayout(1, self.hlayout) if input_type is float: self.entry1 = QDoubleSpinBox(self) self.entry2 = QDoubleSpinBox(self) elif input_type is int: self.entry1 = QSpinBox(self) self.entry2 = QSpinBox(self) self.entry1.setMaximum(max) self.entry1.setMinimum(min) self.entry2.setMaximum(max) self.entry2.setMinimum(min) self.entry1.setValue(entry1) self.entry2.setValue(entry2) self.hlayout.insertWidget(0, self.entry1) self.hlayout.insertWidget(1, self.entry2) self.bb = QDialogButtonBox(self) self.bb.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.vlayout.insertWidget(-1, self.bb) self.show() @staticmethod def getDualEntries(entry1, entry2, input_type=float, text='Enter Values:', min=-10000000000, max=10000000000, parent=None): """ Pops up a dialog box that retrieves 2 values, either float or int Parameters ---------- entry1: int or float Default value in the left box entry2: int or float Default value in the right box input_type : type Either float or int are currently supported text : str Text describing the values to set min : int or float Minimum value for entries max : int or float Maximum value for entries parent : object or None Parent of the Dialog (QDialog) box. Returns ------- (numeric, numeric), int Returns (left value, right value), Ok pressed. If Ok was pressed, value == 1, else 0. """ dialog = DualEntry(entry1, entry2, input_type, text, min, max, parent) result = dialog.exec_() if result: return (dialog.entry1.value(), dialog.entry2.value()), result else: return (), result
class DeviceWidget(QWidget): def __init__(self, device): super().__init__() self._device = device self._layout = QVBoxLayout() self.setLayout(self._layout) self._gb = QGroupBox(self._device.label) self._layout.addWidget(self._gb) # label to display this device's polling rate self._polling_rate_label = QLabel('Polling rate: --') self._layout.addWidget(self._polling_rate_label) self._layout.addStretch() self._gblayout = QVBoxLayout() self._gb.setLayout(self._gblayout) self._hasMessage = False self._retry_time = self._device._retry_time self._retry_thread = None def show_error_message(self, message=''): if not self._hasMessage: self._messageframe = QFrame() vbox = QVBoxLayout() vbox.setContentsMargins(0, 0, 0, 0) self._messageframe.setLayout(vbox) self._txtmessage = QLabel() self._txtmessage.setWordWrap(True) vbox.addWidget(self._txtmessage) self._txtretry = QLabel('Retrying ...') #in {} seconds...'.format(self._retry_time)) vbox.addWidget(self._txtretry) self._gb.setEnabled(False) self._layout.insertWidget(0, self._messageframe) self._hbox = QHBoxLayout() self._btnRetry = QPushButton('Force Retry') self._polling_rate_label.setText('Polling rate: --') self._btnRetry.clicked.connect(self.force_retry_connect) self._hbox.addStretch() self._hbox.addWidget(self._btnRetry) self._hbox.addStretch() self._layout.insertLayout(3, self._hbox) self._hasMessage = True self._txtmessage.setText('<font color="red">{}</font>'.format(message)) if self._retry_thread is not None: # probably ok since the thread isn't doing anything critical del self._retry_thread #self._retry_thread = threading.Thread(target=self.test_update_retry_label, args=()) #self._retry_thread.start() def force_retry_connect(self): self._device.unlock() ''' def test_update_retry_label(self): for i in range(self._retry_time): self._txtretry.setText('Retrying in {} seconds...'.format(self._retry_time - i)) time.sleep(1) self._device.unlock() self._txtretry.setText('Retrying') ''' def set_retry_label(self, time): self._txtretry.setText('Retrying in {} seconds...'.format(str(time))) def hide_error_message(self): if self._hasMessage: wdg = self._layout.itemAt(0).widget() wdg.deleteLater() self._gb.setEnabled(True) self._hasMessage = False self._btnRetry.deleteLater() self._hbox.setParent(None) @property def gblayout(self): return self._gblayout
class MyWindow(QWidget): """main window""" def __init__(self): super().__init__() self.auths = {} self.activeaccounts = [] self.streams = [] self.tweets = [] self.tagarray = [] self.tweettags = [] self.receivetags = [] self.following = [] self.searchstream = None self.init_main() self.init_accounts() self.init_tweets() self.init_widgets() self.show() sys.exit(app.exec_()) def init_main(self): """options of main window""" self.setGeometry(300, 100, 1000, 600) self.setWindowTitle("tomoebi") self.timer = QTimer() self.timer.timeout.connect(self.update_timeline) self.timer.start(500) def init_tweets(self): """create initial tweet""" self.tweets = ["start"] #initialize widgets def init_widgets(self): """initialize widgets""" #upper half of main window consists of accounts, composer and buttons self.compose_vbox = QVBoxLayout() self.accounts_hbox = QHBoxLayout() self.accbuttons = [] for a in self.auths: accbutton = QPushButton(self) accbutton.setWhatsThis(a) accbutton.setCheckable(True) accbutton.toggled.connect(self.choose_account) accbutton.setChecked(True) accbutton.setIcon(PyQt5.QtGui.QIcon('images/' + a + '.jpg')) accbutton.setIconSize(QSize(48, 48)) self.accounts_hbox.addWidget(accbutton) self.addaccbutton = QPushButton("+", self) self.addaccbutton.clicked.connect(self.add_account) self.accounts_hbox.addWidget(self.addaccbutton) self.composer = ComposeTextEdit(self) #self.composer.setPlaceholderText("いまなにしてる?") self.composer.setMaximumHeight(60) self.compose_hbox = QHBoxLayout() self.imagebutton = QPushButton("image", self) self.submitbutton = QPushButton("tweet", self) self.submitbutton.clicked.connect(self.submit) self.hashtag_hbox = QHBoxLayout() self.hashtagedit = QLineEdit(self) self.hashtagedit.setPlaceholderText("hashtags") self.hashtagbutton = QPushButton("set") self.hashtagbutton.setCheckable(True) self.hashtagbutton.toggled.connect(self.sethashtag) self.hashtag_hbox.addWidget(self.hashtagedit) self.hashtag_hbox.addWidget(self.hashtagbutton) self.compose_hbox.addWidget(self.imagebutton) self.compose_hbox.addWidget(self.submitbutton) self.compose_vbox.addLayout(self.accounts_hbox) self.compose_vbox.addWidget(self.composer) self.compose_vbox.addLayout(self.compose_hbox) self.compose_vbox.addLayout(self.hashtag_hbox) #lower half of main window consists of timeline l = QTextEdit() l.setPlainText(self.tweets[0]) l.setReadOnly(True) l.setFixedHeight(350) self.inner = QWidget() self.timeline_vbox = QVBoxLayout(self.inner) self.timeline_vbox.addWidget(l) self.tweets = [] self.timeline_vbox.addStretch() self.scroll = QScrollArea() self.scroll.setWidgetResizable(True) self.scroll.setWidget(self.inner) #integrate upper and lower part of main window self.whole_vbox = QVBoxLayout() self.whole_vbox.addLayout(self.compose_vbox) self.whole_vbox.addWidget(self.scroll) #right half of the main window self.image_collumn = QVBoxLayout() self.imageinner = QWidget() self.imagetimeline = QVBoxLayout(self.imageinner) self.imagescroll = QScrollArea() self.imagescroll.setWidgetResizable(True) self.imagescroll.setWidget(self.imageinner) self.imagetext = QTextEdit() self.imagetext.setMaximumHeight(60) self.imagetext.setReadOnly(True) self.image_collumn.addWidget(self.imagetext) self.image_collumn.addWidget(self.imagescroll) self.whole_hbox = QHBoxLayout() self.whole_hbox.addLayout(self.whole_vbox) self.whole_hbox.addLayout(self.image_collumn) self.setLayout(self.whole_hbox) #initialize registered accounts def init_accounts(self): """load account AT and AS from local and create api object and stream""" if not os.path.exists("images"): os.mkdir("images") if os.path.isfile("auth.json"): with open('auth.json', 'r') as f: authdic = json.load(f) for name, keys in authdic["Twitter"].items(): api = twitter.connect(keys["ACCESS_TOKEN"], keys["ACCESS_SECRET"]) self.auths[name] = api #self.following = self.following + api.friends_ids(k) self.streams.append( twitter.open_userstream(api, self.receive_tweet, name)) if not os.path.isfile("images/" + name + ".jpg"): twitter.getmyicon(api, name) #twitter.open_filterstream(self.auths["XXXX"], self.receive_tweet, # "XXXX", [str(x) for x in self.following]) else: default = {"Twitter": {}, "Mastodon": {}} with open('auth.json', 'w') as f: json.dump(default, f, indent=2) self.authdic = {} def add_account(self): """add account and register it to local file""" api, screen_name = twitter.authentication() self.auths[screen_name] = api self.streams.append( twitter.open_userstream(api, self.receive_tweet, screen_name)) twitter.getmyicon(api, screen_name) accbutton = QPushButton(self) accbutton.setWhatsThis(screen_name) accbutton.setCheckable(True) accbutton.toggled.connect(self.choose_account) accbutton.setIcon(PyQt5.QtGui.QIcon('images/' + screen_name + '.jpg')) accbutton.setIconSize(QSize(48, 48)) self.accounts_hbox.insertWidget(self.accounts_hbox.count() - 1, accbutton) def choose_account(self): """ called when accbutton are toggled. add or remove active accounts """ acc = self.sender() if acc.isChecked(): self.activeaccounts.append(acc.whatsThis()) else: self.activeaccounts.remove(acc.whatsThis()) def receive_tweet(self, status, name, icon): """called when stream receive a tweet""" self.tweets.append((status, name, icon)) def update_timeline(self): """called every 500ms and update gui timeline according to self.tweets""" for t in self.tweets: if hasattr(t[0], "in_reply_to_status_id"): self.addTweet(*t) elif hasattr(t[0], "event"): self.addEvent(*t) self.tweets = [] def addTweet(self, t, name, icon): rtby = None if hasattr(t, "retweeted_status"): rtby = [t.user.profile_image_url_https, t.user.screen_name] t = t.retweeted_status tweet = self.create_tweet(t) tweet_hbox = QHBoxLayout() if icon: if not glob.glob("images/" + t.user.screen_name + ".*"): twitter.geticon(t.user.profile_image_url_https, t.user.screen_name) icon = PyQt5.QtGui.QPixmap( glob.glob("images/" + t.user.screen_name + ".*")[0]) scaled_icon = icon.scaled(QSize(48, 48), 1, 1) iconviewer = IconLabel(t.id, name, self.retweet, self.reply) iconviewer.setPixmap(scaled_icon) icon_vbox = QVBoxLayout() icon_vbox.addWidget(iconviewer, alignment=Qt.AlignTop) if rtby: if not glob.glob("images/" + rtby[1] + ".*"): twitter.geticon(*rtby) icon = PyQt5.QtGui.QPixmap( glob.glob("images/" + rtby[1] + ".*")[0]) scaled_icon = icon.scaled(QSize(24, 24), 1, 1) rticonviewer = QLabel() rticonviewer.setPixmap(scaled_icon) rticon_hbox = QHBoxLayout() #rticon_hbox.addStretch() rticon_hbox.addWidget(rticonviewer, alignment=(Qt.AlignRight | Qt.AlignTop)) icon_vbox.addLayout(rticon_hbox) icon_vbox.addStretch() tweet_hbox.addLayout(icon_vbox) tweet_hbox.addWidget(tweet) favbutton = QPushButton("fav") favbutton.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) favbutton.setCheckable(True) favbutton.toggled.connect(lambda: self.fav(t.id, name)) tweet_hbox.addWidget(favbutton) self.timeline_vbox.insertLayout(0, tweet_hbox) if "media" in t.entities: images = twitter.get_allimages(self.auths[name], t.id) self.imagetext.setPlainText("@" + t.user.screen_name + "\n" + t.text) for n, _ in enumerate(images): pixmap = PyQt5.QtGui.QPixmap() pixmap.loadFromData(QByteArray(images[n])) scaled = pixmap.scaled(QSize(320, 180), 1, 1) imageviewer = QLabel() imageviewer.setPixmap(scaled) self.imagetimeline.insertWidget(0, imageviewer) def addEvent(self, t, name, icon): if t.event == "favorite" and not (t.source["screen_name"] in self.auths.keys()): text = t.source["screen_name"] + " favored " + t.target_object[ "text"] favLabel = QLabel(text) self.timeline_vbox.insertWidget(0, favLabel) def create_tweet(self, t): """create tweet widget""" text = "@" + t.user.screen_name + "\n" + t.text tweetdocument = PyQt5.QtGui.QTextDocument() tweetdocument.setTextWidth( 300) #this line is not working so it needs to be fixed someday tweetdocument.setPlainText(text) tweettext = QTextEdit() tweettext.setDocument(tweetdocument) tweettext.setReadOnly(True) tweettext.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) tweettext.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) tweettext.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) tweettext.setAttribute(103) tweettext.show() tweettext.setFixedHeight(tweettext.document().size().height() + tweettext.contentsMargins().top() * 2) return tweettext def submit(self): """called when tweet button is pressed and submit tweet""" if not self.activeaccounts: return submittext = self.composer.toPlainText() for t in self.tweettags: submittext = submittext + " " + t if not submittext: return for a in self.activeaccounts: self.auths[a].update_status(submittext) self.composer.setPlainText("") def fav(self, tweetid, name): '''favor or unfavor a tweet from an account from which the tweet was obtained''' switch = self.sender() if switch.isChecked(): try: self.auths[name].create_favorite(tweetid) except: print("already favored") else: try: self.auths[name].destroy_favorite(tweetid) except: print("not favored") def retweet(self, id, account): self.auths[account].retweet(id) def reply(self, id, account): if not self.activeaccounts: return submittext = self.composer.toPlainText() if not submittext: return self.auths[account].update_status(submittext, in_reply_to_status_id=id, auto_populate_reply_metadata=True) self.composer.setPlainText("") def sethashtag(self): """set hashtab for receive and tweet""" switch = self.sender() if switch.isChecked(): htinput = self.hashtagedit.text() htlist = htinput.strip().split() for t in htlist: if not t[0] == "*": self.receivetags.append(t) self.tweettags.append(t) else: self.receivetags.append(t[1:]) repl_screen_name = list(self.auths.keys())[0] self.searchstream = twitter.open_filterstream( self.auths[repl_screen_name], self.receive_tweet, repl_screen_name, self.receivetags) else: self.receivetags = [] self.tweettags = [] self.searchstream.disconnect() def closeEvent(self, event): """called when gui window is closed and terminate all streams and thread""" for s in self.streams: s.disconnect() os._exit(1)
def initUI(self): self.card_amount_label = QLabel("With how many cards will you play: ") self.player_one_label = QLabel("What's the first players name: ") self.player_two_label = QLabel("What's the second players name: ") self.statistics_label = QLabel("Some game statistics:") self.games_started_label = QLabel( "The amount of games started: " + str(get_game_statistic("started_games"))) self.games_finished_label = QLabel( "The amount of games finished: " + str(get_game_statistic("finished_games"))) self.go_back_to_setup_button = QPushButton("Go to Setup") self.card_amount_options = QComboBox() self.card_amount_options.addItems(["4", "8", "10", "16", "36"]) self.card_amount_options.setCurrentText( get_game_setting("playfield_size")) self.card_amount_options.currentIndexChanged.connect( self.card_selection_change) self.player_one_name_line_edit = QLineEdit() self.player_one_name_line_edit.setText( get_game_setting("player_one_name")) self.player_one_name_line_edit.setMaxLength(30) self.player_one_name_line_edit.setAlignment(Qt.AlignLeft) self.player_one_name_line_edit.textChanged.connect( self.player_one_name_changed) self.player_two_name_line_edit = QLineEdit() self.player_two_name_line_edit.setText( get_game_setting("player_two_name")) self.player_two_name_line_edit.setMaxLength(30) self.player_two_name_line_edit.setAlignment(Qt.AlignLeft) self.player_two_name_line_edit.textChanged.connect( self.player_two_name_changed) horizontal_go_back_to_setup_gui_box = QHBoxLayout() horizontal_go_back_to_setup_gui_box.addWidget( self.go_back_to_setup_button) horizontal_go_back_to_setup_gui_box.addStretch() horizontal_player_one_name_box = QHBoxLayout() horizontal_player_one_name_box.addWidget(self.player_one_label) horizontal_player_one_name_box.addWidget( self.player_one_name_line_edit) horizontal_player_two_name_box = QHBoxLayout() horizontal_player_two_name_box.addWidget(self.player_two_label) horizontal_player_two_name_box.addWidget( self.player_two_name_line_edit) horizontal_card_amount_box = QHBoxLayout() horizontal_card_amount_box.addWidget(self.card_amount_label) horizontal_card_amount_box.addWidget(self.card_amount_options) horizontal_card_amount_box.addStretch() horizontal_game_statistics_box = QHBoxLayout() horizontal_game_statistics_box.addWidget(self.statistics_label) horizontal_game_statistics_box.addStretch() horizontal_games_started_box = QHBoxLayout() horizontal_games_started_box.addWidget(self.games_started_label) horizontal_games_started_box.addStretch() horizontal_games_finished_box = QHBoxLayout() horizontal_games_finished_box.addWidget(self.games_finished_label) horizontal_games_finished_box.addStretch() vertical_box = QVBoxLayout() vertical_box.insertLayout(1, horizontal_go_back_to_setup_gui_box) vertical_box.insertLayout(2, horizontal_card_amount_box) vertical_box.insertLayout(3, horizontal_player_one_name_box) vertical_box.insertLayout(4, horizontal_player_two_name_box) vertical_box.addSpacing(20) vertical_box.insertLayout(5, horizontal_game_statistics_box) vertical_box.insertLayout(6, horizontal_games_started_box) vertical_box.insertLayout(7, horizontal_games_finished_box) vertical_box.addStretch() self.setLayout(vertical_box) self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height)