import os import sys from PyQt5.QtCore import QUrl, QSize, Qt from PyQt5.QtGui import QIcon, QDesktopServices from PyQt5.QtWidgets import ( QApplication, QDialog, QHBoxLayout, QLabel, QPushButton, QVBoxLayout, QStackedWidget, QScrollArea, QFrame, QWidget ) from ...i18n.translator import get_translation from ..main_window import get_app_icon_object from ...utils.resolution import get_dark_theme class TourStepWidget(QWidget): """ A custom widget representing a single step or page in the feature guide. It neatly formats a title and its corresponding content. """ def __init__(self, title_text, content_text, parent=None, scale=1.0): super().__init__(parent) layout = QVBoxLayout(self) layout.setContentsMargins(20, 20, 20, 20) layout.setSpacing(10) title_font_size = int(14 * scale) content_font_size = int(11 * scale) title_label = QLabel(title_text) title_label.setAlignment(Qt.AlignCenter) title_label.setStyleSheet(f"font-size: {title_font_size}pt; font-weight: bold; color: #E0E0E0; padding-bottom: 15px;") layout.addWidget(title_label) scroll_area = QScrollArea() scroll_area.setWidgetResizable(True) scroll_area.setFrameShape(QFrame.NoFrame) scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) scroll_area.setStyleSheet("background-color: transparent;") content_label = QLabel(content_text) content_label.setWordWrap(True) content_label.setAlignment(Qt.AlignLeft | Qt.AlignTop) content_label.setTextFormat(Qt.RichText) content_label.setOpenExternalLinks(True) content_label.setStyleSheet(f"font-size: {content_font_size}pt; color: #C8C8C8; line-height: 1.8;") scroll_area.setWidget(content_label) layout.addWidget(scroll_area, 1) class HelpGuideDialog (QDialog ): """A multi-page dialog for displaying the feature guide.""" def __init__ (self ,steps_data ,parent_app ,parent =None ): super ().__init__ (parent ) self .current_step =0 self .steps_data =steps_data self .parent_app =parent_app scale = self.parent_app.scale_factor if hasattr(self.parent_app, 'scale_factor') else 1.0 app_icon = get_app_icon_object() if app_icon and not app_icon.isNull(): self.setWindowIcon(app_icon) self.setModal(True) self.resize(int(650 * scale), int(600 * scale)) dialog_font_size = int(11 * scale) current_theme_style = "" if hasattr(self.parent_app, 'current_theme') and self.parent_app.current_theme == "dark": current_theme_style = get_dark_theme(scale) else: current_theme_style = f""" QDialog {{ background-color: #F0F0F0; border: 1px solid #B0B0B0; }} QLabel {{ color: #1E1E1E; }} QPushButton {{ background-color: #E1E1E1; color: #1E1E1E; border: 1px solid #ADADAD; padding: {int(8*scale)}px {int(15*scale)}px; border-radius: 4px; min-height: {int(25*scale)}px; font-size: {dialog_font_size}pt; }} QPushButton:hover {{ background-color: #CACACA; }} QPushButton:pressed {{ background-color: #B0B0B0; }} """ self.setStyleSheet(current_theme_style) self ._init_ui () if self .parent_app : self .move (self .parent_app .geometry ().center ()-self .rect ().center ()) def _tr (self ,key ,default_text =""): """Helper to get translation based on current app language.""" if callable (get_translation )and self .parent_app : return get_translation (self .parent_app .current_selected_language ,key ,default_text ) return default_text def _init_ui (self ): main_layout =QVBoxLayout (self ) main_layout .setContentsMargins (0 ,0 ,0 ,0 ) main_layout .setSpacing (0 ) self .stacked_widget =QStackedWidget () main_layout .addWidget (self .stacked_widget ,1 ) self .tour_steps_widgets =[] scale = self.parent_app.scale_factor if hasattr(self.parent_app, 'scale_factor') else 1.0 for title, content in self.steps_data: step_widget = TourStepWidget(title, content, scale=scale) self.tour_steps_widgets.append(step_widget) self.stacked_widget.addWidget(step_widget) self .setWindowTitle (self ._tr ("help_guide_dialog_title","Kemono Downloader - Feature Guide")) buttons_layout =QHBoxLayout () buttons_layout .setContentsMargins (15 ,10 ,15 ,15 ) buttons_layout .setSpacing (10 ) self .back_button =QPushButton (self ._tr ("tour_dialog_back_button","Back")) self .back_button .clicked .connect (self ._previous_step ) self .back_button .setEnabled (False ) if getattr (sys ,'frozen',False )and hasattr (sys ,'_MEIPASS'): assets_base_dir =sys ._MEIPASS else : assets_base_dir =os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..')) github_icon_path =os .path .join (assets_base_dir ,"assets","github.png") instagram_icon_path =os .path .join (assets_base_dir ,"assets","instagram.png") discord_icon_path =os .path .join (assets_base_dir ,"assets","discord.png") self .github_button =QPushButton (QIcon (github_icon_path ),"") self .instagram_button =QPushButton (QIcon (instagram_icon_path ),"") self .Discord_button =QPushButton (QIcon (discord_icon_path ),"") scale = self.parent_app.scale_factor if hasattr(self.parent_app, 'scale_factor') else 1.0 icon_dim = int(24 * scale) icon_size = QSize(icon_dim, icon_dim) self .github_button .setIconSize (icon_size ) self .instagram_button .setIconSize (icon_size ) self .Discord_button .setIconSize (icon_size ) self .next_button =QPushButton (self ._tr ("tour_dialog_next_button","Next")) self .next_button .clicked .connect (self ._next_step_action ) self .next_button .setDefault (True ) self .github_button .clicked .connect (self ._open_github_link ) self .instagram_button .clicked .connect (self ._open_instagram_link ) self .Discord_button .clicked .connect (self ._open_Discord_link ) self .github_button .setToolTip (self ._tr ("help_guide_github_tooltip","Visit project's GitHub page (Opens in browser)")) self .instagram_button .setToolTip (self ._tr ("help_guide_instagram_tooltip","Visit our Instagram page (Opens in browser)")) self .Discord_button .setToolTip (self ._tr ("help_guide_discord_tooltip","Visit our Discord community (Opens in browser)")) social_layout =QHBoxLayout () social_layout .setSpacing (10 ) social_layout .addWidget (self .github_button ) social_layout .addWidget (self .instagram_button ) social_layout .addWidget (self .Discord_button ) while buttons_layout .count (): item =buttons_layout .takeAt (0 ) if item .widget (): item .widget ().setParent (None ) elif item .layout (): pass buttons_layout .addLayout (social_layout ) buttons_layout .addStretch (1 ) buttons_layout .addWidget (self .back_button ) buttons_layout .addWidget (self .next_button ) main_layout .addLayout (buttons_layout ) self ._update_button_states () def _next_step_action (self ): if self .current_step 0 : self .current_step -=1 self .stacked_widget .setCurrentIndex (self .current_step ) self ._update_button_states () def _update_button_states (self ): if self .current_step ==len (self .tour_steps_widgets )-1 : self .next_button .setText (self ._tr ("tour_dialog_finish_button","Finish")) else : self .next_button .setText (self ._tr ("tour_dialog_next_button","Next")) self .back_button .setEnabled (self .current_step >0 ) def _open_github_link (self ): QDesktopServices .openUrl (QUrl ("https://github.com/Yuvi9587")) def _open_instagram_link (self ): QDesktopServices .openUrl (QUrl ("https://www.instagram.com/uvi.arts/")) def _open_Discord_link (self ): QDesktopServices .openUrl (QUrl ("https://discord.gg/BqP64XTdJN"))