Bootstrap

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)

;