PyQt5万能功能模板

  • Author:浅若清风cyf

QWidget与QLayout

父窗口创建子窗口

注意将子窗口对象声明为父窗口对象的成员,不能是函数中的局部变量,运行后会崩溃!

从layout中移除控件

self.layout.itemAt(i).widget().deleteLater()

Widget嵌入带布局的子组件

## 层次关系:widget→layout→widget
## 假设父widget为self.ui.father_widget
##
self.lay=QVBoxLayout(self.ui.father_widget)
self.subWidget=QWidget()
self.lay.addWidget(self.subWidget)

设为窗口为模态框

from PyQt5.QtCore import Qt

myWin = MainWin()
myWin.setWindowModality(Qt.ApplicationModal)

QMessageBox

基本使用

reply = QMessageBox.information(self, '标题','消息对话框正文',QMessageBox.Yes | QMessageBox.No,QMessageBox.Yes)
reply1 = QMessageBox.question(self, "标题", "提问框消息正文", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
reply2 = QMessageBox.warning(self, "标题", "警告框消息正文", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
reply3 = QMessageBox.critical(self, "标题", "严重错误对话框消息正文", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
reply4 = QMessageBox.about(self, "标题", "关于对话框消息正文")

按钮文字自定义

def showCustomMessageBox(self):
    save, discard, cancel = QMessageBox.Save, QMessageBox.Discard, QMessageBox.Cancel

    if self.config.language == 'zh_CN':
        msg = '图片未保存,是否保存'
        msg_box = QMessageBox(QMessageBox.Warning, '保存修改?', msg)
        icon = QIcon('./resources/logo.png')
        msg_box.setWindowIcon(icon
        msg_box.setStandardButtons(save|discard|cancel)
        btn_save = msg_box.button(QMessageBox.Save)
        btn_save.setText('保存')
        btn_discard=msg_box.button(QMessageBox.Discard)
        btn_discard.setText('不保存')
        btn_cancel=msg_box.button(QMessageBox.Cancel)
        btn_cancel.setText('取消')
        msg_box.exec_()

        if msg_box.clickedButton() == btn_cancel:
            print('点击取消')
            e.ignore()
        elif msg_box.clickedButton()==btn_discard:
            print('不保存')
            e.accept()
        elif msg_box.clickedButton()==btn_save:
            print('保存')
            if not self.saveFile():
                e.ignore()
                return
            e.accept()

        ## reply = QMessageBox.warning(self, '保存修改?', msg, save | discard | cancel, save)

    else:
        msg = 'Do you want to save the picture?'
        reply = QMessageBox.warning(self, 'Save changes?', msg, save | discard | cancel, save)
        if reply == QMessageBox.Cancel:
            e.ignore()
        elif reply == QMessageBox.Discard:
            e.accept()
        elif reply == QMessageBox.Save:
            if not self.saveFile():
                e.ignore()
                return
            e.accept()

定制版:支持中英文、自定义按钮、按钮美化

from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import Qt
import sys
import resources.resources_qt5

def message_win(title, msg, btn_list, default_btn, type='warning', language='zh_CN', icon=None):
    '''
    封装QMessageBox,支持按钮中文显示
    两种模式弹出对话框:1、自定义QMessageBox,2、继承parent
    '''

    style_true = '''
                width: 76px;
                height: 24px;
                background: rgba(0, 164, 255, 0.05);
                border: 1px solid rgba(0, 164, 255, 0.2);
                font-family: "Microsoft YaHei";
                font-weight: 400;
                font-size: 14px;
                color: ##00a4ff;
                text-align:center; 
    '''
    style_false = '''
        width: 76px;
        height: 24px;
        background: rgba(26, 155, 82, 0.05);
        border: 1px solid rgba(26, 155, 82, 0.2);
        font-family: "Microsoft YaHei";
        font-weight: 400;
        font-size: 14px;
        color: ##1a9b52;
        text-align:center; 
    '''
    style_cancel = '''
        width: 76px;
        height: 24px;
        background: rgba(80, 80, 80, 0.05);
        border: 1px solid rgba(80, 80, 80, 0.2);
        font-family: "Microsoft YaHei";
        font-weight: 400;
        font-size: 14px;
        color: ##5a5a5a;
        text-align:center; 
    '''

    ## if language == 'zh_CN':
    if type == 'warning':
        msg_box = QMessageBox(QMessageBox.Warning, title, msg)
    elif type == 'information':
        msg_box = QMessageBox(QMessageBox.Information, title, msg)
    elif type == 'critical':
        msg_box = QMessageBox(QMessageBox.Critical, title, msg)
    elif type == 'question':
        msg_box = QMessageBox(QMessageBox.Question, title, msg)
    else:
        return None

    if icon is None:
        my_icon = QIcon(':/logo2.png')
    else:
        my_icon = icon
    msg_box.setWindowIcon(my_icon)
    msg_box.setStandardButtons(btn_list)

    btn_save = msg_box.button(QMessageBox.Save)
    if btn_save is not None:
        if language == 'zh_CN':
            btn_save.setText('保存')
        btn_save.setStyleSheet(style_true)

    btn_discard = msg_box.button(QMessageBox.Discard)
    if btn_discard is not None:
        if language == 'zh_CN':
            btn_discard.setText('不保存')
        btn_discard.setStyleSheet(style_false)

    btn_cancel = msg_box.button(QMessageBox.Cancel)
    if btn_cancel is not None:
        if language == 'zh_CN':
            btn_cancel.setText('取消')
        btn_cancel.setStyleSheet(style_cancel)

    btn_ok = msg_box.button(QMessageBox.Ok)
    if btn_ok is not None:
        if language == 'zh_CN':
            btn_ok.setText('好的')
        btn_ok.setStyleSheet(style_true)

    btn_no = msg_box.button(QMessageBox.No)
    if btn_no is not None:
        if language == 'zh_CN':
            btn_no.setText('否')
        btn_no.setStyleSheet(style_false)

    btn_yes = msg_box.button(QMessageBox.Yes)
    if btn_yes is not None:
        if language == 'zh_CN':
            btn_yes.setText('是')
        btn_yes.setStyleSheet(style_true)

    msg_box.setDefaultButton(msg_box.button(default_btn))

    ## child_list = msg_box.children()
    ## for i in range(len(child_list)):
    ##     obj = child_list[i]
    ##     if obj.inherits("QWidget"):
    ##         obj.setStyleSheet("background-color: transparent;font-family: Microsoft YaHei;")

    style = '''
        background-color:white;
        font-family: "Microsoft YaHei";
        font-weight: 400;
        font-size: 14px;
        color: ##1a1a1a;
        text-align:center; 
    '''
    msg_box.setStyleSheet(style)
    ## msg_box.setWindowFlags(Qt.FramelessWindowHint)
    msg_box.exec_()

    if msg_box.clickedButton() == btn_ok:
        return QMessageBox.Ok
    elif msg_box.clickedButton() == btn_cancel:
        return QMessageBox.Cancel
    elif msg_box.clickedButton() == btn_discard:
        return QMessageBox.Discard
    elif msg_box.clickedButton() == btn_save:
        return QMessageBox.Save
    elif msg_box.clickedButton() == btn_yes:
        return QMessageBox.Yes
    elif msg_box.clickedButton() == btn_no:
        return QMessageBox.No

        ## reply = QMessageBox.warning(self, '保存修改?', msg, save | discard | cancel, save)

    ## else:
    ##     if type == 'warning':
    ##         return QMessageBox.warning(parent, title, msg, btn_list, default_btn)
    ##     elif type == 'information':
    ##         return QMessageBox.information(parent, title, msg, btn_list, default_btn)
    ##     elif type == 'critical':
    ##         return QMessageBox.critical(parent, title, msg, btn_list, default_btn)
    ##     else:
    ##         return None


class MyWin(QWidget):
    '''
    测试用
    '''

    def __init__(self):
        super(MyWin, self).__init__()
        ## self.resize(100, 100)
        icon = QIcon(':/logo2.png')
        self.setWindowIcon(icon)
        ## 继承parent的icon
        message_win(title='title', msg='msg', btn_list=QMessageBox.Yes | QMessageBox.No,
                    default_btn=QMessageBox.Yes, language='en')
        ## self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ## 中文
    reply = message_win(title='标题', msg='提示内容', btn_list=QMessageBox.Yes | QMessageBox.No|QMessageBox.Cancel,
                        default_btn=QMessageBox.Yes, language='zh_CN')
    print(reply)
    ##
    ## reply = message_win(title='标题', msg='提示内容', btn_list=QMessageBox.Yes | QMessageBox.No|QMessageBox.Cancel,
    ##                     default_btn=QMessageBox.Yes, language='zh_CN',type='information')
    ## print(reply)
    ##
    ## reply = message_win(title='标题', msg='提示内容', btn_list=QMessageBox.Yes | QMessageBox.No|QMessageBox.Cancel,
    ##                     default_btn=QMessageBox.Yes, language='zh_CN',type='critical')
    ## print(reply)
    ## 英文(不继承)
    reply = message_win( title='title', msg='msg', btn_list=QMessageBox.Yes | QMessageBox.No,
                        default_btn=QMessageBox.Yes, language='en')
    print(reply)
    ## 英文(继承)
    ## win = MyWin()
    sys.exit(app.exec_())

QLabel

设置图标

label = QLabel()
label.setPixmap(QtGui.QPixmap(":/resources/模型设置.png"))

QListWidget

image-20220505123808092

item可拖拽调整顺序、删除、双击弹出二级菜单、右键移动控件

'''
    包含可拖动item的QListWidget
    两种模式:mode=files或mode=dirs
    功能:
        拖拽调整item顺序
        右键菜单:上移、下移
        mode=dirs情况下:
            双击可弹窗,调整二级列表顺序
'''

from PyQt5.QtWidgets import QApplication, QListWidgetItem, QWidget, QHBoxLayout, QListWidget, QSpacerItem, QPushButton, \
    QSizePolicy, QMenu, QMessageBox
from PyQt5 import QtCore
import PyQt5.sip as sip
from PyQt5.QtCore import pyqtSignal
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt, QModelIndex


class FileDirListItemWidget(QWidget):
    signal_delItem = pyqtSignal(str)  ## 删除item信号
    signal_moveUp = pyqtSignal(str)  ## 上移item信号
    signal_moveDown = pyqtSignal(str)  ## 下移item信号

    def __init__(self, text, parent=None):
        super(FileDirListItemWidget, self).__init__(parent=parent)
        self.text = text
        self.setup()

    def setup(self):
        '''
        初始化itemWidget
        :return:
        '''
        ## 布局层(组件添加到layout上)
        self.layout = QHBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)

        ## 弹簧
        self.spacer = QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Expanding)

        ## 删除按钮
        self.del_btn = QPushButton("×")
        self.del_btn.setFixedWidth(30)
        self.del_btn.clicked.connect(self.on_delete)

        ## 右键菜单
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.custom_right_menu)

        ## 控件加入layout
        self.layout.addItem(self.spacer)
        self.layout.addWidget(self.del_btn)

    def on_delete(self):
        '''
        删除按钮的槽函数
        :return:
        '''
        self.signal_delItem.emit(self.text)  ## 向父组件传递item的text,父组件根据text删除item

    def custom_right_menu(self, pos):
        '''
        右键菜单
        :param pos:
        :return:
        '''
        menu = QMenu()
        opt1 = menu.addAction("上移")
        opt2 = menu.addAction("下移")

        action = menu.exec_(self.mapToGlobal(pos))

        if action == opt1:
            print('点击上移', self.text)
            self.signal_moveUp.emit(self.text)
        elif action == opt2:
            print('点击下移', self.text)
            self.signal_moveDown.emit(self.text)


class FileDirListWidget(QListWidget):
    '''
    QListWidget窗口,用于调整item(顺序、删除)
    '''
    signal_subLabelsWidgetClose = pyqtSignal(list)  ## 窗口关闭信号

    def __init__(self, labels=None, subLabels=None, mode='files'):
        '''
            mode可选:files、dirs
            dirs模式下,item双击可弹出新窗口,调整subLabels顺序
        :param labels:
        :param mode:
        '''
        super(FileDirListWidget, self).__init__()
        print('mode:', mode)
        print('labels=',labels)
        print('subLabels=',subLabels)

        self.labels = labels        ## files/dirs
        self.subLabels = subLabels  ## None/files
        self.mode = mode
        self.setup()
        self.setDragDropMode(QListWidget.InternalMove)  ## 设置item拖拽模式
        self.fileListWidget = None

    def setup(self):
        '''
        初始化:添加item
        :return:
        '''
        ## for label in self.labels:
        ##     self.addItemWidget(label)
        self.add_items()

        ## 事件绑定
        ## 双击item事件
        if self.mode == 'dirs':
            self.doubleClicked.connect(self.on_itemDoubleClicked)

    def add_items(self):
        '''
        向widget添加items
        :return:
        '''
        self.clear()
        print('self.labels=',self.labels)
        if self.labels is not None:
            for label in self.labels:
                self.addItemWidget(label)

    def del_Item(self, text):
        '''
        删除指定text的item
        :param text:
        :return:
        '''
        print('删除item:', text)
        for i in range(self.count()):
            item_i = self.item(i)
            if self.itemWidget(item_i).text == text:
                self.takeItem(i)
                sip.delete(item_i)
                self.labels.pop(i)  ## 删除索引为i的元素
                break
        print('当前的文件列表:', self.labels)

    def update_subLabels(self, labels):
        '''
        dirs模式下,返回子窗口调整顺序后的labels,并更新sublabels
        :return:
        '''
        print('调整后的的labels:', labels)
        print('准备更新subLabels...')
        self.subLabels[str(self.select_dir)]=labels
        print('更新后的文件顺序:',self.subLabels[str(self.select_dir)])
        ## print(self.subLabels)
        print('subLabels已更新...')


    def on_itemDoubleClicked(self, modelindex: QModelIndex):
        print(self.subLabels)
        item = self.item(modelindex.row())  ## 获取双击的item
        ## print(modelindex.row())
        dir_name = item.text()
        self.select_dir=dir_name
        print(dir_name)
        print(self.subLabels)
        print(self.subLabels.get(dir_name))
        labels = self.subLabels.get(dir_name)
        self.fileListWidget = FileDirListWidget(labels, 'files')
        self.fileListWidget.resize(400,300)
        self.fileListWidget.signal_subLabelsWidgetClose.connect(self.update_subLabels)
        self.fileListWidget.show()
        ## 重写窗口关闭事件,将调整后的file顺序返回

    def on_labelUp(self, text):
        '''
        点击的label上移
        :return:
        '''
        for i in range(len(self.labels)):
            if self.labels[i] == text:
                if i == 0:
                    print('选中第一个item,无法上移!')
                    res = QMessageBox.critical(self, "警告", "选中第一个文件,无法上移!", QMessageBox.Yes | QMessageBox.No,
                                               QMessageBox.Yes)
                    ## print(res)
                    return

                tmp = self.labels[i]
                self.labels[i] = self.labels[i - 1]
                self.labels[i - 1] = tmp
                ## 更新items
                self.add_items()
                break

    def on_labelDown(self, text):
        '''
        点击的label下移
        :return:
        '''
        for i in range(len(self.labels)):
            if self.labels[i] == text:
                if i == len(self.labels) - 1:
                    print('选中最后一个item,无法下移!')
                    res = QMessageBox.critical(self, "警告", "选中最后一个文件,无法下移!", QMessageBox.Yes | QMessageBox.No,
                                               QMessageBox.Yes)
                    ## print(res)
                    return

                tmp = self.labels[i]
                self.labels[i] = self.labels[i + 1]
                self.labels[i + 1] = tmp
                ## 更新items
                self.add_items()
                break

    def addItemWidget(self, label):
        '''
        向widget中添加itemWidget
        :param label:
        :return:
        '''
        ## 新建itemWidget
        item_widget = FileDirListItemWidget(text=label)

        ## 信号绑定
        item_widget.signal_delItem.connect(self.del_Item)
        item_widget.signal_moveUp.connect(self.on_labelUp)
        item_widget.signal_moveDown.connect(self.on_labelDown)

        item = QListWidgetItem(label)
        item.setSizeHint(QtCore.QSize(200, 30))

        self.addItem(item)
        self.setItemWidget(item, item_widget)

    def dropEvent(self, event) -> None:
        '''
        重写拖拽事件
        :param event:
        :return:
        '''
        super(FileDirListWidget, self).dropEvent(event)
        res_list = []
        for i in range(self.count()):
            item_i = self.item(i)
            res_list.append(item_i.text())
        self.labels = res_list
        print('当前的列表:', self.labels)

    def closeEvent(self, event):
        '''
        重写窗口关闭事件,用于dirs模式
        :param event:
        :return:
        '''
        if self.mode == 'files':
            reply = QMessageBox.question(self,
                                         '保存修改?',
                                         "是否保存对文件列表的修改?",
                                         QMessageBox.Yes | QMessageBox.No,
                                         QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.signal_subLabelsWidgetClose.emit(self.labels)
                print('更新数据完成,关闭窗口...')
                event.accept()
            else:
                event.accept()



if __name__ == "__main__":
    app = QApplication(sys.argv)

    ## labels = [str(i + 1) + '.jpg' for i in range(10)]
    ## myshow = FileDirListWidget(labels)

    dirs_num = 10
    labels = [str(i + 1) + '' for i in range(dirs_num)]
    subLabels = {str(key + 1): [str(i + 1) + '.jpg' for i in range(5)] for key in range(dirs_num)}
    print(labels)
    print(subLabels)
    myshow = FileDirListWidget(labels, subLabels, 'dirs')
    myshow.show()
    sys.exit(app.exec_())

QCheckBox

## 勾选状态事件与槽函数
self.ui.checkBox.stateChanged.connect(self.state_changed)

## 槽函数
def state_changed(self, state):
    print('状态设置为:', state)
    if state==2:  ## 勾选
        print('选项已勾选')
    elif state==0:  ## 取消勾选
        print('选项已取消勾选')

QFileDialog

文件类型列表设置

  • QFileDialog.getSaveFileName
def save_dialog(self):
        """Save Image in image formats(PNG,JPEG,BMP,TIFF)
        """
        ## 不同文件扩展名以;;分隔;选择对应项后,会自动设置文件名的扩展名为()中的类型
        files_types = "PNG (*.png);;JPEG (*.jpeg);;TIFF (*.tiff);;BMP(*.bmp)"
        fileName, _ = QFileDialog.getSaveFileName(self, 'Export_Name', os.getenv('HOME'), "untitled.png"),files_types)
        fName = str(fileName)
        file_extension = fName.split(".")[-1]

        if file_extension == 'png' or file_extension == 'jpeg' or file_extension == 'bmp' or file_extension == 'tiff':

            QMessageBox.about(self, 'Information', "File saved")

QPushButton

基本事件绑定

self.ui.pBtn.clicked.connect(self.slotA)

绑定颜色选择器

self.ui.pBtnColorSelector.clicked.connect(self.setColor)

def colorSelector(self):
    ## 弹出颜色选择对话框
    color = QColorDialog.getColor()

    ## 检测用的选择是否合法(点击cancel就是非法,否则就是合法)
    if color.isValid():
        return color.name()
    else:
        return False

def setColor(self):
    color = self.colorSelector()
    if color:
        self.ui.pBtnColorSelector.setStyleSheet('QPushButton{background:%s};' % color)

QTableWidget

添加包含控件的行&设置控件居中

image-20220425124135720

def addRow(self, rowData: list):
    '''
        向单元格添加一行数据
        :param rowData:
        :return:
        '''
    self.ui.tableWidget.setRowCount(self.ui.tableWidget.rowCount() + 1)
    rowIndex = self.ui.tableWidget.rowCount() - 1
    itemOder = QTableWidgetItem(rowData[0])
    itemOder.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
    self.ui.tableWidget.setItem(rowIndex, 0, itemOder)
    widgetColorTag = QWidget()
    btnColorTag = QPushButton()
    btnColorTag.setEnabled(False)
    btnColorTag.setStyleSheet('QPushButton{width: 28px;height: 28px;background:%s;border:none;}' % rowData[1])
    btnColorTag.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
    layoutColorTag = QHBoxLayout()
    layoutColorTag.setContentsMargins(5, 5, 5, 5)
    layoutColorTag.addWidget(btnColorTag)
    widgetColorTag.setLayout(layoutColorTag)
    self.ui.tableWidget.setCellWidget(rowIndex, 1, widgetColorTag)
    itemTagName = QTableWidgetItem(rowData[2])
    itemTagName.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
    self.ui.tableWidget.setItem(rowIndex, 2, itemTagName)
    widgetDelete = QWidget()
    btnOp = QPushButton('删除')
    btnOp.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)
    btnOp.setStyleSheet('''
        QPushButton{
            width: 68px;
            height: 28px;
            background: ##252b41;
            border: 1px solid rgba(142, 159, 196, 0.3);
            font-family: "Microsoft YaHei";
            font-weight: 400;
            font-size: 14px;
            text-align: center;
            color: ##8e9fc4;
            border:none;
        }
        QPushButton:hover{
            background:#2c344b;
        }
        ''')
        layoutDelete = QHBoxLayout()
        layoutDelete.addWidget(btnOp)
        layoutDelete.setContentsMargins(5, 5, 5, 5)
        widgetDelete.setLayout(layoutDelete)
        self.ui.tableWidget.setCellWidget(rowIndex, 3, widgetDelete)
        self.ui.tableWidget.setRowHeight(rowIndex, 44)

