Python运用PySide6/PyQt制作精美界面,创意解析

PyThon运用PySide6/PyQt居然可以制作如此好看的界面——““创意解析””

导语:

你将获取以下知识:

  • 相关控件:

    QWidget
    QLineEidt
    QTableWidget
    QLabel
    QPushButton
    
  • Qss美化

  • 多线程与信号

  • 界面风格:

    圆角,简约,暗夜模式

    主界面分析:

    请添加图片描述

    1. 窗口标题栏被替换
    2. 内容区由搜索框和快捷栏组成

    窗口界面详解:

    1.首先 (标题栏):

    先将默认的标题栏去掉

            self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.FramelessWindowHint) # 设置无边框
            self.setAttribute(Qt.WA_TranslucentBackground) # 设置窗口背景透明
    

    其次通过Qss实现圆角需要paintEvent的支持

    def paintEvent(self, event):
        opt = QStyleOption()
        opt.initFrom(self)
        p = QPainter(self)
        self.style().drawPrimitive(QStyle.PrimitiveElement.PE_Widget,opt,p,self)
    
        super().paintEvent(event)
    

    接着我们开始重实现标题栏
    右边由:QLabel构成 左边由:QPushButton构成

    值得留意的是Qss背景不支持?

    self.setAttribute(Qt.WA_StyledBackground, True)  # 支持qss设置背景
    

    重实现 标题栏拖拽窗口移动

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        if event.button() == Qt.MouseButton.LeftButton:
            self._press = True
            self.mouseStartPos = event.globalPos()	# 获取点击屏幕坐标
            self.windowTopLeftPos = self.mapToGlobal(self.frameGeometry().topLeft()) # 获取窗口左上角屏幕坐标
    
    def mouseMoveEvent(self, event):
        super().mouseMoveEvent(event)
        if self._press:
            distance = event.globalPos() - self.mouseStartPos # 计算移动距离
            self.window().move(distance + self.windowTopLeftPos) # 移动窗口
    
    def mouseReleaseEvent(self, event):
        super().mouseReleaseEvent(event)
        if self._press:
            self._press = False
            
    # 注意坐标系应相当于屏幕坐标系
    
    2.其次 (搜索框):

    主要是对QLineEidt的样式设置和PaintEvent的重写

    def paintEvent(self, event):
        super().paintEvent(event)
    
        painter = QPainter(self)
    
        img = QPixmap('./img/find.png').scaled(34,34,Qt.AspectRatioMode.IgnoreAspectRatio, Qt.TransformationMode.SmoothTransformation)
        painter.drawPixmap(
            600-25-18,2, img)
        
    # Qss
    QLineEdit{
            color: white;
            padding-left: 12px;
            padding-right: 40px;
            border: 2px solid gray;
            border-radius: 20px;
            background-color: #282c35;
        }
    

    至于QLineEdit的搜索按钮点击事件,通过重写mousePressEvent实现,详解源代码

    3.最后 (快捷栏):

    它由QTableWidet构成,单元格由两个QLabel构成

    值得注意点是:QTableWidget设置样式(stylesheet)后,单元格行高列宽会失效,解决方法如下:

    self.horizontalHeader().setDefaultSectionSize(int)
    self.verticalHeader().setDefaultSectionSize(int)
    

    标题和图标,通过爬虫获取

    from PIL import Image
    from urllib.parse import urlsplit
    import requests as rq
    
    def getIcon(url:str):
       # 知识点:网址通常开放 主机名 + “/favicon.ico” 为图标api
       parser_url = urlsplit(url)
       netloc = parser_url.netloc	# 获取主机名
       icon_url = parser_url.scheme + '://' + netloc + "/favicon.ico"
       savePath = f'./img/{netloc.replace(".","_")}.ico'
       if not os.path.exists(savePath):
           img = Image.open(BytesIO(rq.get(icon_url,headers=HEADERS,verify=False).content))
           if img.width < 64 or img.height < 64:
               img = img.resize((64,64))
           img.save(savePath,sizes=[(64,64)])
       return netloc.replace(".","_")
    
    # 获取标题部分详见源代码
    

    由于爬虫会阻塞主线程,所以使用多线程加载是个好方法

    from threading import Thread
    
    currentThreading = Thread(target=self.cellInit,
                                         args=(row,index,DEFAULTURLS[key],key,))
    currentThreading.start()
    
    class shortcutsBar(QTableWidget):
       addCell = Signal(int,int,str,str)
       def __init__(self, parent=None):
           super().__init__(parent)
           self.bind()
           self.initSetup()
    	
       def bind(self):
           self.addCell.connect(self.setCellEvent)
    	
       def initSetup(self):
           # 详解源代码
           .......
           
    		DEFAULTURLS ={'SteamWorkShop': 'https://steamcommunity.com/', 'Watt Toolkit': 'https://steampp.net/'}
           for index,key in enumerate(DEFAULTURLS.keys()):
               row = 0
               if index > 5:
                   index -= 5
                   row = 1
    
               currentThreading = Thread(target=self.cellInit,
                                         args=(row,index,DEFAULTURLS[key],key,))
               currentThreading.start()
       
       def cellInit(self,row:int,col:int,url:str,text:str):
           img = getIcon(url)
           self.addCell.emit(row,col,img,text)
    
       @Slot(int,int,str,str)
       def setCellEvent(self,row:int,col:int,img:str,text:str):
           cell = QWidget()
           cellLayout = QVBoxLayout(cell)
           cellText = QLabel(text)
           cellText.setFont(QFont('微软雅黑',12))
           cellText.setStyleSheet('color: #ffffff')
    
           cellIcon = iconLabel(f'./img/{img}.ico',text)
    
           cellLayout.addWidget(cellIcon,alignment=Qt.AlignmentFlag.AlignCenter)
           cellLayout.addWidget(cellText,alignment=Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignBottom)
    
           self.setCellWidget(row,col,cell)
    

    这里采用原生多线程 + Signal 的原因是:在多线程中操作任何UI容易造成软件崩溃
    如果是连续往UI添加东西要给界面留绘制时间 (sleep)

    作者:漫天白梦

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python运用PySide6/PyQt制作精美界面,创意解析

    发表回复