zhiyiYo / PyQt-Fluent-Widgets

A fluent design widgets library based on C++ Qt/PyQt/PySide. Make Qt Great Again.
https://qfluentwidgets.com
GNU General Public License v3.0
5.68k stars 550 forks source link

[Bug]: 导航栏返回功能 #794

Closed sciolism-9 closed 7 months ago

sciolism-9 commented 7 months ago

What happened?

给当前 导航栏a 添加 子栏b ,再在这个 子栏b 中添加 子栏c ,然后点击回到 导航栏a ,更新子栏b 为子栏B,再在 子栏B 中更新 子栏c 为 子栏C ,此时在子栏C页面按下导航栏的返回按钮,发现没有回到子栏B,而是回到子栏b

https://github.com/zhiyiYo/PyQt-Fluent-Widgets/assets/67938359/4b5e1d44-3908-4741-b9d1-2ea12c45da60

Operation System

Windows10

Python Version

3.8.18 64bit

PyQt/PySide Version

pyqt 5.15.10

PyQt/PySide-Fluent-Widgets Version

v1.5.1

How to Reproduce?

添加子栏

def add_first_subInterface(self, value_list):
        if not self.first_subInterface == None:
                    self.navigationInterface.removeWidget(
                        self.first_subInterface.objectName())
        self.first_subInterface = value_list[0]
        icon = value_list[1]
        text = value_list[2]
        position = NavigationItemPosition.SCROLL
        self.stackWidget.addWidget(self.first_subInterface)
        self.first_subInterface.add_interface.connect(
            self.add_second_subInterface)
        self.navigationInterface.addItem(
            routeKey=self.first_subInterface.objectName(),
            icon=icon,
            text=text,
            onClick=lambda: self.switchTo(self.first_subInterface),
            position=position,
            tooltip=text,
            parentRouteKey=self.folderInterface.objectName()
        )
        self.navigationInterface.widget(
            self.first_subInterface.objectName()).click()

Minimum code

# coding:utf-8
import sys
from PyQt5.QtCore import Qt, QRect, QUrl, pyqtSignal
from PyQt5.QtGui import QIcon, QPainter, QImage, QBrush, QColor, QFont, QDesktopServices
from PyQt5.QtWidgets import QApplication, QFrame, QStackedWidget, QHBoxLayout, QLabel

from qfluentwidgets import (NavigationInterface, NavigationItemPosition, NavigationWidget, MessageBox, LineEdit, PushButton,
                            isDarkTheme, setTheme, Theme, qrouter)
from qfluentwidgets import FluentIcon as FIF
from qframelesswindow import FramelessWindow, TitleBar

# 导航栏a
class Widget(QFrame):
    add_interface = pyqtSignal(list)

    def __init__(self, text: str, parent=None):
        super().__init__(parent=parent)
        self.setObjectName("widget")
        self.label = LineEdit(self)
        self.label.setText(text)
        self.label.adjustSize()
        self.label.setAlignment(Qt.AlignCenter)

        self.btn = PushButton("click", self)

        self.hBoxLayout = QHBoxLayout(self)
        self.hBoxLayout.addWidget(self.label, 1, Qt.AlignCenter)
        self.hBoxLayout.addWidget(self.btn, 1, Qt.AlignCenter)

        self.btn.clicked.connect(self.send)

        # leave some space for title bar
        self.hBoxLayout.setContentsMargins(0, 32, 0, 0)

    def send(self):
        w = Widget_2("first_"+self.label.text())
        self.add_interface.emit(
            [w, FIF.FOLDER, "first_"+self.label.text()])

# 子栏b、B
class Widget_2(QFrame):
    add_interface = pyqtSignal(list)

    def __init__(self, text: str, parent=None):
        super().__init__(parent=parent)
        self.setObjectName("widget_2")
        self.label = LineEdit(self)
        self.label.setText(text)
        self.label.adjustSize()
        self.label.setAlignment(Qt.AlignCenter)

        self.btn = PushButton("click", self)

        self.hBoxLayout = QHBoxLayout(self)
        self.hBoxLayout.addWidget(self.label, 1, Qt.AlignCenter)
        self.hBoxLayout.addWidget(self.btn, 1, Qt.AlignCenter)

        self.btn.clicked.connect(self.send)

        # leave some space for title bar
        self.hBoxLayout.setContentsMargins(0, 32, 0, 0)

    def send(self):
        w = Widget_3("second_"+self.label.text())
        self.add_interface.emit(
            [w, FIF.FOLDER, "second_"+self.label.text()])