单元格按钮绑定删除事件(获取控件所在行列)

  • 基于QPoint和QModelIndex获取
def deleteRow(self, btn: QPushButton):
    widget = btn.parentWidget()  ## 由于btn外面嵌套了一层widget,应该通过获取这个widget所在位置确定按钮的位置(直接获取btn的位置得到的行列永远是0,1)
    modelIndex = self.ui.tableWidget.indexAt(QPoint(widget.frameGeometry().x(), widget.frameGeometry().y()))
    print('点击了{}行{}列的按钮'.format(modelIndex.row(), modelIndex.column()))
    self.ui.tableWidget.removeRow(modelIndex.row())

QListWidget

自定义QListWidgetItem

  • 先创建一个QListWidgetItem添加到QListWidget中,通过setSizeHint设置QListWidgetItem的尺寸,防止自定义的Widget加入后显示不全

  • 创建自定义的widget,通过self.ui.listWidget.setItemWidget(item, widget)替换原来的item

  • image-20220427181256355

from PyQt5.QtWidgets import *
from PyQt5.Qt import Qt
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QPixmap
import sys
import wisdom_store.ui.main.resources_qt5

'''
自定义QListWidget的item的方法(item可包含多个控件):
    先创建一个QListWidgetItem添加到QListWidget中,然后创建自定义的widget,之后通过self.ui.listWidget.setItemWidget(item, widget)替换原来的item
    
'''


