# coding:utf-8 import sys from PyQt5.QtCore import Qt, QRect from PyQt5.QtGui import QIcon, QPainter, QImage, QBrush, QColor, QFont from PyQt5.QtWidgets import QApplication, QFrame, QStackedWidget, QHBoxLayout, QLabel from qfluentwidgets import NavigationInterface, NavigationItemPostion, NavigationWidget, MessageBox from qfluentwidgets import FluentIconFactory as FIF from qframelesswindow import FramelessWindow, StandardTitleBar class Widget(QFrame): def __init__(self, text: str, parent=None): super().__init__(parent=parent) self.label = QLabel(text, self) self.label.setAlignment(Qt.AlignCenter) self.hBoxLayout = QHBoxLayout(self) self.hBoxLayout.addWidget(self.label, 1, Qt.AlignCenter) self.setObjectName(text.replace(' ', '-')) class AvatarWidget(NavigationWidget): """ Avatar widget """ def __init__(self, parent=None): super().__init__(isSelectable=False, parent=parent) self.avatar = QImage('resource/shoko.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.isSelected: painter.setBrush(QColor(0, 0, 0, 6 if self.isEnter else 10)) painter.drawRoundedRect(self.rect(), 5, 5) # draw indicator painter.setBrush(QColor(0, 153, 188)) painter.drawRoundedRect(0, 10, 3, 16, 1.5, 1.5) elif self.isEnter: painter.setBrush(QColor(0, 0, 0, 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.black) font = QFont('Segoe UI') font.setPixelSize(14) painter.setFont(font) painter.drawText(QRect(44, 0, 255, 36), Qt.AlignVCenter, 'zhiyiYo') class Window(FramelessWindow): def __init__(self): super().__init__() self.setTitleBar(StandardTitleBar(self)) self.hBoxLayout = QHBoxLayout(self) self.navigationInterface = NavigationInterface(self, True) self.stackWidget = QStackedWidget(self) # create sub interface self.searchInterface = Widget('Search Interface', self) self.musicInterface = Widget('Music Interface', self) self.videoInterface = Widget('Video Interface', self) self.folderInterface = Widget('Folder Interface', self) self.settingInterface = Widget('Setting Interface', self) self.stackWidget.addWidget(self.searchInterface) self.stackWidget.addWidget(self.musicInterface) self.stackWidget.addWidget(self.videoInterface) self.stackWidget.addWidget(self.folderInterface) self.stackWidget.addWidget(self.settingInterface) # initialize layout self.hBoxLayout.setSpacing(0) self.hBoxLayout.setContentsMargins(0, self.titleBar.height(), 0, 0) self.hBoxLayout.addWidget(self.navigationInterface) self.hBoxLayout.addWidget(self.stackWidget) self.hBoxLayout.setStretchFactor(self.stackWidget, 1) # add items to navigation interface self.navigationInterface.addItem( routeKey=self.searchInterface.objectName(), iconPath=FIF.path(FIF.SEARCH), text='Search', onClick=lambda: self.switchTo(self.searchInterface) ) self.navigationInterface.addItem( routeKey=self.musicInterface.objectName(), iconPath=FIF.path(FIF.MUSIC), text='Music library', onClick=lambda: self.switchTo(self.musicInterface) ) self.navigationInterface.addItem( routeKey=self.navigationInterface.objectName(), iconPath=FIF.path(FIF.VIDEO), text='Video library', onClick=lambda: self.switchTo(self.videoInterface) ) self.navigationInterface.addSeparator() # add navigation items to scroll area self.navigationInterface.addItem( routeKey='folder', iconPath=FIF.path(FIF.FOLDER), text='Folder library', onClick=lambda: self.switchTo(self.folderInterface), position=NavigationItemPostion.SCROLL ) # for i in range(1, 21): # self.navigationInterface.addItem( # f'folder{i}', # FIF.path(FIF.FOLDER), # f'Folder {i}', # lambda: print('Folder clicked'), # position=NavigationItemPostion.SCROLL # ) # add custom widget to bottom self.navigationInterface.addWidget( routeKey='avatar', widget=AvatarWidget(), onClick=self.showMessageBox, position=NavigationItemPostion.BOTTOM ) self.navigationInterface.addItem( routeKey='setting', iconPath=FIF.path(FIF.SETTING), text='Settings', onClick=lambda: self.switchTo(self.settingInterface), position=NavigationItemPostion.BOTTOM ) self.stackWidget.currentChanged.connect(self.onCurrentInterfaceChanged) self.stackWidget.setCurrentIndex(1) with open('resource/demo.qss', encoding='utf-8') as f: self.setStyleSheet(f.read()) self.resize(900, 700) self.setWindowIcon(QIcon('resource/logo.png')) self.setWindowTitle('PyQt-Fluent-Widgets') desktop = QApplication.desktop().availableGeometry() w, h = desktop.width(), desktop.height() self.move(w//2 - self.width()//2, h//2 - self.height()//2) def switchTo(self, widget): self.stackWidget.setCurrentWidget(widget) def onCurrentInterfaceChanged(self, index): widget = self.stackWidget.widget(index) self.navigationInterface.setCurrentItem(widget.objectName()) def showMessageBox(self): w = MessageBox( 'This is a help message', 'You clicked a customized navigation widget. You can add more custom widgets by calling `NavigationInterface.addWidget()` 😉', self ) w.exec() 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_()