class Workshop(QWidget): def __init__(self): super().__init__() self.n = 0 self.btn = QPushButton('Push me') self.lbl = QLabel(str(self.n)) self.main_layout = QVBoxLayout() self.sub_layout = QVBoxLayout() self.sub_layout.addWidget(self.lbl) self.sub_layout.addWidget(self.btn) self.main_layout.addLayout(self.sub_layout) self.btn.clicked.connect(self.change_label) self.setLayout(self.main_layout) self.show() def replace_label(self, old_label, new_label): """ Replace old_label by new_label in self.main_layout and its sub layouts. Do NOT set old_label to be new_label, the change is only in self.main_layout! You may see on [my post](https://stackoverflow.com/questions/ 63094202/qlayout-replace-not-replacing/63094407#63094407) that self.main_layout.replaceWidget(old_label, new_label) does not do the job. We need to also use old_label.deleteLater(). By the way, it is written on the C++ documentation but NOT the Python one that: > The parent of widget from is left unchanged. This is idiotic. I hate it, and I am currently listening to Banco del Mutuo Soccorso so I should instead be happy. F**k that. Also, f**k Trump. :param old_label: Label to be replaced and deleted. :param new_label: Label to replace old_label. """ self.main_layout.replaceWidget(old_label, new_label) old_label.deleteLater() @Slot() def change_label(self): new_label = QLabel(str(self.n + 1)) self.replace_label(self.lbl, new_label) self.n += 1 self.lbl = new_label @Slot() def change_label_USELESS(self): """ By using this fuction around line 21 in: self.btn.clicked.connect(self.change_label_useless) we see that simply setting self.lbl = new_label does not change it in the interface. """ new_label = QLabel(str(self.n + 1)) self.n += 1 self.lbl = new_label
class SubtitleTabManager(GlobalSetting): activation_signal = Signal(bool) tab_clicked_signal = Signal() def __init__(self): super().__init__() self.subtitle_tabs = [] self.subtitle_tabs_indices = [] self.current_index_counter = 0 self.current_tab_index = 0 self.subtitle_tab_comboBox = SubtitleTabComboBox() self.subtitle_tab_delete_button = SubtitleTabDeleteButton() self.MainLayout = QVBoxLayout() self.subtitle_tab_setting_layout = QHBoxLayout() self.setLayout(self.MainLayout) self.subtitle_tabs.append( SubtitleSelectionSetting(self.current_index_counter)) self.subtitle_tabs_indices.append(self.current_index_counter) self.current_index_counter += 1 self.current_subtitle_tab = self.subtitle_tabs[-1] self.current_subtitle_tab.is_there_old_files_signal.connect( self.update_is_there_old_files) self.current_subtitle_tab.is_there_old_files_signal.connect( self.update_is_subtitle_enabled) self.subtitle_tab_setting_layout.addWidget(self.subtitle_tab_comboBox) self.subtitle_tab_setting_layout.addWidget( self.subtitle_tab_delete_button) self.subtitle_tab_setting_layout.addStretch(1) self.MainLayout.addLayout(self.subtitle_tab_setting_layout) self.MainLayout.addWidget(self.current_subtitle_tab) self.subtitle_tab_delete_button.hide() self.activation_signal.emit(True) self.connect_signals() def connect_signals(self): self.subtitle_tab_comboBox.current_tab_changed_signal.connect( self.change_current_tab) self.subtitle_tab_comboBox.create_new_tab_signal.connect( self.create_new_tab) self.subtitle_tab_delete_button.remove_tab_signal.connect( self.delete_current_tab) self.tab_clicked_signal.connect(self.tab_clicked) def change_current_tab(self, tab_index): real_index = self.subtitle_tabs_indices[tab_index] self.MainLayout.replaceWidget(self.current_subtitle_tab, self.subtitle_tabs[tab_index]) self.current_subtitle_tab.hide() self.subtitle_tabs[tab_index].show() self.current_subtitle_tab = self.subtitle_tabs[tab_index] self.current_tab_index = real_index if real_index == 0: self.subtitle_tab_delete_button.hide() else: self.subtitle_tab_delete_button.show() self.subtitle_tab_delete_button.set_is_there_old_file( len(GlobalSetting.SUBTITLE_FILES_LIST[real_index]) > 0) self.current_subtitle_tab.tab_clicked_signal.emit() def create_new_tab(self): self.subtitle_tabs.append( SubtitleSelectionSetting(self.current_index_counter)) self.subtitle_tabs_indices.append(self.current_index_counter) self.current_tab_index = self.current_index_counter self.current_index_counter += 1 self.MainLayout.replaceWidget(self.current_subtitle_tab, self.subtitle_tabs[-1]) self.current_subtitle_tab.hide() self.subtitle_tabs[-1].show() self.current_subtitle_tab = self.subtitle_tabs[-1] self.current_subtitle_tab.is_there_old_files_signal.connect( self.update_is_there_old_files) self.current_subtitle_tab.is_there_old_files_signal.connect( self.update_is_subtitle_enabled) self.subtitle_tab_delete_button.show() self.current_subtitle_tab.tab_clicked_signal.emit() def delete_current_tab(self): index_to_delete = self.current_tab_index index_in_list = self.subtitle_tabs_indices.index( self.current_tab_index) previous_tab_index = index_in_list - 1 self.MainLayout.replaceWidget(self.subtitle_tabs[index_in_list], self.subtitle_tabs[previous_tab_index]) self.subtitle_tab_comboBox.delete_tab(index_in_list) self.subtitle_tabs[index_in_list].hide() self.subtitle_tabs[index_in_list].deleteLater() self.subtitle_tabs.remove(self.subtitle_tabs[index_in_list]) self.current_subtitle_tab = self.subtitle_tabs[previous_tab_index] self.current_subtitle_tab.show() self.subtitle_tabs_indices.remove(index_to_delete) GlobalSetting.SUBTITLE_FILES_LIST.pop(index_to_delete, None) GlobalSetting.SUBTITLE_FILES_ABSOLUTE_PATH_LIST.pop( index_to_delete, None) GlobalSetting.SUBTITLE_DELAY.pop(index_to_delete, None) GlobalSetting.SUBTITLE_TRACK_NAME.pop(index_to_delete, None) GlobalSetting.SUBTITLE_SET_DEFAULT.pop(index_to_delete, None) GlobalSetting.SUBTITLE_SET_FORCED.pop(index_to_delete, None) GlobalSetting.SUBTITLE_SET_AT_TOP.pop(index_to_delete, None) GlobalSetting.SUBTITLE_TAB_ENABLED.pop(index_to_delete, None) GlobalSetting.SUBTITLE_LANGUAGE.pop(index_to_delete, None) self.current_tab_index = self.subtitle_tabs_indices[previous_tab_index] def update_is_there_old_files(self, new_state): self.subtitle_tab_delete_button.set_is_there_old_file(new_state) def update_is_subtitle_enabled(self): for state in GlobalSetting.SUBTITLE_TAB_ENABLED.values(): if state: self.activation_signal.emit(True) GlobalSetting.SUBTITLE_ENABLED = True return GlobalSetting.SUBTITLE_ENABLED = False self.activation_signal.emit(False) def tab_clicked(self): self.current_subtitle_tab.tab_clicked_signal.emit() if not GlobalSetting.JOB_QUEUE_EMPTY: self.subtitle_tab_delete_button.setEnabled(False) self.subtitle_tab_comboBox.hide_new_tab_option() else: self.subtitle_tab_delete_button.setEnabled(True) self.subtitle_tab_comboBox.show_new_tab_option() def set_default_directory(self): for subtitle_tab in self.subtitle_tabs: subtitle_tab.set_default_directory()
class _RemoveNanEditor(AbsOperationEditor): _baseText = { 0: 'Remove with more than: <b>{}</b> nan', 1: 'Remove with more than: <b>{}%</b> nan' } def __init__(self, mode: str, parent: QWidget = None): """ Builds the editor :param mode: one of 'col' or 'row' :param parent: a parent widget """ self.__mode: str = mode super().__init__(parent) def editorBody(self) -> QWidget: self.__group = QButtonGroup() self.__group.setExclusive(True) lab = QLabel('Choose how to remove:') self.__group.addButton(QRadioButton('By number'), id=0) self.__group.addButton(QRadioButton('By percentage'), id=1) self.__currId = None self.__summaryLabel = QLabel() self.__slider = QSlider(Qt.Horizontal, self) self.__slider.setMinimum(0) self.__slider.setTracking(True) self.__slider.setSingleStep(1) self.__numBox = QSpinBox() self.__numBox.setMinimum(0) self.__numBox.setMaximum(10000000) radioLayout = QHBoxLayout() radioLayout.addWidget(self.__group.button(0)) radioLayout.addWidget(self.__group.button(1)) self.__bodyLayout = QVBoxLayout() self.__bodyLayout.addWidget(lab) self.__bodyLayout.addLayout(radioLayout) self.__bodyLayout.addSpacing(20) self.__bodyLayout.addWidget(QLabel('Move the slider to set removal parameter:')) self.__bodyLayout.addSpacing(10) self.__bodyLayout.addWidget(self.__slider if self.__mode == 'row' else self.__numBox) self.__bodyLayout.addWidget(self.__summaryLabel) self.__group.buttonClicked[int].connect(self._toggleMode) # Both are connected, only one is shown self.__slider.valueChanged.connect(self._onValueChanged) self.__numBox.valueChanged[int].connect(self._onValueChanged) # Set a default button and label text self.__group.button(0).click() self.__summaryLabel.setText(self._baseText[0].format(self.__slider.minimum())) body = QWidget() body.setLayout(self.__bodyLayout) return body @Slot(int) def _toggleMode(self, bid: int) -> None: # NOTE: could be refactored if bid == self.__currId: return self.__currId = bid if bid == 0: if not (self.inputShapes and self.inputShapes[0]) and self.__mode == 'row': self.__slider.setDisabled(True) self._onValueChanged(self.__slider.value()) elif not self.__slider.isEnabled(): self.__slider.setEnabled(True) else: if self.__mode == 'row': self.__slider.setMaximum(self.inputShapes[0].nColumns) self._onValueChanged(self.__slider.value()) else: self.__bodyLayout.replaceWidget(self.__slider, self.__numBox) self.__slider.hide() self.__numBox.show() self._onValueChanged(self.__numBox.value()) else: if self.__mode == 'row': if not self.__slider.isEnabled(): self.__slider.setEnabled(True) else: self.__bodyLayout.replaceWidget(self.__numBox, self.__slider) self.__numBox.hide() self.__slider.show() self._onValueChanged(self.__slider.value()) self.__slider.setMaximum(100) @Slot(int) def _onValueChanged(self, value: int): self.__summaryLabel.setText(self._baseText[self.__currId].format(value)) def getOptions(self) -> Iterable: if self.__group.checkedId() == 0: # By number return None, self.__slider.value() if self.__mode == 'row' else self.__numBox.value() else: # By perc return self.__slider.value() / 100, None def setOptions(self, percentage: float, number: int) -> None: if percentage is not None: self.__group.button(1).click() self.__slider.setValue(percentage * 100) elif number is not None: self.__group.button(0).click() self.__slider.setValue(number) if self.__mode == 'row' else self.__numBox.setValue(number) else: # Both None self.__slider.setValue(0) self.__numBox.setValue(0) def refresh(self) -> None: if self.__mode == 'row' and self.__group.checkedId() == 0: self.__slider.setMaximum(self.inputShapes[0].nColumns) self.__slider.setEnabled(True)