class CustomItemWidget(QWidget):
    def __init__(self,text):
        super(CustomItemWidget, self).__init__()

        lay = QHBoxLayout()
        labelIcon = QLabel()
        labelIcon.setPixmap(QtGui.QPixmap(":/resources/模型设置.png"))
        labelIcon.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        labelIcon.setContentsMargins(5, 0, 5, 0)
        lay.addWidget(labelIcon)
        labelItemName = QLabel(text)
        labelItemName.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        labelItemName.setStyleSheet('''
            font-family: "Microsoft YaHei";
            font-weight: 400;
            font-size: 14px;
            color: ##dcefff;
            border: 1px solid rgba(0, 0, 0, 0);
        ''')
        lay.addWidget(labelItemName)
        labelIndicator = QLabel()
        labelIndicator.setPixmap(QtGui.QPixmap(":/resources/右箭头.png"))
        labelIndicator.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        lay.addWidget(labelIndicator)

        lay.setSpacing(0)
        self.setLayout(lay)


class CustomListWidget(QWidget):
    def __init__(self):
        super(CustomListWidget, self).__init__()
        self.resize(300, 600)
        self.listWidget = QListWidget()

        self.listWidget.setStyleSheet('''
            QListWidget{
                background: ##2c344b;
                border:none;
                outline:0px;  /*去除选中item外轮廓虚线*/
            }
            QListWidget::item:selected{
                background: ##21273C;
                border:none;
            }
        ''')

        lay = QHBoxLayout()
        lay.addWidget(self.listWidget)
        self.setLayout(lay)

        ## item1
        listItem1 = QListWidgetItem(self.listWidget)
        self.listWidget.addItem(listItem1)
        widget1 = CustomItemWidget('模型设置')
        listItem1.setSizeHint(QSize(100, 60))
        self.listWidget.setItemWidget(listItem1, widget1)

        ## item2
        listItem2 = QListWidgetItem(self.listWidget)
        self.listWidget.addItem(listItem2)
        widget2 = CustomItemWidget('推理参数')
        listItem2.setSizeHint(QSize(100, 60))  ## 设置原始item的尺寸,否则自定义的widget可能显示不全
        self.listWidget.setItemWidget(listItem2, widget2)

        self.listWidget.setCurrentItem(listItem1)

        self.listWidget.currentItemChanged.connect(self.selectItem)

    def selectItem(self, currentItem: QListWidgetItem, previousItem: QListWidgetItem):
        ## currentItem.setBackground(QtGui.QColor('#21273C'))
        ## if previousItem is not None:
        ##     previousItem.setBackground(QtGui.QColor('#2C344B'))
        print('当前选中:{}'.format(self.listWidget.currentRow()))