# 子栏c、C
class Widget_3(QFrame):
    add_interface = pyqtSignal(list)

    def __init__(self, text: str, parent=None):
        super().__init__(parent=parent)
        self.setObjectName("widget_3")
        self.label = LineEdit(self)
        self.label.setText(text)
        self.label.adjustSize()
        self.label.setAlignment(Qt.AlignCenter)

        self.hBoxLayout = QHBoxLayout(self)
        self.hBoxLayout.addWidget(self.label, 1, Qt.AlignCenter)

        # leave some space for title bar
        self.hBoxLayout.setContentsMargins(0, 32, 0, 0)

class AvatarWidget(NavigationWidget):
    """ Avatar widget """

    def __init__(self, parent=None):
        super().__init__(isSelectable=False, parent=parent)
        self.avatar = QImage('resource/logo.png').scaled(
            24, 24, Qt.KeepAspectRatio, Qt.SmoothTransformation)

    def paintEvent(self, e):
        painter = QPainter(self)
        painter.setRenderHints(
            QPainter.SmoothPixmapTransform | QPainter.Antialiasing)

        painter.setPen(Qt.NoPen)

        if self.isPressed:
            painter.setOpacity(0.7)

        # draw background
        if self.isEnter:
            c = 255 if isDarkTheme() else 0
            painter.setBrush(QColor(c, c, c, 10))
            painter.drawRoundedRect(self.rect(), 5, 5)

        # draw avatar
        painter.setBrush(QBrush(self.avatar))
        painter.translate(8, 6)
        painter.drawEllipse(0, 0, 24, 24)
        painter.translate(-8, -6)

        if not self.isCompacted:
            painter.setPen(Qt.white if isDarkTheme() else Qt.black)
            font = QFont('Segoe UI')
            font.setPixelSize(14)
            painter.setFont(font)
            painter.drawText(QRect(44, 0, 255, 36), Qt.AlignVCenter, 'zhiyiYo')

class CustomTitleBar(TitleBar):
    """ Title bar with icon and title """

    def __init__(self, parent):
        super().__init__(parent)
        # add window icon
        self.iconLabel = QLabel(self)
        self.iconLabel.setFixedSize(18, 18)
        self.hBoxLayout.insertSpacing(0, 10)
        self.hBoxLayout.insertWidget(
            1, self.iconLabel, 0, Qt.AlignLeft | Qt.AlignBottom)
        self.window().windowIconChanged.connect(self.setIcon)

        # add title label
        self.titleLabel = QLabel(self)
        self.hBoxLayout.insertWidget(
            2, self.titleLabel, 0, Qt.AlignLeft | Qt.AlignBottom)
        self.titleLabel.setObjectName('titleLabel')
        self.window().windowTitleChanged.connect(self.setTitle)

    def setTitle(self, title):
        self.titleLabel.setText(title)
        self.titleLabel.adjustSize()

    def setIcon(self, icon):
        self.iconLabel.setPixmap(QIcon(icon).pixmap(18, 18))

