2********************************************************************************
4@brief individual GUI toolkit function and selection
5********************************************************************************
11from typing
import Any, Optional, TYPE_CHECKING
18from PyQt6.QtGui
import QIcon, QPixmap, QFont, QAction, QActionGroup, QStatusTipEvent, QColor, QResizeEvent, QCloseEvent
19from PyQt6.QtWidgets
import QMainWindow, QMessageBox, QAbstractItemView, QTableWidgetItem, QPushButton, QLabel, QTextBrowser, QStatusBar, QTabWidget, \
20 QHeaderView, QInputDialog, QLineEdit, QApplication, QDialog, QFileDialog, QMenuBar, QTableWidget, QCheckBox, QWidget, QMenu
21from PyQt6.QtCore
import QSharedMemory, Qt, QThread, QSettings, QObject, pyqtSignal, pyqtBoundSignal, QSize, QRect, QEvent, QTimer, QByteArray
23from Source.Views.mainwindow_ui
import Ui_MainWindow
24from Source.Views.dialogs.dialog_splash_ui
import Ui_SplashScreen
25from Source.Views.dialogs.dialog_about_ui
import Ui_AboutDialog
26from Source.Views.dialogs.dialog_help_ui
import Ui_HelpDialog
27from Source.Views.dialogs.dialog_report_ui
import Ui_ReportDialog
28from Source.Views.dialogs.dialog_article_size_ui
import Ui_ArticleSizeDialog
34APPLICATION = QApplication
35SHARED_MEMORY = QSharedMemory
36WINDOW_TYPE = QMainWindow
39SIGNAL_BOUND = pyqtBoundSignal
45RESIZE_EVENT = QResizeEvent
46CLOSE_EVENT = QCloseEvent
49BYTE_ARRAY = QByteArray
51MAIN_WINDOW = Ui_MainWindow
52SPLASH_SCREEN = Ui_SplashScreen
53HELP_DIALOG = Ui_HelpDialog
54ABOUT_DIALOG = Ui_AboutDialog
55REPORT_DIALOG = Ui_ReportDialog
56ARTICLE_SIZE_DIALOG = Ui_ArticleSizeDialog
68 @brief Check if application is already open
69 @param shared_memory : shared memory
70 @return status if application is already open
72 b_already_open =
not shared_memory.create(1)
76def run_app(window:
"APPLICATION | DIALOG | MainWindow", app: Optional[APPLICATION | DIALOG], close_event: bool =
False) ->
None:
78 @brief Run application
79 @param window : window
81 @param close_event: close complete application
83 if window
is not None:
84 if not isinstance(window, APPLICATION):
93def close_app(window:
"DIALOG | MainWindow") ->
None:
95 @brief Close application
96 @param window : window to close
103 @brief Connect signal.
104 @param signal : signal variable
105 @param function : function to connect
107 signal.connect(function)
112 @brief Disconnect signal.
113 @param signal : signal variable
121 @param signal : signal to call
130def config_window(window:
"DIALOG | MainWindow", title: Optional[str] =
None, frameless: bool =
False, icon: Optional[str] =
None, fullscreen: Optional[bool] =
None,
131 min_width: Optional[int] =
None, min_height: Optional[int] =
None, geometry: Optional[QByteArray] =
None, state: Optional[QByteArray] =
None,
132 fix_size: bool =
False, resize: Optional[tuple[int, int]] =
None, move: Optional[tuple[int, int]] =
None, style: Optional[str] =
None,
133 resize_callback: Any =
None, show: Optional[bool] =
None, thread: Optional[bool] =
False) ->
None:
135 @brief Configure Window
136 @param window : window
137 @param title : windows title
138 @param icon : window icon
139 @param fullscreen : fullscreen status
140 @param min_width : minimum window width
141 @param min_height : minimum window height
142 @param geometry : window geometry
143 @param state : window state
144 @param fix_size : state if window is resizable
145 @param resize : resize size
146 @param move : move window
147 @param style : window style
148 @param resize_callback : resize callback function
149 @param frameless : no window flags and frameless
150 @param show : show/close window
151 @param thread : run window in Thread
153 if title
is not None:
154 window.setWindowTitle(title)
156 window.setWindowFlag(Qt.WindowType.FramelessWindowHint)
159 window.setWindowIcon(QIcon(icon))
160 if fullscreen
is not None:
162 if not window.isFullScreen():
163 window.showFullScreen()
165 if window.isFullScreen():
167 if min_width
is not None:
168 window.setMinimumWidth(min_width)
169 if min_height
is not None:
170 window.setMinimumHeight(min_height)
171 if geometry
is not None:
172 window.restoreGeometry(geometry)
173 if state
is not None:
174 if not isinstance(window, DIALOG):
175 window.restoreState(state)
177 window.setFixedSize(window.size())
178 if resize
is not None:
179 width, height = resize
180 window.resize(width, height)
184 if style
is not None:
185 window.setStyleSheet(style)
186 if resize_callback
is not None:
187 if not isinstance(window, DIALOG):
188 window.resized.connect(resize_callback)
196def config_app(app: APPLICATION, icon: Optional[str] =
None, process_events: Optional[bool] =
False) ->
None:
198 @brief Configure Application
199 @param app : application
200 @param icon : window icon
201 @param process_events : process event status
204 app.setWindowIcon(QIcon(icon))
211 @brief Get window size
212 @param window : window
213 @return windows width and height
215 width = window.frameGeometry().width()
216 height = window.frameGeometry().height()
222 @brief Get window position
223 @param window : window
224 @return windows x and y position
233 @brief Get window geometry for safe
234 @param window : window
235 @return window geometry
237 window_geometry = window.saveGeometry()
238 return window_geometry
243 @brief Get window state for safe
244 @param window : window
245 @return window geometry
247 window_geometry = window.saveState()
248 return window_geometry
253 @brief Get fullscreen status
254 @param window : window
255 @return fullscreen status
257 fullscreen = bool(window.isFullScreen())
264 @param dark_mode : style status for dark mode
269 style = qdarktheme.load_stylesheet(
"dark", corner_shape=
"rounded")
271 style = qdarktheme.load_stylesheet(
"light", corner_shape=
"rounded")
281 @brief Menubar filter
282 @param menubar : menubar
284 @return event filter TODO kein richtiger return
286 class StatusTipFilter(QObject):
291 def eventFilter(self, watched: Optional[QObject], event: Optional[QEvent]) -> bool:
293 @brief Filter to prevent tip event (statusbar message no longer disappears on menu hover).
294 @param watched : object
295 @param event : arrived event
296 @return return event filter
298 if isinstance(event, QStatusTipEvent):
301 b_return = super().eventFilter(watched, event)
304 menubar.installEventFilter(StatusTipFilter(gui))
307def connect_menu(menu: QMenu | QAction, function: Any, index: Any =
"") ->
None:
310 @param menu : connect function to this menu
311 @param function : function to connect
312 @param index : index to connect function with parameter
315 menu.triggered.connect(function)
317 menu.triggered.connect(
lambda: function(index))
322 @brief Get text from menu
323 @param menu : menu to get text
324 @return text from menu
326 menu_text = menu.text()
330def config_menu(menu: QMenu | QAction, enable: Optional[bool] =
None, show: Optional[bool] =
None,
331 text: Optional[str] =
None, checked: Optional[bool] =
None, icon: Optional[str] =
None) ->
None:
333 @brief Configure menu.
334 @param menu : menu to configure
335 @param enable : enable status
336 @param show : show status
337 @param text : menu text
338 @param checked : checked status
339 @param icon : path for menu icon
341 if enable
is not None:
342 menu.setEnabled(enable)
344 if isinstance(menu, QMenu):
345 menu_action = menu.menuAction()
346 if menu_action
is not None:
347 menu_action.setVisible(show)
349 menu.setVisible(show)
351 if isinstance(menu, QMenu):
356 font.setPointSize(I_MENU_FONT_SIZE)
358 if checked
is not None:
359 if isinstance(menu, QAction):
360 menu.setChecked(checked)
362 menu.setIcon(QIcon(icon))
365def group_menu(parent:
"MainWindow", l_actions: list[QAction], actual_value: Any, l_match_values: list[Any]) -> QActionGroup:
367 @brief Set action group.
368 @param parent : parent window controller
369 @param l_actions: list with actions to add
370 @param actual_value : actual setting value
371 @param l_match_values : list with possible match values to check default setting
372 @return action group object
374 action_group = QActionGroup(parent)
375 for action
in l_actions:
376 action_group.addAction(action)
377 for i, match_value
in enumerate(l_match_values):
378 if match_value == actual_value:
379 l_actions[i].setChecked(
True)
385 l_match_values: list[Any], none_action: QAction) -> tuple[QActionGroup, bool]:
387 @brief Set action group.
388 @param parent : parent window controller
389 @param l_actions: list with actions to add
390 @param actual_value : actual setting value
391 @param l_match_values : list with possible match values to check default setting
392 @param none_action : none action is set if actual_value is None
393 @return action group object
395 action_group = QActionGroup(parent)
396 action_group.addAction(none_action)
397 all_actions_add =
True
399 if actual_value
is None:
400 none_action.setChecked(
True)
401 b_action_present =
True
403 none_action.setChecked(
False)
404 b_action_present =
False
406 for value
in l_match_values:
407 if i_action_index < len(l_actions):
408 config_menu(l_actions[i_action_index], enable=
True, show=
True, text=value)
409 action_group.addAction(l_actions[i_action_index])
410 if actual_value == value:
411 b_action_present =
True
413 l_actions[i_action_index].setChecked(
True)
415 l_actions[i_action_index].setChecked(
False)
418 all_actions_add =
False
420 for i_index
in range(i_action_index, len(l_actions)):
421 if (i_index == i_action_index)
and (
not b_action_present):
422 config_menu(l_actions[i_index], enable=
False, show=
True, text=actual_value)
423 l_actions[i_index].setChecked(
True)
425 config_menu(l_actions[i_index], enable=
False, show=
False)
426 action_group.addAction(l_actions[i_index])
427 return action_group, all_actions_add
434def connect_button(btn: QPushButton, clicked_fnc: Any =
None, pressed_fnc: Any =
None,
435 released_fnc: Any =
None, index: Any =
"") ->
None:
437 @brief Connect widget.
438 @param btn : connect function to this button
439 @param clicked_fnc : function to connect button clicked
440 @param pressed_fnc : function to connect button pressed
441 @param released_fnc : function to connect button released
442 @param index : index to connect function with parameter
444 if clicked_fnc
is not None:
446 btn.clicked.connect(clicked_fnc)
448 btn.clicked.connect(
lambda: clicked_fnc(index))
449 if pressed_fnc
is not None:
451 btn.pressed.connect(pressed_fnc)
453 btn.pressed.connect(
lambda: pressed_fnc(index))
454 if released_fnc
is not None:
456 btn.released.connect(released_fnc)
458 btn.released.connect(
lambda: released_fnc(index))
461def config_btn(btn: QPushButton, enable: Optional[bool] =
None, show: Optional[bool] =
None, text: Optional[str] =
None,
462 fg: Optional[str] =
None, bg: Optional[str] =
None, style: Optional[str] =
None, icon: Optional[str] =
None,
463 size: Optional[tuple[int, int, int, int]] =
None, font: Optional[QFont] =
None, icon_size: Optional[int] =
None) ->
None:
465 @brief Configure button.
466 @param btn : button to configure
467 @param enable : enable status
468 @param show : show status
469 @param text : button text
470 @param fg : foreground color
471 @param bg : background color
472 @param style : button style
473 @param icon : icon in button
474 @param size : button size and position
475 @param font : button text font
476 @param icon_size : icon size
478 if enable
is not None:
479 btn.setEnabled(enable)
489 btn.setStyleSheet(f
"color: {fg};")
491 btn.setStyleSheet(f
"background-color: {bg};")
493 btn.setStyleSheet(style)
497 image.addPixmap(QPixmap(icon), QIcon.Mode.Normal, QIcon.State.Off)
501 if icon_size
is not None:
502 btn.setIconSize(QSize(icon_size, icon_size))
504 x, y, width, height = size
505 btn.setGeometry(QRect(x, y, width, height))
512 @brief Get button text
513 @param btn : button to get text
516 btn_text = btn.text()
524def config_label(label: QLabel, text: Optional[str] =
None, fg: Optional[str] =
None, bg: Optional[str] =
None,
525 size: Optional[tuple[int, int, int, int]] =
None, font: Optional[QFont] =
None, central: bool =
False,
526 open_link: Optional[str] =
None, show: Optional[bool] =
None) ->
None:
528 @brief Configure label.
529 @param label : label widget to set
530 @param text : text to set
531 @param fg : foreground color
532 @param bg : background color
533 @param size : text size and position
534 @param font : text font
535 @param central : central alignment
536 @param open_link : open link
537 @param show : show text
540 if open_link
is not None:
541 text += f
"<a href=\"{open_link}\">{open_link}</a>"
544 label.setStyleSheet(f
"background-color: {bg};")
546 label.setStyleSheet(f
"color: {fg};")
548 x, y, width, height = size
549 label.setGeometry(QRect(x, y, width, height))
553 label.setAlignment(Qt.AlignmentFlag.AlignCenter)
554 if open_link
is not None:
555 label.setOpenExternalLinks(bool(open_link))
567def config_text(label: QTextBrowser, text: Optional[str] =
None, fg: Optional[str] =
None, bg: Optional[str] =
None,
568 size: Optional[tuple[int, int, int, int]] =
None, font: Optional[QFont] =
None) ->
None:
570 @brief Configure text.
571 @param label : label widget to set
572 @param text : text to set
573 @param fg : foreground color
574 @param bg : background color
575 @param size : text size and position
576 @param font : text font
581 label.setStyleSheet(f
"color: {fg};")
583 label.setStyleSheet(f
"background-color: {bg};")
585 x, y, width, height = size
586 label.setGeometry(QRect(x, y, width, height))
593 @brief Configure text.
594 @param label : label widget to set
595 @return text from label
597 text = label.toPlainText()
605def config_statusbar(statusbar: QStatusBar, lbl_left: Optional[QLabel] =
None, lbl_right: Optional[QLabel] =
None,
606 text: Optional[str] =
None, fg: Optional[str] =
None, bg: Optional[str] =
None) ->
None:
608 @brief Configure statusbar.
609 @param statusbar : statusbar widget
610 @param lbl_left : statusbar label left
611 @param lbl_right : statusbar label right
612 @param text : statusbar text
613 @param fg : front color
614 @param bg : background color
616 if (lbl_left
is None)
and (lbl_right
is None):
618 statusbar.showMessage(text)
620 statusbar.setStyleSheet(f
"background-color: {bg};")
622 statusbar.setStyleSheet(f
"color: {fg};")
624 if lbl_left
is not None:
625 statusbar.addWidget(lbl_left)
626 if lbl_right
is not None:
627 statusbar.addPermanentWidget(lbl_right)
634def config_table(table: QTableWidget, clear_selection: bool =
False, single_row_selection: Optional[bool] =
None,
635 row_description: Optional[list[str]] =
None, font: Optional[QFont] =
None,
636 size: Optional[tuple[int, int, int, int]] =
None, min_section_size: Optional[int] =
None,
637 row_count: Optional[int] =
None, auto_size: bool =
False) ->
None:
639 @brief Configure table
640 @param table : table to configure
641 @param clear_selection : clear selected table item
642 @param single_row_selection : only one line can select
643 @param row_description : row description text
644 @param font : row description font
645 @param size :row description size
646 @param min_section_size : default vertical size
647 @param row_count : number of rows to set
648 @param auto_size : do auto size of horizontal entries
650 if single_row_selection
is not None:
651 if single_row_selection:
652 table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
654 table.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
656 table.clearSelection()
657 if row_description
is not None:
658 table.setColumnCount(len(row_description))
659 for i, value
in enumerate(row_description):
660 item = QTableWidgetItem()
661 table.setHorizontalHeaderItem(i, item)
662 horizontal_header_item = table.horizontalHeaderItem(i)
663 if horizontal_header_item
is not None:
664 horizontal_header_item.setText(value)
666 horizontal_header_item.setFont(font)
668 x, y, width, height = size
669 table.setGeometry(QRect(x, y, width, height))
670 if min_section_size
is not None:
672 vertical_header = table.verticalHeader()
673 if vertical_header
is not None:
674 vertical_header.setMinimumSectionSize(min_section_size)
675 vertical_header.setDefaultSectionSize(min_section_size)
676 if row_count
is not None:
677 table.setRowCount(row_count)
681 for row
in range(table.rowCount()):
682 for col
in range(table.columnCount()):
683 table_item = table.item(row, col)
684 if table_item
is not None:
685 table_item.setFont(font)
686 table.resizeColumnsToContents()
687 header = table.horizontalHeader()
688 if header
is not None:
689 header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
690 vertical_header = table.verticalHeader()
691 if vertical_header
is not None:
692 vertical_header.setVisible(
False)
693 table.scrollToBottom()
697 font: Optional[QFont] =
None, fg: Optional[str] =
None, bg: Optional[str] =
None) ->
None:
699 @brief Insert table item
700 @param table : insert item to this table
701 @param i_entry_index : table index of the new item
702 @param l_item_param : item parameter to insert to table
703 @param font : font of table item
704 @param fg : foreground color
705 @param bg : foreground color
707 for j, param
in enumerate(l_item_param):
708 row_cnt = table.rowCount()
709 if row_cnt <= i_entry_index:
710 table.insertRow(i_entry_index)
711 item = QTableWidgetItem(param)
713 item.setForeground(QColor(fg))
715 item.setBackground(QColor(bg))
717 item.setTextAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
719 item.setTextAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
722 table.setItem(i_entry_index, j, item)
727 @brief Get selected table row
728 @param table : get selected row of this table
729 @return selected table row index
731 selected_items = table.selectedItems()
733 selected_row = selected_items[0].row()
741 @brief Get selected table column
742 @param table : get selected column of this table
743 @return selected table column index
745 selected_items = table.selectedItems()
747 selected_column = selected_items[0].column()
749 selected_column =
None
750 return selected_column
757def create_dialog(parent:
"MainWindow | None", icon: Optional[str] =
None, minmax: bool =
False, frameless: bool =
False) -> DIALOG:
760 @param parent : parent window controller
761 @param icon : table header to set
762 @param minmax : min and max window flag
763 @param frameless : no window flags and frameless
766 dialog = QDialog(parent)
767 dialog.setWindowFlag(Qt.WindowType.WindowContextHelpButtonHint,
False)
768 dialog.setWindowFlag(Qt.WindowType.WindowMinMaxButtonsHint, minmax)
770 dialog.setWindowFlag(Qt.WindowType.FramelessWindowHint)
773 dialog.setWindowIcon(QIcon(icon))
777def config_tab(tab: QTextBrowser |
None, tab_index: Optional[int] =
None, text: Optional[str] =
None,
778 main_tab: Optional[QTabWidget] =
None, tab_name: Optional[str] =
None) ->
None:
780 @brief Select Tab index
781 @param tab : tab to config
782 @param tab_index : tab index to set
783 @param text : text to set
784 @param main_tab : main tab
785 @param tab_name : tab name
787 if main_tab
is not None:
788 if tab_index
is not None:
789 main_tab.setCurrentIndex(tab_index)
790 if tab_name
is not None:
791 main_tab.setTabText(tab_index, tab_name)
794 html = markdown.markdown(text)
798def open_message_box(parent:
"MainWindow | None", title: str, text: str, btn_yes: Optional[str] =
None, btn_no: Optional[str] =
None,
799 btn_ok: Optional[str] =
None, btn_close: Optional[str] =
None, btn_cancel: Optional[str] =
None, btn_special: Optional[str] =
None,
800 check_box: Optional[str] =
None, window_icon: Optional[str] =
None, detailed_text: Optional[str] =
None, execute: bool =
True) -> tuple[bool, bool]:
802 @brief Open message box
803 @param parent : parent window controller
804 @param title : box title
805 @param text : box text
806 @param btn_yes : button yes
807 @param btn_no : button no
808 @param btn_ok : button okay
809 @param btn_close : button close
810 @param btn_cancel : button cancel
811 @param btn_special : special button (user defined) name
812 @param check_box : name for check box
813 @param window_icon : box icon
814 @param detailed_text : detailed text
815 @param execute : execute message box
816 @return accept status of message box and check box status
819 dialog = QMessageBox(parent)
820 dialog.setWindowTitle(title)
821 if window_icon
is not None:
822 dialog.setWindowIcon(QIcon(window_icon))
825 dialog.setIcon(QMessageBox.Icon.Critical)
827 dialog.setIcon(QMessageBox.Icon.Question
if btn_cancel
else QMessageBox.Icon.Information)
828 if btn_yes
is not None:
829 dialog.addButton(QMessageBox.StandardButton.Yes)
830 if isinstance(btn_yes, str):
831 btn = dialog.button(QMessageBox.StandardButton.Yes)
834 if btn_no
is not None:
835 dialog.addButton(QMessageBox.StandardButton.No)
836 if isinstance(btn_no, str):
837 btn = dialog.button(QMessageBox.StandardButton.No)
840 if btn_ok
is not None:
841 dialog.addButton(QMessageBox.StandardButton.Ok)
842 if isinstance(btn_ok, str):
843 btn = dialog.button(QMessageBox.StandardButton.Ok)
846 if btn_close
is not None:
847 dialog.addButton(QMessageBox.StandardButton.Close)
848 if isinstance(btn_close, str):
849 btn = dialog.button(QMessageBox.StandardButton.Close)
851 btn.setText(btn_close)
852 if btn_cancel
is not None:
853 dialog.addButton(QMessageBox.StandardButton.Cancel)
854 if isinstance(btn_cancel, str):
855 btn = dialog.button(QMessageBox.StandardButton.Cancel)
857 btn.setText(btn_cancel)
858 if btn_special
is not None:
859 if isinstance(btn_special, str):
860 dialog.addButton(btn_special, QMessageBox.ButtonRole.ResetRole)
861 if detailed_text
is not None:
862 dialog.setDetailedText(detailed_text)
863 if check_box
is not None:
864 box = QCheckBox(check_box)
865 dialog.setCheckBox(box)
869 choice = dialog.exec()
871 is_checked = box.isChecked()
874 b_accept = choice
not in [QMessageBox.StandardButton.No, QMessageBox.StandardButton.Close, QMessageBox.StandardButton.Cancel]
878 return b_accept, is_checked
881def input_dialog(parent:
"MainWindow | None", title: str, label: str, default_input: str =
"", show_input: bool =
True,
882 password_input: bool =
False, icon: Optional[str] =
None) -> tuple[str, bool]:
884 @brief Open input dialog
885 @param parent : parent window controller
886 @param title : dialog title
887 @param label : dialog label
888 @param default_input : default input text
889 @param show_input : show input text status
890 @param password_input : show input text as password status
891 @param icon : dialog icon
893 @todo Button Text an Sprachauswahl anpassen
898 echo = QLineEdit.EchoMode.Password
900 echo = QLineEdit.EchoMode.Normal
902 echo = QLineEdit.EchoMode.NoEcho
903 input_text, ok = QInputDialog.getText(parent, title, label, echo=echo, text=default_input)
904 return input_text, bool(ok)
907def open_directory(parent:
"MainWindow", title: str, directory: str |
None) -> str:
909 @brief Open directory
910 @param parent : parent window controller
911 @param title : dialog title
912 @param directory : open file dialog in this directory
913 @return selected path
915 s_path = QFileDialog.getExistingDirectory(parent=parent,
921def open_file(parent:
"MainWindow", title: str, directory: Optional[str] =
None,
922 filetypes: Optional[tuple[str, str]] =
None, multiple: bool =
False) -> str | list[str]:
925 @param parent : parent window controller
926 @param title : dialog title
927 @param directory : open file dialog in this directory
928 @param filetypes : file types to select
929 @param multiple : status if multiple files can selected
930 @return selected file
932 file_dialog = QFileDialog.getOpenFileNames
if multiple
else QFileDialog.getOpenFileName
933 if filetypes
is not None:
934 filter_name, filter_type = filetypes
935 type_filter = f
"{filter_name} ({filter_type})"
938 s_file_name_path, _ = file_dialog(parent=parent, caption=title,
941 return s_file_name_path
944def save_file(parent:
"MainWindow", title: str, directory: str, filetypes: tuple[str, str]) -> str:
947 @param parent : parent window controller
948 @param title : dialog title
949 @param directory : open file dialog in this directory
950 @param filetypes : file type to save
951 @return path to save file
953 filter_name, filter_type = filetypes
954 s_file_name_path, _ = QFileDialog.getSaveFileName(parent=parent, caption=title,
956 filter=f
"{filter_name} ({filter_type})")
957 return s_file_name_path
967 @param sound_path : path to sound file
972 sound = pygame.mixer.Sound(sound_path)
982 @brief Copy text to clipboard
983 @param text : text to copy
985 cb = QApplication.clipboard()
998 @param parent : parent window
999 @param callback : timeout callback function
1003 timer = QTimer(parent)
1004 timer.setSingleShot(
True)
1005 if callback
is not None:
1006 timer.timeout.connect(callback)
1014def config_icon(widget: QLabel, icon: Optional[str] =
None, icon_size: Optional[tuple[int, int]] =
None) ->
None:
1016 @brief Configure icon
1017 @param widget : set icon of this widget
1018 @param icon : icon to set
1019 @param icon_size : icon size
1021 if icon
is not None:
1022 widget.setPixmap(QPixmap(icon))
1027 @brief Create Stylesheet
1028 @param widget : get style of this widget
1031 style: str = widget.styleSheet()
1038 @param font_style : font style
1039 @param font_size : font size
1042 font = QFont(font_style, font_size)