if __name__ == '__main__':
    ## 自适应高分辨率屏幕(注意放在QApplication创建之前)
    QtCore.QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)  ## 适应windows缩放
    QtGui.QGuiApplication.setAttribute(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)  ## 设置支持小数放大比例(适应如125%的缩放比)
    app = QApplication(sys.argv)
    widgetWindow = CustomListWidget()
    widgetWindow.show()
    sys.exit(app.exec_())

其他功能&操作

替换QtDesigner创建的控件&保持已绑定的事件处理方法仍然有效

1、继承QLabel,增加点击事件(以重写QLabel增加点击事件为例)

class QLabelUser(QLabel):
    signal_clicked = pyqtSignal()

    def __init__(self, text=''):
        super(QLabelUser, self).__init__()
        self.setText(text)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(sizePolicy)
        font = QFont()
        font.setFamily("Microsoft YaHei")
        font.setPointSize(-1)
        font.setBold(True)
        font.setWeight(62)
        self.setFont(font)
        self.setStyleSheet("font-family: \"Microsoft YaHei\";\n"
                           "font-weight: 500;\n"
                           "font-size: 16px;\n"
                           "line-height: 14px;\n"
                           "text-align: left;\n"
                           "color: ##fff;")
        self.setAlignment(Qt.AlignCenter)
        self.setObjectName("label_user")
        self.setCursor(QCursor(Qt.ArrowCursor))

    def mousePressEvent(self, ev):
        self.signal_clicked.emit()
        print('信号发送')