class Window(FramelessWindow):

    def __init__(self):
        super().__init__()
        self.setTitleBar(CustomTitleBar(self))

        self.hBoxLayout = QHBoxLayout(self)
        self.navigationInterface = NavigationInterface(
            self, showMenuButton=True, showReturnButton=True)
        self.stackWidget = QStackedWidget(self)

        # create sub interface

        self.folderInterface = Widget('1', self)
        self.folderInterface.add_interface.connect(self.add_first_subInterface)

        self.first_subInterface = None
        self.second_subInterface = None

        self.settingInterface = Widget('Setting Interface', self)

        # initialize layout
        self.initLayout()

        # add items to navigation interface
        self.initNavigation()

        self.initWindow()

    def initLayout(self):
        self.hBoxLayout.setSpacing(0)
        self.hBoxLayout.setContentsMargins(0, 0, 0, 0)
        self.hBoxLayout.addWidget(self.navigationInterface)
        self.hBoxLayout.addWidget(self.stackWidget)
        self.hBoxLayout.setStretchFactor(self.stackWidget, 1)

        self.titleBar.raise_()
        self.navigationInterface.displayModeChanged.connect(
            self.titleBar.raise_)

    def initNavigation(self):

        self.navigationInterface.addSeparator()

        # add navigation items to scroll area
        self.addSubInterface(self.folderInterface, FIF.FOLDER,
                             'Folder library', NavigationItemPosition.SCROLL)

        # add custom widget to bottom
        self.navigationInterface.addWidget(
            routeKey='avatar',
            widget=AvatarWidget(),
            onClick=self.showMessageBox,
            position=NavigationItemPosition.BOTTOM
        )

        self.addSubInterface(self.settingInterface, FIF.SETTING,
                             'Settings', NavigationItemPosition.BOTTOM)

        qrouter.setDefaultRouteKey(
            self.stackWidget, self.folderInterface.objectName())

        self.stackWidget.currentChanged.connect(self.onCurrentInterfaceChanged)
        self.stackWidget.setCurrentIndex(0)

    def initWindow(self):
        self.resize(900, 700)
        self.setWindowIcon(QIcon('resource/logo.png'))
        self.setWindowTitle('PyQt-Fluent-Widgets')
        self.titleBar.setAttribute(Qt.WA_StyledBackground)

        desktop = QApplication.desktop().availableGeometry()
        w, h = desktop.width(), desktop.height()
        self.move(w//2 - self.width()//2, h//2 - self.height()//2)

        self.setQss()

    def addSubInterface(self, interface, icon, text: str, position=NavigationItemPosition.TOP):
        """ add sub interface """
        self.stackWidget.addWidget(interface)
        self.navigationInterface.addItem(
            routeKey=interface.objectName(),
            icon=icon,
            text=text,
            onClick=lambda: self.switchTo(interface),
            position=position,
            tooltip=text
        )

    def add_first_subInterface(self, value_list):
        if not self.first_subInterface == None:
            self.navigationInterface.removeWidget(
                self.first_subInterface.objectName())

        self.first_subInterface = value_list[0]
        icon = value_list[1]
        text = value_list[2]

        position = NavigationItemPosition.SCROLL
        self.stackWidget.addWidget(self.first_subInterface)

        self.first_subInterface.add_interface.connect(
            self.add_second_subInterface)

        self.navigationInterface.addItem(
            routeKey=self.first_subInterface.objectName(),
            icon=icon,
            text=text,
            onClick=lambda: self.switchTo(self.first_subInterface),
            position=position,
            tooltip=text,
            parentRouteKey=self.folderInterface.objectName()
        )
        self.navigationInterface.update()
        self.navigationInterface.widget(
            self.first_subInterface.objectName()).click()

    def add_second_subInterface(self, value_list):
        if not self.second_subInterface == None:
            self.navigationInterface.removeWidget(
                self.second_subInterface.objectName())

        self.second_subInterface = value_list[0]
        icon = value_list[1]
        text = value_list[2]

        position = NavigationItemPosition.SCROLL
        self.stackWidget.addWidget(self.second_subInterface)

        self.navigationInterface.addItem(
            routeKey=self.second_subInterface.objectName(),
            icon=icon,
            text=text,
            onClick=lambda: self.switchTo(self.second_subInterface),
            position=position,
            tooltip=text,
            parentRouteKey=self.first_subInterface.objectName()
        )
        self.navigationInterface.widget(
            self.second_subInterface.objectName()).click()

    def setQss(self):
        color = 'dark' if isDarkTheme() else 'light'
        with open(f'resource/{color}/demo.qss', encoding='utf-8') as f:
            self.setStyleSheet(f.read())

    def switchTo(self, widget):
        self.stackWidget.setCurrentWidget(widget)

    def onCurrentInterfaceChanged(self, index):
        widget = self.stackWidget.widget(index)
        self.navigationInterface.setCurrentItem(widget.objectName())
        qrouter.push(self.stackWidget, widget.objectName())

    def showMessageBox(self):
        w = MessageBox(
            '支持作者🥰',
            '个人开发不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护项目的动力🚀',
            self
        )
        w.yesButton.setText('来啦老弟')
        w.cancelButton.setText('下次一定')

        if w.exec():
            QDesktopServices.openUrl(QUrl("https://afdian.net/a/zhiyiYo"))

    def resizeEvent(self, e):
        self.titleBar.move(46, 0)
        self.titleBar.resize(self.width()-46, self.titleBar.height())

if __name__ == '__main__':
    QApplication.setHighDpiScaleFactorRoundingPolicy(
        Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)

    app = QApplication(sys.argv)
    w = Window()
    w.show()
    app.exec_()
sciolism-9 commented 7 months ago

解决方法,每次更新子栏前因为需要实例化对应界面,在objectName添加时间戳来区分:self.first_subInterface.setObjectName(f"{self.firstsubInterface.objectName()}{time.time()}")