def add_tag_to_bar(self, text): tag = QFrame() tag.setStyleSheet( 'border:1px solid rgb(192, 192, 192); border-radius: 4px;') tag.setContentsMargins(2, 2, 2, 2) tag.setFixedHeight(28) hbox = QHBoxLayout() hbox.setContentsMargins(4, 4, 4, 4) hbox.setSpacing(10) tag.setLayout(hbox) label = QLabel(text) label.setStyleSheet('border:0px') label.setFixedHeight(16) hbox.addWidget(label) x_button = QPushButton('x') x_button.setFixedSize(20, 20) x_button.setStyleSheet('border:0px; font-weight:bold') x_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) x_button.clicked.connect(partial(self.delete_tag, text)) hbox.addWidget(x_button) tag.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred) self.hLayout.addWidget(tag)
def horizontal_line(): line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) line.setFixedHeight(2) line.setContentsMargins(0, 30, 0, 0) return line
class FrameLayout(QFrame): """ This class defines the FrameLayout which emulates the behaviour of the Maya Python API's maya.cmds.frameLayout function. The only difference is that the render setup version also provides a color bar and background color to differentiate layers, collections, and overrides. """ def __init__(self, item, parent): super(FrameLayout, self).__init__(parent) self.item = weakref.ref(item) self.isCollapsed = False self.mainLayout = None self.titleFrame = None self.contentFrame = None self.contentLayout = None self.initFrameLayout() def addWidget(self, widget): self.contentLayout.addWidget(widget) def getWidget(self, index): return self.contentLayout.itemAt( index).widget() if self.contentLayout.count() > index else None def initMainLayout(self): self.mainLayout = QVBoxLayout() self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(0) self.setLayout(self.mainLayout) def initTitleFrame(self): self.titleFrame = TitleFrame(self.item(), self) self.mainLayout.addWidget(self.titleFrame) def initContentFrame(self): self.contentFrame = QFrame() self.contentFrame.setContentsMargins(0, 0, 0, 0) self.contentLayout = QVBoxLayout() self.contentLayout.setContentsMargins(0, 0, 0, 0) self.contentLayout.setSpacing(0) self.contentFrame.setLayout(self.contentLayout) self.mainLayout.addWidget(self.contentFrame) def toggleCollapsed(self): """ When the title frame is clicked, this function is called to toggle the collapsed state """ self.isCollapsed = not self.isCollapsed self.contentFrame.setVisible(self.isCollapsed) self.titleFrame.arrowAndTitle.setArrow(self.isCollapsed) self.update() def initFrameLayout(self): self.setContentsMargins(0, 0, 0, 0) self.initMainLayout() self.initTitleFrame() self.initContentFrame() self.titleFrame.clicked.connect(self.toggleCollapsed)
class MainWindow(QMainWindow): def __init__(self, args, parent=None): super().__init__(parent=parent) self.resize(int(width_factor * 1100), int(height_factor * 635)) self.setWindowTitle("MKV Muxing Batch GUI v" + str(Version)) self.setWindowIcon(GlobalFiles.AppIcon) self.tabs = TabsManager() self.tabs_frame = QFrame() self.tabs_layout = QVBoxLayout() self.setup_tabs_layout() self.setCentralWidget(self.tabs_frame) self.setAttribute(Qt.WA_DeleteOnClose) self.show_window() self.tabs.set_default_directories() self.task_bar_progress = TaskBarProgress( window_handle=self.windowHandle()) self.connect_signals() def connect_signals(self): self.tabs.currentChanged.connect(self.update_minimum_size) self.tabs.task_bar_start_muxing_signal.connect( self.task_bar_progress.start_muxing) self.tabs.update_task_bar_progress_signal.connect( self.task_bar_progress.update_progress) self.tabs.update_task_bar_paused_signal.connect( self.task_bar_progress.pause_progress) self.tabs.update_task_bar_clear_signal.connect( self.task_bar_progress.hide_progress) def show_window(self): self.showNormal() self.raise_() self.activateWindow() def setup_tabs_layout(self): self.tabs_frame.setContentsMargins(0, 0, 0, 0) self.tabs_layout.setContentsMargins(9, 9, 9, 12) self.tabs_layout.addWidget(self.tabs) self.tabs_frame.setLayout(self.tabs_layout) def update_minimum_size(self): self.setMinimumSize(self.minimumSizeHint()) def closeEvent(self, event: PySide2.QtGui.QCloseEvent): want_to_exit = check_if_exit_when_muxing_on() if want_to_exit: super().closeEvent(event) else: event.ignore()
class Card(QWidget): def __init__(self, parent: QWidget, card_details: Dict[str, Any]): super().__init__() if parent is not None: self.setParent(parent) self.model = create_card(card_details) self.setupUi() def get_card_background_colour(self): if isinstance(self.model, SpellCard): return QColor(CardColours[CardType.SPELL]) elif isinstance(self.model, TrapCard): return QColor(CardColours[CardType.TRAP]) else: pass def setupUi(self): self.main_layout = QVBoxLayout() self.name_attr_layout = QHBoxLayout() self.name_label = QLabel(self.model.name) font = self.name_label.font() font.setBold(True) font.setCapitalization(QFont.AllUppercase) font.setPointSize(12) self.name_label.setFont(font) self.name_label.setMargin(5) pixmap = get_attr_icon(self.model.attribute) self.attr_icon = QLabel() self.attr_icon.setPixmap(pixmap) self.attr_icon.setAlignment(Qt.AlignRight) self.name_attr_layout.addWidget(self.name_label) self.name_attr_layout.addWidget(self.attr_icon) self.main_layout.addLayout(self.name_attr_layout) self.level_layout = QHBoxLayout() self.main_layout.addLayout(self.level_layout) self.picture_frame = QFrame() self.picture_frame.setFixedSize(250, 250) self.picture_frame.setFrameStyle(QFrame.Box | QFrame.Plain) self.picture_frame.setFrameShadow(QFrame.Shadow.Raised) self.picture_frame.setLineWidth(1) self.picture_frame.setContentsMargins(0, 0, 0, 0) self.picture_frame.setLayout(QGridLayout()) self.image_holder = QLabel() pixmap = self._get_card_image() self.image_holder.setPixmap( pixmap.scaled( self.picture_frame.width(), self.picture_frame.height(), Qt.KeepAspectRatio, )) self.picture_frame.layout().addWidget(self.image_holder) self.main_layout.addWidget(self.picture_frame) # Card sets here? self.desc_group_box = QGroupBox() self.desc_group_box.setMaximumWidth(250) self.set_up_group_box() self.main_layout.addWidget(self.desc_group_box) self.id_label = QLabel(self.model.id) self.id_label.setAlignment(Qt.AlignLeft) self.id_label.setMargin(5) self.main_layout.addWidget(self.id_label) self.setLayout(self.main_layout) pal = QPalette() pal.setColor(QPalette.Background, self.get_card_background_colour()) self.setAutoFillBackground(True) self.setPalette(pal) def _get_card_image(self): image_name = self.model.img_url.split("/")[-1] if os.path.exists(self.get_image_path(image_name)): image = Image.open(self.get_image_path(image_name)) else: image_data = requests.get(self.model.img_url).content image = Image.open(BytesIO(image_data)) image.save(self.get_image_path(image_name)) image = image.crop((44, 106, 380, 438)) # this is about correct data = image.tobytes("raw", "RGB") qimage = QImage(data, image.size[0], image.size[1], QImage.Format_RGB888) pixmap = QPixmap.fromImage(qimage) return pixmap def get_image_path(self, image_name): return os.path.join(os.getcwd(), "images", image_name) def set_up_group_box(self): self.desc_group_box.setLayout(QVBoxLayout()) self.desc_label = QLabel(self.model.desc) self.desc_label.setWordWrap(True) self.desc_group_box.layout().addWidget(self.desc_label) if isinstance(self.model, (MonsterCard)): self.desc_group_box.setTitle(self.get_group_box_title()) line = QFrame() line.setFrameShape((QFrame.HLine)) line.setFrameShadow(QFrame.Sunken) self.desc_group_box.layout().addWidget(line) label = QLabel( f"ATK/{self.model.attack} DEF/{self.model.defence}") label.setAlignment(Qt.AlignRight) self.desc_group_box.layout().addWidget(label) def get_group_box_title(self): return "[TEST/TEST]"
class MainWindowUi(QMainWindow): """sets up ui properties of MainWindowUi class""" def __init__(self) -> None: """inits MainWindow class configuring parameters of MainWindow class and inherits from QtWidget.QMainWindow loads .ui file sets up file and directory path vars, inits click events(menuebar, coboboxes, btns) and shows gui the first time Returns: None""" super(MainWindowUi, self).__init__() self.setWindowTitle("It_Hilfe") self.resize(820, 450) self.setWindowIcon(QIcon("./data/favicon2.png")) self.setMinimumSize(700, 250) self.file_path = None self.dir = None self.last_open_file_path = None self.last_open_file_dir = None self.initial_theme = None self.registered_devices = {} self.user_config_file = "./data/user_config.json" # setup stackedwidget self.stacked_widget = QStackedWidget() self.setCentralWidget(self.stacked_widget) self.setup_menubar() self.setup_p_view() self.setup_p_register() self.setup_p_create() self.setup_p_preferences() self.setup_signals() self.font = QFont() self.font.setPointSize(9) self.validate(self.set_user_preferences, file_path=self.user_config_file, schema=validate_json.ItHilfeUserPreferencesSchema, forbidden=[""]) # setup statusbar self.statusbar = self.statusBar() self.stacked_widget.setCurrentWidget(self.p_view) def setup_menubar(self) -> None: """inits menubar Returns: None""" self.menu_Bar = self.menuBar() menu_file = self.menu_Bar.addMenu("file") self.action_open = QAction("open") self.action_save = QAction("save") self.action_new = QAction("new") self.action_print = QAction("print") self.action_preferences = QAction("preferences") self.action_hide_menu_bar = QAction("hide menubar") self.action_print.setShortcut(QKeySequence("Ctrl+p")) self.action_open.setShortcut(QKeySequence("Ctrl+o")) self.action_save.setShortcut(QKeySequence("Ctrl+s")) self.action_hide_menu_bar.setShortcut(QKeySequence("Ctrl+h")) self.action_hide_menu_bar.setIcon(QIcon("./data/show_hide.ico")) self.action_print.setIcon(QIcon("./data/print2.ico")) self.action_open.setIcon(QIcon("./data/open.ico")) self.action_save.setIcon(QIcon("./data/save.ico")) self.action_new.setIcon(QIcon("./data/newfile.ico")) self.action_preferences.setIcon(QIcon("./data/preferences.ico")) menu_file.addAction(self.action_open) menu_file.addAction(self.action_save) menu_file.addAction(self.action_new) menu_file.addAction(self.action_print) menu_file.addAction(self.action_preferences) menu_edit = self.menu_Bar.addMenu("edit") self.action_register = QAction("register") self.action_register.setShortcut(QKeySequence("Ctrl+n")) self.action_register.setIcon(QIcon("./data/register.ico")) menu_edit.addAction(self.action_register) menu_view = self.menu_Bar.addMenu("view") menu_view.addAction(self.action_hide_menu_bar) def setup_p_view(self) -> None: """inits stacked widget page widget Returns: None""" self.p_view = QtWidgets.QWidget() self.stacked_widget.addWidget(self.p_view) self.model = QStandardItemModel(self.p_view) self.model.setHorizontalHeaderLabels(labels) self.filters = [] source_model = self.model for filter_num in range(7): filter = QSortFilterProxyModel() filter.setSourceModel(source_model) filter.setFilterKeyColumn(filter_num) source_model = filter self.filters.append(filter) delegate = ComboDelegate() self.table = QtWidgets.QTableView(self.p_view) self.table.setModel(self.filters[-1]) self.table.setItemDelegateForColumn(2, delegate) self.table.horizontalHeader().setStretchLastSection(True) self.table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.header = FilterHeader(self.table) self.header.set_filter_boxes() self.header.setMaximumHeight(50) self.table.setHorizontalHeader(self.header) self.bt_burger = QPushButton(self.p_view) self.bt_burger.setIcon(QIcon("./data/menu2.svg")) self.bt_burger.setIconSize(QSize(30, 30)) self.bt_burger.setToolTip('slide out description') l_burger = QLabel("menu", self.p_view) self.bt_register_new = QPushButton(self.p_view) self.bt_register_new.setIcon(QIcon("./data/add.ico")) self.bt_register_new.setIconSize(QSize(30, 30)) self.bt_register_new.setToolTip("register new") l_register_new = QLabel("register new", self.p_view) self.bt_delete_column = QPushButton(self.p_view) self.bt_delete_column.setIcon(QIcon("./data/remove.ico")) self.bt_delete_column.setIconSize(QSize(30, 30)) self.bt_delete_column.setToolTip( "delete columns with min 1 cell selected") l_delete = QLabel("delete column", self.p_view) self.bt_hide_show_filter = QPushButton(self.p_view) self.bt_hide_show_filter.setIcon(QIcon("./data/show_hide.ico")) self.bt_hide_show_filter.setIconSize(QSize(30, 30)) self.bt_hide_show_filter.setToolTip("hide/show filter input") l_hide_show = QLabel("hide/show", self.p_view) self.left_btn_frame = QFrame(self.p_view) self.left_btn_frame.setMaximumWidth(40) self.left_btn_frame.setContentsMargins(0, 0, 0, 0) self.left_menu_frame = QFrame(self.p_view) self.left_menu_frame.setMaximumWidth(0) self.left_menu_frame.setContentsMargins(0, 0, 0, 0) p_view_layout2 = QtWidgets.QVBoxLayout(self.left_btn_frame) p_view_layout2.addWidget(self.bt_burger) p_view_layout2.addWidget(self.bt_register_new) p_view_layout2.addWidget(self.bt_delete_column) p_view_layout2.addWidget(self.bt_hide_show_filter) p_view_layout2.setAlignment(Qt.AlignTop) p_view_layout2.setContentsMargins(0, 0, 0, 0) self.p_view_layout3 = QtWidgets.QVBoxLayout(self.left_menu_frame) self.p_view_layout3.addWidget(l_burger) self.p_view_layout3.addWidget(l_register_new) self.p_view_layout3.addWidget(l_delete) self.p_view_layout3.addWidget(l_hide_show) self.p_view_layout3.setAlignment(Qt.AlignTop | Qt.AlignCenter) self.p_view_layout3.setContentsMargins(0, 0, 0, 0) self.p_view_layout3.setSpacing(25) p_view_layout = QHBoxLayout(self.p_view) p_view_layout.setContentsMargins(0, 0, 0, 0) p_view_layout.addWidget(self.left_btn_frame) p_view_layout.addWidget(self.left_menu_frame) p_view_layout.addWidget(self.table) self.p_view.setLayout(p_view_layout) self.p_view.addAction(self.action_open) self.p_view.addAction(self.action_save) self.p_view.addAction(self.action_new) self.p_view.addAction(self.action_print) self.p_view.addAction(self.action_register) self.p_view.addAction(self.action_hide_menu_bar) def setup_p_register(self) -> None: """inits stacked widget page widgets Returns: None""" self.p_register = QtWidgets.QWidget() self.stacked_widget.addWidget(self.p_register) l_user = QtWidgets.QLabel("Username", self.p_register) self.in_username = QtWidgets.QLineEdit(self.p_register) l_devicename = QtWidgets.QLabel("Devicename", self.p_register) self.in_devicename = QtWidgets.QLineEdit(self.p_register) l_devicetype = QtWidgets.QLabel("DeviceType", self.p_register) self.in_combobox_devicetype = QtWidgets.QComboBox(self.p_register) l_os = QtWidgets.QLabel("OS", self.p_register) self.in_combobox_os = QtWidgets.QComboBox(self.p_register) l_comment = QtWidgets.QLabel("Comment", self.p_register) self.in_comment = QtWidgets.QTextEdit(self.p_register) self.bt_enter_register = QPushButton("register", self.p_register) self.bt_cancel_register = QPushButton("cancel", self.p_register) p_register_layout = QtWidgets.QVBoxLayout(self.p_register) p_register_layout.addWidget(l_user) p_register_layout.addWidget(self.in_username) p_register_layout.addWidget(l_devicename) p_register_layout.addWidget(self.in_devicename) p_register_layout.addWidget(l_devicetype) p_register_layout.addWidget(self.in_combobox_devicetype) p_register_layout.addWidget(l_os) p_register_layout.addWidget(self.in_combobox_os) p_register_layout.addWidget(l_comment) p_register_layout.addWidget(self.in_comment) p_register_layout.addWidget(self.bt_enter_register) p_register_layout.addWidget(self.bt_cancel_register) def setup_p_create(self) -> None: """inits stacked widget page widget Returns: None""" self.p_create = QtWidgets.QWidget() self.stacked_widget.addWidget(self.p_create) l_new_filepath = QtWidgets.QLabel("new filepath", self.p_create) self.bt_mod_new_path = QPushButton("mod filepath", self.p_create) self.in_new_filepath = QtWidgets.QLineEdit(self.p_create) l_new_filename = QtWidgets.QLabel("new filename", self.p_create) self.in_new_filename = QtWidgets.QLineEdit(self.p_create) self.bt_create = QPushButton("create", self.p_create) self.bt_cancel_create = QPushButton("cancel", self.p_create) p_create_layout = QtWidgets.QVBoxLayout(self.p_create) p_create_layout.addWidget(l_new_filepath) p_create_layout.addWidget(self.in_new_filepath) p_create_layout.addWidget(l_new_filename) p_create_layout.addWidget(self.in_new_filename) p_create_layout.addStretch(100) p_create_layout.addWidget(self.bt_mod_new_path) p_create_layout.addWidget(self.bt_create) p_create_layout.addWidget(self.bt_cancel_create) def setup_p_preferences(self) -> None: """inits setup_p_preferences stacked widget page widget Returns: None""" self.p_preferences = QWidget() self.p_preferences.resize(500, 250) self.p_preferences.setWindowTitle("preferences") self.list_Widget = QListWidget(self.p_preferences) self.list_Widget.addItems(["appearance", "about"]) self.list_Widget.setMaximumWidth(100) self.stacked_widget_preferences = QStackedWidget(self.p_preferences) # setup appearance self.apperence_widget = QWidget() self.stacked_widget_preferences.addWidget(self.apperence_widget) self.in_combo_themes = QComboBox(self.apperence_widget) self.in_combo_themes.addItems(["dark_theme", "light_theme"]) self.in_combo_theme_initial = QComboBox(self.apperence_widget) self.in_combo_theme_initial.addItems(["dark_theme", "light_theme"]) self.text_size_slider = QSlider(QtCore.Qt.Orientation.Horizontal, self.apperence_widget) self.text_size_slider.setTickPosition(QSlider.TickPosition.TicksAbove) self.text_size_slider.setMaximum(15) self.text_size_slider.setMinimum(8) stacked_widget_preferences_layout = QGridLayout(self.apperence_widget) stacked_widget_preferences_layout.setAlignment(QtCore.Qt.AlignTop) stacked_widget_preferences_layout.addWidget(QLabel("theme"), 0, 0) stacked_widget_preferences_layout.addWidget(self.in_combo_themes, 0, 1) stacked_widget_preferences_layout.addWidget(QLabel("initial theme"), 1, 0) stacked_widget_preferences_layout.addWidget( self.in_combo_theme_initial, 1, 1) stacked_widget_preferences_layout.addWidget(QLabel("Fontsize"), 2, 0) stacked_widget_preferences_layout.addWidget(self.text_size_slider, 2, 1) self.about_widget = QWidget() self.stacked_widget_preferences.addWidget(self.about_widget) about_text_edit = QTextEdit(self.about_widget) about_text_edit.setText( "developed by Maurice Jarck\nwith kind support from Shuai Lou\n07.2020-04.2021" ) about_text_edit.setEnabled(False) stacked_widget_about_layout = QGridLayout(self.about_widget) stacked_widget_about_layout.addWidget(about_text_edit) p_apperance_layout = QHBoxLayout(self.p_preferences) p_apperance_layout.addWidget(self.list_Widget) p_apperance_layout.addWidget(self.stacked_widget_preferences) def setup_signals(self) -> None: """connects signals Returns: None""" # header for filter, editor in zip(self.filters, self.header.editors): editor.textChanged.connect(filter.setFilterRegExp) # line edit self.in_new_filename.returnPressed.connect(lambda: self.validate( self.new, line_edit_list=[self.in_new_filepath, self.in_new_filename], data=False)) # comboboxes self.in_combobox_devicetype.addItems( ["choose here"] + [x.__name__ for x in valid_devices]) self.in_combobox_devicetype.currentIndexChanged.connect( lambda: self.update_combobox( self.in_combobox_os, valid_devices[ self.in_combobox_devicetype.currentIndex() - 1].expected_OS )) self.in_combo_themes.currentIndexChanged.connect( lambda: self.change_theme(self.in_combo_themes.currentText())) self.in_combo_theme_initial.currentTextChanged.connect(lambda: setattr( self, "initial_theme", self.in_combo_theme_initial.currentText())) # btns self.bt_delete_column.clicked.connect(self.delete) # self.bt_hide_show_filter.clicked.connect(lambda: self.toggle_hide_show_ani(37, 47, "height", self.header, b"maximumHeight")) self.bt_hide_show_filter.clicked.connect(self.header.hide_show) # self.bt_hide_show_filter.clicked.connect(lambda: self.toggle_hide_show_ani(30, 44, "height", self.header, b"maximumHeight")) self.bt_register_new.clicked.connect( lambda: self.stacked_widget.setCurrentWidget(self.p_register)) self.bt_enter_register.clicked.connect(lambda: self.validate( self.register, line_edit_list=[self.in_username, self.in_devicename], combo_box_list=[self.in_combobox_devicetype, self.in_combobox_os], forbidden=list(self.registered_devices.keys()), checkfname=True)) self.bt_create.clicked.connect(lambda: self.validate( self.new, line_edit_list=[self.in_new_filepath, self.in_new_filename], data=False)) self.bt_mod_new_path.clicked.connect(lambda: self.new(True)) self.bt_burger.clicked.connect(lambda: self.toggle_hide_show_ani( 0, 66, "width", self.left_menu_frame, b"maximumWidth", )) # menu bar self.action_register.triggered.connect( lambda: self.stacked_widget.setCurrentWidget(self.p_register)) self.action_open.triggered.connect(self.get_open_file_path) self.action_save.triggered.connect(self.save) self.action_new.triggered.connect(lambda: self.new(True)) self.action_print.triggered.connect( lambda: self.validate(self.print, data=False, checkfname=True)) self.action_hide_menu_bar.triggered.connect( lambda: self.toggle_hide_show(self.menu_Bar)) self.action_preferences.triggered.connect(self.p_preferences.show) # cancel self.bt_cancel_register.clicked.connect(lambda: self.cancel([ self.in_username, self.in_devicename, self.in_combobox_os, self. in_comment ])) # list widget self.list_Widget.currentRowChanged.connect( lambda: self.stacked_widget_preferences.setCurrentIndex( self.list_Widget.currentIndex().row())) # slider self.text_size_slider.sliderMoved.connect( lambda: self.change_font_size(self.text_size_slider.value())) # self.text_size_slider.sliderMoved.connect(lambda: print(self.text_size_slider.value())) def change_theme(self, theme) -> None: """changes theme according to combobox selection Returns: None""" with open(f"./data/{theme}.css", "r") as file: stylesheed = " ".join(file.readlines()) self.setStyleSheet(stylesheed) self.p_preferences.setStyleSheet(stylesheed) if self.in_combo_themes.currentText() == "dark_theme": self.left_btn_frame.setStyleSheet( u"background: #455364; border: 0px solid;") self.p_view_layout3.setSpacing(30) else: self.left_btn_frame.setStyleSheet( u"background: #ADADAD; border: 0px solid;") self.p_view_layout3.setSpacing(25) return self.in_combo_themes.currentText() def toggle_hide_show_ani(self, collapsed_val: int, expanded_val: int, actual: str, to_animate, property: bytes): """interpolates over a defined range of vales and sets it to a given property of a given widget""" if getattr(to_animate, actual)() == expanded_val: destination = collapsed_val else: destination = expanded_val print(getattr(to_animate, actual)(), destination) self.ani = QPropertyAnimation(to_animate, property) self.ani.setDuration(300) self.ani.setStartValue(getattr(to_animate, actual)()) self.ani.setEndValue(destination) self.ani.setEasingCurve(QEasingCurve.Linear) self.ani.start() def toggle_hide_show(self, widget: QWidget) -> None: """toggles visibiliy of a given widget Arg: widget: widget which is aimed to be hidden or shown Returs: None""" if widget.isVisible(): widget.hide() else: widget.show() def reopen_last_file(self) -> None: """asks for reopening of the last opened file""" if self.last_open_file_path != "" or self.last_open_file_path is not None: reopen_dialog = QMessageBox.question( self.p_view, "reopen last file?", "Do you want to reopen the last edited file?", QMessageBox.Yes | QMessageBox.No) if reopen_dialog == QMessageBox.Yes: self.file_path = self.last_open_file_path self.load() def change_font_size(self, size: int) -> None: """changes all font sizes""" self.font.setPointSize(size) self.menu_Bar.setFont(self.font) self.header.setFont(self.font) self.table.setFont(self.font) self.p_preferences.setFont(self.font) def set_user_preferences(self) -> None: """Reads user_config file and sets its propertys""" with open(self.user_config_file, "r") as config_file: data = dict(json.load(config_file)) self.last_open_file_path = data["last_open_file_path"] self.initial_theme = data['initial_theme'] self.change_font_size(data['font_size']) self.text_size_slider.setValue(data['font_size']) self.in_combo_theme_initial.setCurrentText(self.initial_theme) self.in_combo_themes.setCurrentText(self.initial_theme) with open(f"./data/{self.initial_theme}.css") as file: style_sheed = " ".join(file.readlines()) self.setStyleSheet(style_sheed) self.p_preferences.setStyleSheet(style_sheed) self.bt_burger.setStyleSheet( "border: 0px solid; background: transparent;") self.bt_register_new.setStyleSheet( "border: 0px solid; background: transparent;") self.bt_delete_column.setStyleSheet( "border: 0px solid; background: transparent;") self.bt_hide_show_filter.setStyleSheet( "border: 0px solid; background: transparent;") self.left_menu_frame.setStyleSheet(u" border: 0px solid;") if self.initial_theme == "dark_theme": self.left_btn_frame.setStyleSheet( u"background: #455364; border: 0px solid;") else: self.left_btn_frame.setStyleSheet( u"background: #ADADAD; border: 0px solid;") def cancel(self, widgets: list) -> None: """click event for all cancel buttons shows fist page in stacked widget and clears all widgets in widgets Args: widgets: defines list containing widgets to clear, only widgets with method .clear() are possible Returns: None""" for widget in widgets: widget.clear() self.stacked_widget.setCurrentWidget(self.p_view) def update_combobox(self, box, data: list) -> None: """ clears combo box updates combobox so that old content not needed any more isnt displayed and adds 'choose here' dummy to ensure an index change will be made (updating next box depends on index change) Args: box: instance of pyqt5.QtWidgets.qComboBox data: data supposed to be inserted into combobox Returns: None""" box.clear() box.addItems(["choose here"] + data) def validate(self, command, file_path: str = None, schema=None, line_edit_list: list = None, combo_box_list: list = None, data=None, forbidden: list = None, checkfname: bool = None) -> None: """validates user input Args: command: function to be called after vailidation process if finished line_edit_list: contents pyqt5.QtWidgets.QlineEdit instances to be checked if empty or current text in forbidden or not in allowed combo_box_list: contents pyqt5.QtWidgets.qComboBox instances to be checked if nothing selected data: data to be passed into command function if needed forbidden: houses keys which are not allowed to be entered checkfname: check weather an file path exists or not Returns: None""" fails = 0 if line_edit_list is not None: for x in line_edit_list: if x.text() == "": x.setText("fill all fields") fails += 1 if forbidden is not None and x.text() in forbidden: x.setText("in forbidden!!") fails += 1 if combo_box_list is not None: for combobox in combo_box_list: if combobox.currentText() == "": self.statusbar.showMessage("all comboboxes must be filled") fails += 1 if checkfname is True and self.file_path is None: self.statusbar.showMessage( "no file path specified, visit Ctrl+o or menuebar/edit/open to fix" ) fails += 1 if file_path is not None: if forbidden is not None and file_path in forbidden: fails += 1 self.statusbar.showMessage("select a file to continue") else: try: validate_json.validate(file_path, schema) except ValidationError as e: self.msg_box = QtWidgets.QMessageBox.critical( self, "validation failed", f"Invalid Json file, problem in: {e.messages}") fails += 1 if fails == 0: if data is None: command() else: command(data) else: message = f"problem\ncommand: {command.__name__}\nfails: {fails}" print(message) return message def register(self) -> None: """registers a new device and saves Returns: None""" logic.register(devname=self.in_devicename.text(), devtype=[ device for device in valid_devices if device.__name__ == self.in_combobox_devicetype.currentText() ].pop(), username=self.in_username.text(), os=self.in_combobox_os.currentText(), comment=self.in_comment.toPlainText(), datetime=str(datetime.datetime.now()), registered_devices=self.registered_devices) new_values = [ self.in_devicename.text(), self.in_username.text(), self.in_combobox_os.currentText(), [ device.__name__ for device in valid_devices if device.__name__ == self.in_combobox_devicetype.currentText() ].pop(), self.in_comment.toPlainText(), str(datetime.datetime.now()) ] row = [QStandardItem(str(item)) for item in new_values] self.model.appendRow(row) self.stacked_widget.setCurrentWidget(self.p_view) self.in_devicename.clear() self.in_username.clear() self.in_combobox_os.clear() self.in_comment.clear() self.save() def delete(self) -> None: """deletes all rows associated with min 1 slected cell Returns: None""" rows = sorted(set(index.row() for index in self.table.selectedIndexes()), reverse=True) qb = QMessageBox() answ = qb.question(self, 'delete rows', f"Are you sure to delete {rows} rows?", qb.Yes | qb.No) if answ == qb.Yes: for row in rows: self.registered_devices.pop( str(self.model.index(row, 0).data())) self.model.removeRow(row) qb.information(self, 'notification', f"deleted {rows} row") else: qb.information(self, 'notification', "Nothing Changed") self.save() def get_open_file_path(self) -> None: """gets file-path and set it to self.file_path, extra step for json validation Returns: None""" self.file_path = \ QFileDialog.getOpenFileName(self, "open file", f"{self.last_open_file_dir or 'c://'}", "json files (*json)")[0] self.validate(command=self.load, file_path=self.file_path, schema=validate_json.ItHilfeDataSchema, forbidden=[""]) def load(self) -> None: """opens json file and loads its content into registered devices Returns: None""" self.model.clear() self.registered_devices.clear() with open(self.file_path, "r") as file: data = dict(json.load(file)) devices = data["devices"].values() self.last_open_file_dir = data["last_open_file_dir"] for value in devices: row = [] for i, item in enumerate(value): cell = QStandardItem(str(item)) row.append(cell) if i == 0 or i == 3 or i == 5: cell.setEditable(False) self.model.appendRow(row) new = [x for x in valid_devices if x.__name__ == value[3]].pop(0)(value[0], value[1], value[4], value[5]) new.OS = value[2] self.registered_devices[value[0]] = new self.model.setHorizontalHeaderLabels(labels) self.statusbar.showMessage("") # auto complete for a in range(len(self.header.editors)): completer = QCompleter([ self.model.data(self.model.index(x, a)) for x in range(self.model.rowCount()) ]) completer.setCompletionMode(QCompleter.InlineCompletion) self.header.editors[a].setCompleter(completer) def save(self) -> None: """saves content fo self.registered_devices into specified json file Returns: None""" if not self.file_path: self.statusbar.showMessage( "no file path set all changes get lost if closed") else: with open( self.file_path, 'w', ) as file: devices = { k: [ v.name, v.user, v.OS, v.__class__.__name__, v.comment, v.datetime ] for (k, v) in enumerate(self.registered_devices.values()) } last_open_file_dir = "/".join(self.file_path.split("/")[:-1]) resulting_dict = { "devices": devices, "last_open_file_dir": last_open_file_dir } json.dump(resulting_dict, file) self.statusbar.showMessage("saved file") with open(self.user_config_file, "w") as user_preferences_file: json.dump( { "last_open_file_path": self.last_open_file_path, "initial_theme": self.initial_theme, "font_size": self.text_size_slider.value() }, user_preferences_file) def new(self, stage: bool, test: bool = False) -> None: """creates new csv file to save into stage is True: set filepath stage is False: set new name, save Args: stage: determines a which stage to execute this function Returns: None""" if stage is True: if not test: self.dir = QFileDialog.getExistingDirectory( self, "select a folder", "c://") self.stacked_widget.setCurrentWidget(self.p_create) self.in_new_filepath.setText(self.dir) self.registered_devices.clear() else: self.file_path = self.dir + f"/{self.in_new_filename.text()}.json" self.save() self.stacked_widget.setCurrentWidget(self.p_view) def print(self, test: bool) -> None: """setup and preview pViewTable for paper printing Returns: None""" with open(self.file_path) as f: self.data = json.dumps(dict(json.load(f)), sort_keys=True, indent=6, separators=(".", "=")) self.document = QtWidgets.QTextEdit() self.document.setText(self.data) if not test: printer = QPrinter() previewDialog = QPrintPreviewDialog(printer, self) previewDialog.paintRequested.connect( lambda: self.document.print_(printer)) previewDialog.exec_()