2、替换控件

## 替换欢迎信息的label,增加点击事件
## 创建新的控件
self.label_user = QLabelUser('欢迎使用')
self.label_user.setParent(self.ui.widget_welcome)
self.label_user.signal_clicked.connect(self.show_auth_win)

## self.ui.label_user = self.label_user  ## 替换布局的widget与直接覆盖原始ui的控件的效果不完全一样
lay1 = self.ui.widget_welcome.layout()
widget_label_user = lay1.itemAt(0).widget()  ## 获取原始控件(指向self.ui.label_user)
widget_label_user = self.label_user  ## 替换控件(勿删)【此方法替换后,self.ui.label_user绑定的事件仍然有效】

设置鼠标手势

from PyQt5.QtGui import QCursor
from PyQt5.QtCore import Qt

self.ui.btn.setCursor(QCursor(Qt.PointingHandCursor))  ## 鼠标在某个控件上的样式
self.setCursor(QCursor(Qt.ArrowCursor))  ## 界面全局的鼠标样式

'''
常用手势:
textToCursorShape = {
            u'正常': Qt.ArrowCursor,  ## 正常样式
            u'后台运行': Qt.BusyCursor,
            u'等待': Qt.WaitCursor,
            u'精准十字': Qt.CrossCursor,
            u'禁止': Qt.ForbiddenCursor,     
            u'食指手型': Qt.PointingHandCursor,  ## 点击样式
            u'打开手型': Qt.OpenHandCursor,  ## 拖拽样式
            u'关闭手型': Qt.ClosedHandCursor,
            u'水平调整': Qt.SizeHorCursor,
            u'垂直调整': Qt.SizeVerCursor,
            u'沿对角线调整1': Qt.SizeFDiagCursor,
            u'沿对角线调整2': Qt.SizeBDiagCursor,
            u'候选上箭头': Qt.UpArrowCursor,
            u'移动': Qt.SizeAllCursor,
            u'文本选择': Qt.IBeamCursor,
            u'帮助选择': Qt.WhatsThisCursor,
            u'水平分割': Qt.SplitHCursor,
            u'垂直分割': Qt.SplitVCursor,
            u'空': Qt.BlankCursor,
            u'自定义': custonCursor
        }
'''

Qt样式设置

1.通过窗口对象设置样式

from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QApplication
import sys


class QSSBasic(QWidget):
    def __init__(self):
        super(QSSBasic, self).__init__()

        btn1 = QPushButton(self)
        btn1.setText('按钮1')
        btn2 = QPushButton(self)
        btn2.setText('按钮2')
        btn3 = QPushButton(self)
        btn3.setText('按钮3')

        lay = QVBoxLayout()
        lay.addWidget(btn1)
        lay.addWidget(btn2)
        lay.addWidget(btn3)

        self.setLayout(lay)

        ## 设置样式
        qssStyle = '''
            QPushButton{
                background-color:yellow;
            }
        '''
        self.setStyleSheet(qssStyle)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = QSSBasic()
    win.show()
    sys.exit(app.exec_())

2.使用QSS选择器设置控件样式

from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QApplication
import sys


class QSSSelector(QWidget):
    def __init__(self):
        super(QSSSelector, self).__init__()

        btn1 = QPushButton(self)
        btn1.setText('按钮1')
        btn1.setProperty('name', 'btn1')  ## 为控件设置属性
        btn2 = QPushButton(self)
        btn2.setText('按钮2')
        btn3 = QPushButton(self)
        btn3.setText('按钮3')

        lay = QVBoxLayout()
        lay.addWidget(btn1)
        lay.addWidget(btn2)
        lay.addWidget(btn3)

        self.setLayout(lay)

        ## 设置样式
        qssStyle = '''
            QPushButton[name='btn1']{
                background-color:yellow;
            }
        '''
        self.setStyleSheet(qssStyle)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = QSSSelector()
    win.show()
    sys.exit(app.exec_())

3.子控件选择器

from PyQt5.QtWidgets import QWidget, QComboBox, QVBoxLayout, QApplication
import sys


class QSSSubControl(QWidget):
    def __init__(self):
        super(QSSSubControl, self).__init__()

        self.setWindowTitle('QSS子控件选择器')
        combobox=QComboBox(self)
        combobox.setObjectName('myComboBox')
        combobox.addItem('Windows')
        combobox.addItem('Linux')
        combobox.addItem('Unix')

        combobox.move(50,50)

        self.setGeometry(250,200,320,150)

        ## 设置样式【#号相当于id;::后为子控件】
        qssStyle = '''
            QComboBox##myComboBox::drop-down{
                image:url(下拉.png)
            }
        '''
        self.setStyleSheet(qssStyle)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = QSSSubControl()
    win.show()
    sys.exit(app.exec_())

4.窗口样式

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt


class WindowPattern(QMainWindow):
    def __init__(self):
        super(WindowPattern, self).__init__()
        self.resize(500, 260)
        self.setWindowTitle('设置窗口的样式')

        ## self.setWindowFlags(Qt.WindowMaximizeButtonHint | Qt.WindowStaysOnTopHint|Qt.FramelessWindowHint)
        self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowMaximizeButtonHint|Qt.WindowMinimizeButtonHint)
        self.setObjectName('MainWindow')
        self.setStyleSheet("#MainWindow{border-image:url(背景.jpg);}")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = WindowPattern()
    win.show()
    sys.exit(app.exec_())

5.为标签和按钮添加背景图

from PyQt5.QtWidgets import *
import sys


class LabelButtonBackground(QWidget):
    def __init__(self):
        super(LabelButtonBackground, self).__init__()
        label1=QLabel(self)
        label1.setToolTip('这是一个文本标签')
        label1.setStyleSheet("QLabel{border-image:url('背景.jpg');}")

        label1.setFixedWidth(476)
        label1.setFixedHeight(259)

        btn1 = QPushButton(self)
        btn1.setObjectName('btn1')
        btn1.setFixedWidth(200)
        btn1.setFixedHeight(200)

        style = '''
            ##btn1{
                background-image:url('添加.png');
            }
            ##btn1:Pressed{
                background-image:url('已添加.png');
            }
        '''

        btn1.setStyleSheet(style)

        lay = QVBoxLayout()
        lay.addWidget(label1)
        lay.addStretch()
        lay.addWidget(btn1)

        self.setLayout(lay)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = LabelButtonBackground()
    win.show()
    sys.exit(app.exec_())

6.缩放图片

from PyQt5.QtWidgets import *
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import Qt
import sys


class LabelButtonBackground(QWidget):
    def __init__(self):
        super(LabelButtonBackground, self).__init__()
        label1 = QLabel(self)
        label1.setToolTip('这是一个文本标签')

        label1.setFixedWidth(100)
        label1.setFixedHeight(200)

        filename = '添加.png'
        img = QImage(filename)

        ## img_scaled = img.scaled(label1.width(), label1.height(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
        img_scaled = img.scaled(label1.width(), label1.height(),Qt.KeepAspectRatio, Qt.SmoothTransformation)
        label1.setPixmap(QPixmap.fromImage(img_scaled))

        lay = QVBoxLayout()
        lay.addWidget(label1)
        lay.addStretch()

        self.setLayout(lay)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = LabelButtonBackground()
    win.show()
    sys.exit(app.exec_())

7.窗口透明效果

from PyQt5.Qt import *
import sys

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = QMainWindow()
    win.setWindowTitle('窗口透明效果')
    win.setWindowOpacity(0.1)
    win.resize(400, 200)
    win.show()
    sys.exit(app.exec_())