pyqt5学习笔记1: 常用组件的使用

1 快速启动一个窗口

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore

app = QtWidgets.QApplication(sys.argv) # 例化Widget前必须先例化一个app。
win = QtWidgets.QWidget()    # 例化Widget
win.show()                   # 显示Widget
sys.exit(app.exec_())        # 当app退出时,退出当前python程序

2 窗口基本设置

窗口位置、大小、图标、标题、布局

效果图:

------------------------------------
| <> 一个窗口样例             - 口 X|
------------------------------------
|      ---------------------------- |
| 姓名 | 输入条                   | |
|      ---------------------------- |
|                                   |
|                                   |
|                    ----    ----   |
|                   |取消|  |提交|  |
|                    ----    ----   |
-------------------------------------

代码

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtGui

class CDemo(QtWidgets.QWidget):
    def __init__(self):
        super().__init__() # 要先调用QWidget(父类)的初始化函数.

        #窗口中的几个部件
        self.o_label     = QtWidgets.QLabel('姓名')
        self.o_line_edit = QtWidgets.QLineEdit('输入条')
        self.o_btn_0     = QtWidgets.QPushButton('取消')
        self.o_btn_1     = QtWidgets.QPushButton('提交')

        # 窗口位置与大小,参数:x位置,y位置, x宽度, y高度
        self.setGeometry(200, 200, 400, 200) #self.resize(400, 300)

        # 窗口图标,显示在窗口标题拦最左边.
        self.setWindowIcon(QtGui.QIcon('collapse_on.png'))

        # 窗口标题,显示在窗口标题拦.
        self.setWindowTitle('一个窗口样例')

        # 设置窗口内组件摆放
        self.setLayout(self.__gen_layout())

    def __gen_layout(self):
        # 子layout,水平放置标签和输入框,标签使用默认宽度,输入框拉伸占满其余宽度。
        _o_layout_0 = QtWidgets.QHBoxLayout()
        _o_layout_0.addWidget(self.o_label) # 添加Widget
        _o_layout_0.addWidget(self.o_line_edit)

        # 子layout,水平放置两个按钮, 靠右对齐.
        _o_layout_1 = QtWidgets.QHBoxLayout()
        _o_layout_1.addStretch(1) # stretch 占满左侧空间, 1是个比例,不代表绝对值.
        _o_layout_1.addWidget(self.o_btn_0)
        _o_layout_1.addWidget(self.o_btn_1)
        _o_layout_1.addSpacing(10) # 最右侧空出10px的空白.

        # 主layout, 垂直放置子layout.
        _o_layout_main = QtWidgets.QVBoxLayout()
        _o_layout_main.addLayout(_o_layout_0) # 添加layout
        _o_layout_main.addStretch(1)          # 中间拉伸,使按钮放置到最底下.
        _o_layout_main.addLayout(_o_layout_1)

        return _o_layout_main

app = QtWidgets.QApplication(sys.argv)
win = CDemo()
win.show()
sys.exit(app.exec_())

3 QTabWidget(页签)

效果图

代码

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore

class CDemo(QtWidgets.QTabWidget):
    def __init__(self):
        super().__init__()

        '''设置tab标签的位置'''
        self.setTabPosition(QtWidgets.QTabWidget.North)

        '''设置tab可以关闭, 默认情况下tab不可关'''
        self.setTabsClosable(True)
        self.tabCloseRequested.connect(self.__on_tab_close_clicked)

        '''添加tab, 参数:widget, tab_name'''
        self.addTab(QtWidgets.QTextEdit('A'), 'Tab 0')
        self.addTab(QtWidgets.QTextEdit('B'), 'Tab 1')

        self.setWindowTitle('A tab example')

    def __on_tab_close_clicked(self, idx):
        if self.count()>=1:
            self.widget(idx).deleteLater()
            self.removeTab(idx)

app = QtWidgets.QApplication(sys.argv)
win = CDemo()
win.show()
sys.exit(app.exec_())

4 QTreeWidget(树)

创建tree

#创建Tree, 指定表头和列数
o_tree = QtWidgets.QTreeWidget()
o_tree.setHeaderLabels['name', 'value']
o_tree.setColumnCount(2)

遍历tree

#遍历tree
o_item_iter = QtWidgets.QTreeWidgetItemIterator(o_tree)
while o_item_iter.value():
    _o_curr_tree_item = o_item_iter.value()

    #do something for _o_curr_tree_item

    _o_item_iter.__iadd__(1)

QTreeWidgetItem自带CheckBox,可以直接设置Check状态

#三种Check状态:
QtCore.Qt.Checked           # 选中
QtCore.Qt.UnChecked         # 未选中
QtCore.Qt.PartiallyChecked  # 半选中

#设置、获取Check状态:
o_tree_item.setCheckState(0, QtCore.Qt.Unchecked) # 设置当前tree item第0列的check状态
o_tree_item.checkState(0) # 获取当前tree item第0列的check状态

QTreeWidgetItem设置图标/设置列宽

#设置第0列图标
o_tree_item.setIcon(0, QtGui.QIcon('xx.png'))
#设置第0列列宽
o_tree.setColumnWidth(0, 100)

获取当前Tree、Item的父容器、子Item

#获取tree item的父容器(可能是tree,也可能是item)
o_tree_or_item = o_tree_item.parent()
#获取tree item最顶部的tree widget
o_tree = o_tree_item.treeWidget()

#获取tree/item的child:
for i in range(o_tree_item.childCount()):
    _o_child = o_tree_item.child(i)
#展开与移动滚动条
o_tree.scrollToItem(_o_child) #将滚动条移到到特定item
o_tree.expandAll() #展开所有折叠的item, 注意, 需要在添加完item后再执行这个命令, 这样所有item都会展开, 否则这条命令后的item不会展开.

5 QTableWidget(表格)

效果图:

创建表格

from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore
import sys
import time

class CDemo(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.o_table = self._gen_table()

        self.resize(500, 300)
        self.setLayout(self._gen_layout())

    def _gen_table(self):
        _o_table = QtWidgets.QTableWidget()

        '''设置行数、列数'''
        _o_table.setRowCount(2)
        _o_table.setColumnCount(3)

        '''为第0行添加单元格,单元格内容为文本'''
        for i in range(3):
            _o_table.setItem(0, i, QtWidgets.QTableWidgetItem(f'cell0{i}'))

        '''为第1行添加单元格,单元格内容为其它Widget'''
        for i in range(3):
            _o_table.setCellWidget(1, i, self._gen_combox())
            //_o_table.setCellWidget(1, i, self._gen_btn(1, i)) //给btn传递参数, 用于辨别是哪个btn

        '''设置水平、垂直表头'''
        _o_table.setHorizontalHeaderLabels(['A', 'B', 'C'])
        _o_table.setVerticalHeaderLabels(['0', '1'])

        '''设置表头可见,默认可见'''
        _o_table.horizontalHeader().setVisible(True)
        _o_table.verticalHeader().setVisible(True)

        '''设置鼠标悬停表头时的tip'''
        _o_table.horizontalHeaderItem(0).setToolTip('This is Column A')

        '''设置列宽、行高根据内容调整, 
           注意,设置些命令后,再重新给单元格赋值,列宽、行高不会自动调整
                 即这个设置是静态设置
        '''
        _o_table.resizeColumnsToContents()
        _o_table.resizeRowsToContents()

        '''设置特定行/列的列宽、行高'''
        _o_table.setRowHeight(0, 20)
        _o_table.setColumnWidth(0, 100)

        return _o_table

    def _gen_combox(self):
        _o_combox = QtWidgets.QComboBox()
        _o_combox.addItems(['0', '1', '2', '3'])

        return _o_combox

    def _gen_btn(self, i_row, i_column):
        _o_btn = QtWidgets.QPushButton('b')
        //click连接到的函数不能写参数, 但使用lambda后, 将有参数函数转为无参数函数, 就可以在连接的参数中使用参数了
        _o_btn.clicked.connect(lambda: self._on_btn_clicked(i_row, i_column))

    def _on_btn_clicked(self, i_row, i_column):
        print(f'{i_row} {i_column}')

    def _gen_layout(self):
        _o_layout_main = QtWidgets.QVBoxLayout()
        _o_layout_main.addWidget(self.o_table)

        return _o_layout_main

app = QtWidgets.QApplication(sys.argv)
win = CDemo()
win.show()
sys.exit(app.exec_())

表格常用设置

#清空整个表格的内容(行、列会保留):
o_table.clear()     # 会把表格内容, 表头等清除掉
o_table.clearSpan() # Span信息要单独清除

#行操作
o_table.rowCount()      # 获取行数目
o_table.setRowCount(10) # 设置行数目
o_table.insertRow(7)    # 在表格中第6行下面添加一行做为第7行(添加行后才能往行中添加单元格)
o_table.removeRow(i)    # 删除行

#删除所有行:注意要倒序删除,因为删除过程中行序号是动态变化的。
for i in range(o_table.rowCount()-1, -1, -1):
    o_table.removeRow(i)

#给单元格设置item、widget
o_table.setItem(i, j, o_table_item)
o_table.setCellWidget(i, j, o_widget)

#获取单元格item/widget:
o_table_item = o_table.item(i, j)   # 普通文本单元格,类型是QTableWidgetItem
o_widget = o_table.cellWidget(i, j) # 单元格内容是QWidget时,通过cellWidget获取。

#遍历单元格:
for i in range(o_table.rowCount()):
    for j in range(o_table.columnCount()):
        o_item = o_table.item(i, j) # 如果该单元格是cellwidget, 则需要用o_table.cellwidget(i, j)

#获取行高之和
i_height = 0
for i in range(o_table.rowCount()):
    i_height += o_table.rowHeight(i)


#设置单元格文本对齐方式:格式:水平对齐方式 | 垂直对齐方式
o_table_item.setTextAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter) 

#设置单元格背景色:
o_table_item.setBackground(QtGui.QColor(220, 220, 220))


#设置单元格不可编辑, 不可选中:
o_table_item.setFlags(o_table_item.flags() & ~QtCore.Qt.ItemIsEditable)
o_table_item.setFlags(o_table_item.flags() & ~QtCore.Qt.ItemIsSelectable)


#设置合并单元格:
#    i_span是行span,表示(i, j)这个单元格占多少行,是在同一列中扩展,
#    j_span是列span,表示(i, j)这个单元格占多少列,是在同一行中扩展
o_table.setSpan(i, j, i_span, j_span)


#设置表头内容:
o_table.setHorizontalHeaderLabels(['c0', 'c1', 'c2'])
o_table.setVerticalHeaderLabels(['r0', 'r1', 'r2'])

#获取行表头和列表头
o_table.VerticalHeader()
o_table.horizontalHeader()

#设置表头隐藏:
o_table.VerticalHeader().setVisible(False)
o_table.horizontalHeader().setVisible(False)


#获取表头item:
o_table.horizontalHeaderItem(i) # 水平第i个表头
o_table.verticalHeaderItem(j)   # 垂直第j个表头

# 滚动条
o_table.scrollToBottom() # 滚动到底部

6 QDockWidget(可拖动窗口)

效果图:

代码:

导入模块

import sys  
from PyQt5 import QtWidgets  
from PyQt5 import QtGui  
from PyQt5 import QtCore  

定义一个Dock

class CLeftDock(QtWidgets.QDockWidget):  
    def __init__(self):  
        super().__init__()  
  
        self.setWindowTitle('left dock')  
        self.setWidget(QtWidgets.QTextEdit('left'))  
        #self.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea)  
  
        '''  
        设置属性, 只设置一个的话,会把其它默认属性去掉  
        比如dockwidget默认是可以从主窗口中拖出来,而且可关闭,  
        如果只设置Closable属性,则dockwidget就不能从主窗口拖出来了.  
        '''  
        self.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)  

又定义一个Dock

class CBasicDock(QtWidgets.QDockWidget):  
    def __init__(self):  
        super().__init__()  
  
        self.setWindowTitle('basic dock')  
        self.setWidget(QtWidgets.QTextEdit('Basic'))  
        #self.setAllowedAreas(QtCore.Qt.TopDockWidgetArea)  
        '''同CLeftDock中的描述'''  
        self.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)  

再定义一个Dock

class CDetailDock(QtWidgets.QDockWidget):  
    def __init__(self):  
        super().__init__()  
  
        self.setWindowTitle('detail dock')  
        self.setWidget(CDetailWidget())  
        #self.setAllowedAreas(QtCore.Qt.RightDockWidgetArea)  
        '''同CLeftDock中的描述'''  
        self.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)  
  
        #_o_size_policy = self.sizePolicy()  
        #_o_size_policy.setHorizontalPolicy(QtWidgets.QSizePolicy.Expanding)  
        #_o_size_policy.setVerticalPolicy(QtWidgets.QSizePolicy.Preferred)  
        #_o_size_policy.setHorizontalStretch(100)  
        #_o_size_policy.setVerticalStretch(0)  

#dock窗口内容
class CDetailWidget(QtWidgets.QTextEdit):  
    def __init__(self):  
        super().__init__()  
        self.append('Detail')  
  
    '''设置推荐尺寸, 用于界面启动时各dock初始尺寸'''  
    def sizeHint(self):  
        return QtCore.QSize(500, 300)  

主窗口

class CMainWindow(QtWidgets.QMainWindow):  
    def __init__(self):  
        super().__init__()  
  
        self.o_left_dock = CLeftDock()  
        self.o_basic_dock = CBasicDock()  
        self.o_detail_dock = CDetailDock()  
  
        '''设置Dock的摆放关系'''  
        self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.o_left_dock)  
        self.splitDockWidget(self.o_left_dock, self.o_basic_dock, QtCore.Qt.Horizontal)  
        self.splitDockWidget(self.o_basic_dock, self.o_detail_dock, QtCore.Qt.Vertical)  
  
        self.setGeometry(200, 200, 600, 400)  
        self.setWindowTitle('dock example')  
  
        '''隐藏默认的中心Widget,只显示Dock'''  
        o_central_widget = self.centralWidget()  
        if o_central_widget!=None:  
            o_central_widget.hide()  

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

7 自定义可折叠Widget

说明:可以用这种自定义的方式实现类似tree的效果,但实测速度方面并不如tree.

效果图:

代码:

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtGui

class CollapseWidget(QtWidgets.QWidget):
    def __init__(self, s_text, list_widgets=None):
        super().__init__()

        self.s_text = s_text
        self.list_widgets = list_widgets if list_widgets!=None else []

        self.o_btn = self._gen_btn()          # 通过点击btn 显示/隐藏widget
        self._set_list_widgets_visible(False) # 设置widget默认隐藏

        self.setStyleSheet(self._gen_style_sheet())
        self.setLayout(self._gen_layout())

    def _gen_btn(self):
        _o_btn = QtWidgets.QPushButton(self.s_text)
        if len(self.list_widgets)>0:
            _o_btn.setIcon(QtGui.QIcon('collapse_on.png'))
        _o_btn.clicked.connect(self._on_btn_clicked)
        return _o_btn

    def _on_btn_clicked(self):
        _o_btn = self.sender()
        if len(self.list_widgets)>0:
            _b_visible = self.list_widgets[0].isVisible()

            if _b_visible==True:
                _o_btn.setIcon(QtGui.QIcon('collapse_on.png'))
                self._set_list_widgets_visible(False)
            else:
                _o_btn.setIcon(QtGui.QIcon('collapse_off.png'))
                self._set_list_widgets_visible(True)

    def _set_list_widgets_visible(self, b_visible):
        for _o_widget in self.list_widgets:
            _o_widget.setVisible(b_visible)

    def _gen_style_sheet(self):
        _list = list()
        _list.append('QPushButton {')
        _list.append('    text-align: left;')
        _list.append('    border: none;')
        #_list.append('    background-color: transparent;')
        _list.append('}')
        _list.append('QPushButton:hover {')
        _list.append('    background-color: lightblue;')
        _list.append('}')
        _list.append('QPushButton:pressed {')
        _list.append('    background-color: lightskyblue;')
        _list.append('}')

        return ' '.join(_list)

    def _gen_layout(self):
        _o_layout_btn = QtWidgets.QVBoxLayout()
        _o_layout_btn.setContentsMargins(0,0,0,0)
        _o_layout_btn.addWidget(self.o_btn)

        _o_layout_w0 = QtWidgets.QVBoxLayout()
        for _o_widget in self.list_widgets:
            _o_layout_w0.addWidget(_o_widget)

        _o_layout_w1 = QtWidgets.QHBoxLayout()
        _o_layout_w1.addSpacing(40)
        _o_layout_w1.addLayout(_o_layout_w0)

        _o_layout_main = QtWidgets.QVBoxLayout()
        _o_layout_main.addLayout(_o_layout_btn)
        _o_layout_main.addLayout(_o_layout_w1)
        #_o_layout_main.addStretch(1)

        return _o_layout_main

class Demo(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.o_widget_0 = CollapseWidget('1. size', [CollapseWidget('1.1 height'), CollapseWidget('1.2 width')])
        self.o_widget_1 = CollapseWidget('2. head', [CollapseWidget('2.1 title' ), CollapseWidget('2.2 position')])

        self.resize(300, 200)
        self.setLayout(self._gen_layout())

    def _gen_layout(self):
        _o_layout = QtWidgets.QVBoxLayout()
        _o_layout.addWidget(self.o_widget_0)
        _o_layout.addWidget(self.o_widget_1)
        _o_layout.addStretch(1)
        return _o_layout

app = QtWidgets.QApplication(sys.argv)
win = Demo()
win.show()
sys.exit(app.exec_())

8 QMessageBox(消息框)/QDialog(自定义对话框)


效果图:

代码:

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtGui

class Demo(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.o_btn_info = self._gen_btn_info()

        self.setLayout(self._gen_layout())
        self.resize(300, 150)

    def _gen_btn_info(self):
        _o_btn = QtWidgets.QPushButton('information')
        _o_btn.clicked.connect(self._on_btn_info_clicked)
        return _o_btn

    def _on_btn_info_clicked(self):
        QtWidgets.QMessageBox.information(
            self,                     # 父容器, 可以为None
            'Information',            # 消息框标题
            'This is a information',  # 消息框文本内容
            QtWidgets.QMessageBox.Ok  # 消息框按钮
        )

    def _gen_layout(self):
        _o_layout_main = QtWidgets.QHBoxLayout()
        _o_layout_main.addWidget(self.o_btn_info)
        _o_layout_main.addStretch(1)

        return _o_layout_main

app = QtWidgets.QApplication(sys.argv)
win = Demo()
win.show()
sys.exit(app.exec_())

消息框:

QMessageBox.information(parent, 'title', 'msg', QMessageBox.Ok)
QMessageBox.warning(parent, 'title', 'msg', QMessageBox.Ok)
QMessageBox.critical(parent, 'title', 'msg', QMessageBox.Ok)

问题框:

reply = QtWidgets.QMessageBox.question(  
    parent,                         # 父容器, 可以为None
    'Question',                     # 消息框标题
    'Msg',                          # 消息框文本内容
    QMessageBox.Yes|QMessageBox.No, # 消息框按钮
    QMessageBox.No,                 # 默认按钮
)  
  
if reply == QMessageBox.Yes:        # 获取用户响应
    do something  
else:  
    do something  

自定义对话框
效果图:

代码:

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore

class Demo(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.o_btn_dialog= self._gen_btn_dialog()

        self.setLayout(self._gen_layout())
        self.resize(300, 150)

    def _gen_btn_dialog(self):
        _o_btn = QtWidgets.QPushButton('Dialog')
        _o_btn.clicked.connect(self._on_btn_dialog_clicked)
        return _o_btn

    def _on_btn_dialog_clicked(self):
        _o_dialog = CMyDialog()
        _o_dialog.select_done.connect(self._on_select_done)
        _o_dialog.exec_()
        _o_dialog.destroy()

    def _on_select_done(self, s_text):
        self.o_btn_dialog.setText(s_text)

    def _gen_layout(self):
        _o_layout_main = QtWidgets.QHBoxLayout()
        _o_layout_main.addWidget(self.o_btn_dialog)
        _o_layout_main.addStretch(1)

        return _o_layout_main

class CMyDialog(QtWidgets.QDialog):
    select_done = QtCore.pyqtSignal(str)

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

        self.o_combox = QtWidgets.QComboBox()
        self.o_combox.addItems(['Java', 'C#', 'Python'])

        self.o_btn_ok = self._gen_btn_ok()
        self.o_btn_cancel = self._gen_btn_cancel()

        self.setLayout(self._gen_layout())

    def _gen_btn_ok(self):
        _o_btn = QtWidgets.QPushButton('Ok')
        _o_btn.clicked.connect(self._on_btn_ok_clicked)
        return _o_btn

    def _on_btn_ok_clicked(self):
        _s_text = self.o_combox.currentText()
        self.select_done.emit(_s_text)
        self.close()
        
    def _gen_btn_cancel(self):
        _o_btn = QtWidgets.QPushButton('Cancel')
        _o_btn.clicked.connect(self._on_btn_cancel_clicked)
        return _o_btn

    def _on_btn_cancel_clicked(self):
        self.close()

    def _gen_layout(self):
        _o_layout_combox = QtWidgets.QHBoxLayout()
        _o_layout_combox.addWidget(self.o_combox)

        _o_layout_btn = QtWidgets.QHBoxLayout()
        _o_layout_btn.addWidget(self.o_btn_ok)
        _o_layout_btn.addWidget(self.o_btn_cancel)

        _o_layout_main = QtWidgets.QVBoxLayout()
        _o_layout_main.addLayout(_o_layout_combox)
        _o_layout_main.addLayout(_o_layout_btn)

        return _o_layout_main

app = QtWidgets.QApplication(sys.argv)
win = Demo()
win.show()
sys.exit(app.exec_())


9. 特殊对话框(打开文件, 保存文件, 选择颜色)

效果图:


代码

import sys
import os
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui

class CDemo(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.o_btn_open = self._gen_btn_open()
        self.o_btn_save = self._gen_btn_save()
        self.o_btn_color= self._gen_btn_color()
        self.o_line_edit = self._gen_line_edit()

        self.resize(300, 200)
        self.setLayout(self._gen_layout())

    def _gen_line_edit(self):
        _o_line_edit = QtWidgets.QLineEdit('')
        return _o_line_edit

    def _gen_btn_open(self):
        _o_btn = QtWidgets.QPushButton('open')
        _o_btn.clicked.connect(self._on_btn_open_clicked)
        return _o_btn

    def _on_btn_open_clicked(self):
        file_name, file_type = QtWidgets.QFileDialog.getOpenFileName(
            self,           # parent
            'OpenFile',     # title
            os.getcwd(),    # 起始目录
            'All Files(*);; Text Files(*.txt)' # file_type filter, 要用两个分号
        )
        #如果上面对话框没选择文件, 而是点击"取消", 则file_name==''
        print(file_name) # 选中的全路径文件名
        print(file_type) # 选择的文件类型,比如"All Files(*)"
        self.o_line_edit.setText(file_name)

    def _gen_btn_save(self):
        _o_btn = QtWidgets.QPushButton('save')
        _o_btn.clicked.connect(self._on_btn_save_clicked)
        return _o_btn

    def _on_btn_save_clicked(self):
        file_name, file_type = QtWidgets.QFileDialog.getSaveFileName(
            self,           # parent
            'SaveFile',     # title
            os.getcwd(),    # 起始目录
            'All Files(*);; Text Files(*.txt)' # file_type filter, 要用两个分号
        )
        #如果上面对话框没选择文件, 而是点击"取消", 则file_name==''
        print(file_name) # 选中/输入的全路径文件名
        print(file_type) # 选择的文件类型,比如"All Files(*)"
        self.o_line_edit.setText(file_name)

    def _gen_btn_color(self):
        _o_btn = QtWidgets.QPushButton('color')
        _o_btn.clicked.connect(self._on_btn_color_clicked)
        return _o_btn

    def _on_btn_color_clicked(self):
        _o_color = QtWidgets.QColorDialog.getColor() # 返回选择的颜色
        print(_o_color.name()) # 颜色名,是"#00FF00"这种字符串名字
        self.o_line_edit.setStyleSheet(f'QLineEdit {{background-color: {_o_color.name()} }}')


    def _gen_layout(self):
        _o_layout_0 = QtWidgets.QHBoxLayout()
        _o_layout_0.addWidget(self.o_line_edit)
        _o_layout_0.addWidget(self.o_btn_open)
        _o_layout_0.addWidget(self.o_btn_save)
        _o_layout_0.addWidget(self.o_btn_color)

        _o_layout_main = QtWidgets.QVBoxLayout()
        _o_layout_main.addLayout(_o_layout_0)
        _o_layout_main.addStretch(1)

        return _o_layout_main

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

10. 给Widget加滚动条

听说MainWidow自带滚动条, 但QWidget并没有, 所以如果想让QWidget中的内容可以滚动, 一般使用如下方法:
声明一个滚动条QScrollArea, 把QWidget添加到滚动条中(注意, 不是把滚动条添加到QWidget中), 这样QWidget就可以滚动了.

import sys
from PyQt5 import QtWidgets
class CDemo(QtWidgets.QScrollArea):
    def __init__(self):
        super().__init__()

        self.list_btn = self._gen_list_btn() # 声明一系列button
        self.o_widget = self._gen_widget()   # 把这些button按行列放到widget中
        self.setWidget(self.o_widget)        # 把widget放到滚动条中, 这样这些button所在的widget就有滚动条了.

    def _gen_list_btn(self):
        _list = list()
        for i in range(50):
            _list_i = list()
            for j in range(10):
                _list_i.append(QtWidgets.QPushButton(f'{i}.{j}'))
            _list.append(_list_i)
        return _list

    def _gen_widget(self):
        _o_widget = QtWidgets.QWidget()
        _o_widget.setLayout(self._gen_layout())

        return _o_widget
                
    def _gen_layout(self):
        _o_layout_main = QtWidgets.QVBoxLayout()

        for i in range(len(self.list_btn)):
            _o_layout_i = QtWidgets.QHBoxLayout()
            for j in range(len(self.list_btn[i])):
                _o_btn = self.list_btn[i][j]
                _o_layout_i.addWidget(_o_btn)
            _o_layout_main.addLayout(_o_layout_i)
        return _o_layout_main

app = QtWidgets.QApplication(sys.argv)
win = CDemo()
win.show()
sys.exit(app.exec_())

11 QProgressBar(进度条)

效果图:

代码

import sys
import time
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui

class Demo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.o_progress_bar = self._gen_progress_bar()
        self.o_btn_start = self._gen_btn_start()
        self.o_btn_clear = self._gen_btn_clear()

        self.setLayout(self._gen_layout())

    def _gen_progress_bar(self):
        _o_pbar = QtWidgets.QProgressBar()
        #_o_pbar.setRange(0, 100) # 默认范围是0~100, 且默认会显示%
        _o_pbar.setValue(30)      # 设置进度条的当前值
        return _o_pbar

    def _gen_btn_start(self):
        _o_btn = QtWidgets.QPushButton('Start')
        _o_btn.clicked.connect(self._on_btn_start_clicked)
        return _o_btn

    def _on_btn_start_clicked(self):
        self.o_btn_clear.setEnabled(False)
        _i_start = self.o_progress_bar.value() # 获取进度条的当前值
        print(type(_i_start))
        for i in range(_i_start, 100+1): # 注意循环上限设置为101
            time.sleep(0.1)
            self.o_progress_bar.setValue(i+0)
        self.o_btn_clear.setEnabled(True)

    def _gen_btn_clear(self):
        _o_btn = QtWidgets.QPushButton('Clear')
        _o_btn.clicked.connect(self._on_btn_clear_clicked)
        return _o_btn

    def _on_btn_clear_clicked(self):
        self.o_progress_bar.setValue(0)

    def _gen_layout(self):
        _o_layout = QtWidgets.QHBoxLayout()
        _o_layout.addWidget(self.o_progress_bar)
        _o_layout.addWidget(self.o_btn_start)
        _o_layout.addWidget(self.o_btn_clear)
        return _o_layout

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

12 QSlider和QSpinBox

效果图:

代码

import sys
import time
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui

class Demo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.o_slider = self._gen_slider()
        self.o_spinbox = self._gen_spinbox()

        self.setLayout(self._gen_layout())

    def _gen_slider(self):
        _o_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal) # 设置滑杆为水平方向, 默认垂直方向.
        _o_slider.setRange(0, 127)
        _o_slider.setSingleStep(1)
        _o_slider.setValue(30)
        _o_slider.valueChanged.connect(self._on_slider_value_changed)
        return _o_slider

    def _on_slider_value_changed(self):
        self.o_spinbox.setValue(self.o_slider.value())

    def _gen_spinbox(self):
        _o_spinbox = QtWidgets.QSpinBox()
        _o_spinbox.setRange(0, 127)
        _o_spinbox.setSingleStep(1)
        _o_spinbox.setValue(30)
        _o_spinbox.valueChanged.connect(self._on_spinbox_value_changed)
        return _o_spinbox

    def _on_spinbox_value_changed(self):
        self.o_slider.setValue(self.o_spinbox.value())

    def _gen_layout(self):
        _o_layout = QtWidgets.QHBoxLayout()
        _o_layout.addWidget(self.o_slider)
        _o_layout.addWidget(self.o_spinbox)
        return _o_layout

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

13 QComboBox

效果图:

代码

import sys
import time
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui

class Demo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.o_combox = self._gen_combox()
        self.o_label = QtWidgets.QLabel('Null')

        self.setLayout(self._gen_layout())
        self.resize(200, 100)

    def _gen_combox(self):
        _o_combox = QtWidgets.QComboBox()
        _o_combox.addItems(['Python', 'Java', 'C++']) # 设置待选项
        _o_combox.setCurrentIndex(1)                  # 设置当前选中值
        _o_combox.currentIndexChanged.connect(self._on_combox_idx_changed) # 当前索引变化时
        return _o_combox

    def _on_combox_idx_changed(self):
        _o_combox = self.sender()
        _i = _o_combox.currentIndex() # 获取当前索引
        _s = _o_combox.currentText()  # 获取当前文本

        self.o_label.setText(f'{_i}: {_s}')

    def _gen_layout(self):
        _o_layout = QtWidgets.QHBoxLayout()
        _o_layout.addWidget(self.o_combox)
        _o_layout.addSpacing(10)
        _o_layout.addWidget(self.o_label)
        return _o_layout

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

14 QRadioButton(单选框)

效果图:

说明:
同一父容器下的QRadioButton都是互斥的, 要对它们分组有两种方法:

  1. 不同组的QRadioButton放到不同的父容器中.
  2. 不同组的QRadioButton使用QButtonGroup.

不过QButtonGroup是不可见的组件, 它也没有什么好用的方法,
所以我们倾向于定义一个容器专门存放QRadioButton, 并在容器中定义些好用的method.

代码:

import sys
import time
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui

class CMyWidgetRadioButton(QtWidgets.QWidget):
    toggled = QtCore.pyqtSignal()

    def __init__(self, parent=None, list_items=None):
        super().__init__(parent)

        self._list_items = list_items if list_items!=None else []

        self._list_radio_btn = self._gen_list_radio_btn()

        self.setLayout(self._gen_layout())

    def _gen_list_radio_btn(self):
        _list = list()
        for _s_text in self._list_items:
            _o_radio_btn = QtWidgets.QRadioButton()
            _o_radio_btn.setText(str(_s_text)) # radio button设置文本
            _o_radio_btn.toggled.connect(self._on_radio_btn_toggled) # 选择情况切换时
            _list.append(_o_radio_btn)
        return _list

    def _on_radio_btn_toggled(self):
        self.toggled.emit()

    def _gen_layout(self):
        _o_layout = QtWidgets.QVBoxLayout()
        for _o_radio_btn in self._list_radio_btn:
            _o_layout.addWidget(_o_radio_btn)

        return _o_layout

    def current_idx(self):
        _i_out = None
        for _i, _o_radio_btn in enumerate(self._list_radio_btn):
            if _o_radio_btn.isChecked()==True: # radio button是否选中
                _i_out = _i
                break
            else:
                continue
        return _i_out

    def current_text(self):
        _s_out = None

        _i = self.current_idx()
        if _i!=None:
            _s_out = self._list_radio_btn[_i].text() # 获取radio button的文本
        else:
            _s_out = None
        return _s_out

class Demo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.o_widget_radio_0 = self._gen_widget_radio_btn_0() # 把一组RadioButton都放到这个Widget中.
        self.o_widget_radio_1 = self._gen_widget_radio_btn_1() # 把一组RadioButton都放到这个Widget中.
        self.o_label = QtWidgets.QLabel('Null')

        self.setLayout(self._gen_layout())
        self.resize(200, 100)

    def _gen_widget_radio_btn_0(self):
        _o_widget = CMyWidgetRadioButton(list_items=['苹果', '香蕉', '西瓜', '桔子'])
        _o_widget.toggled.connect(self._on_widget_radio_btn_toggled)
        return _o_widget

    def _gen_widget_radio_btn_1(self):
        _o_widget = CMyWidgetRadioButton(list_items=['一个', '二个', '三个'])
        _o_widget.toggled.connect(self._on_widget_radio_btn_toggled)
        return _o_widget

    def _on_widget_radio_btn_toggled(self):
        _s_0 = self.o_widget_radio_0.current_text()
        _s_1 = self.o_widget_radio_1.current_text()
        self.o_label.setText(f'{_s_0}, {_s_1}')

    def _gen_layout(self):
        _o_layout_btn = QtWidgets.QHBoxLayout()
        _o_layout_btn.addWidget(self.o_widget_radio_0)
        _o_layout_btn.addWidget(self.o_widget_radio_1)

        _o_layout_label = QtWidgets.QVBoxLayout()
        _o_layout_label.addWidget(self.o_label)

        _o_layout_main = QtWidgets.QVBoxLayout()
        _o_layout_main.addLayout(_o_layout_btn)
        _o_layout_main.addLayout(_o_layout_label)
        return _o_layout_main

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

15 QCheckBox(复选框)

效果图:

代码:

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui

class CMyWidgetCheckBox(QtWidgets.QWidget):
    state_changed = QtCore.pyqtSignal()

    def __init__(self, parent=None, list_items=None):
        super().__init__(parent)

        self._list_items = list_items if list_items!=None else []

        self._list_chkbox = self._gen_list_chkbox()

        self.setLayout(self._gen_layout())

    def _gen_list_chkbox(self):
        _list = list()
        for _s_text in self._list_items:
            _o_chkbox = QtWidgets.QCheckBox()
            _o_chkbox.setText(str(_s_text)) # chkbox设置文本
            _o_chkbox.stateChanged.connect(self._on_chkbox_state_changed) # 选择情况切换时
            _list.append(_o_chkbox)
        return _list

    def _on_chkbox_state_changed(self):
        self.state_changed.emit()

    def _gen_layout(self):
        _o_layout = QtWidgets.QVBoxLayout()
        for _o_chkbox in self._list_chkbox:
            _o_layout.addWidget(_o_chkbox)

        return _o_layout

    def list_checked_idx(self):
        _list_out = list()
        for _i, _o_chkbox in enumerate(self._list_chkbox):
            if _o_chkbox.isChecked()==True:
                _list_out.append(_i)
            else:
                continue
        return _list_out

    def list_checked_text(self):
        _list_out = list()

        for _i in self.list_checked_idx():
            _s_text = self._list_chkbox[_i].text()
            _list_out.append(_s_text)
        return _list_out

class Demo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.o_widget_chkbox_0 = self._gen_widget_chkbox_0() # 把一组CheckBox都放到这个Widget中.
        self.o_widget_chkbox_1 = self._gen_widget_chkbox_1() # 把一组CheckBox都放到这个Widget中.
        self.o_label = QtWidgets.QLabel('Null')

        self.setLayout(self._gen_layout())
        self.resize(200, 100)

    def _gen_widget_chkbox_0(self):
        _o_widget = CMyWidgetCheckBox(list_items=['苹果', '香蕉', '西瓜', '桔子'])
        _o_widget.state_changed.connect(self._on_widget_chkbox_state_changed)
        return _o_widget

    def _gen_widget_chkbox_1(self):
        _o_widget = CMyWidgetCheckBox(list_items=['一个', '二个', '三个'])
        _o_widget.state_changed.connect(self._on_widget_chkbox_state_changed)
        return _o_widget

    def _on_widget_chkbox_state_changed(self):
        _list_text_0 = self.o_widget_chkbox_0.list_checked_text()
        _list_text_1 = self.o_widget_chkbox_1.list_checked_text()
        self.o_label.setText(f'{" ".join(_list_text_0)}\n{" ".join(_list_text_1)}')
        pass

    def _gen_layout(self):
        _o_layout_btn = QtWidgets.QHBoxLayout()
        _o_layout_btn.addWidget(self.o_widget_chkbox_0)
        _o_layout_btn.addWidget(self.o_widget_chkbox_1)

        _o_layout_label = QtWidgets.QVBoxLayout()
        _o_layout_label.addWidget(self.o_label)

        _o_layout_main = QtWidgets.QVBoxLayout()
        _o_layout_main.addLayout(_o_layout_btn)
        _o_layout_main.addLayout(_o_layout_label)
        return _o_layout_main

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

16 QPushButton

def __init__(self):
    self.o_btn = QtWidgets.QPushButton('按钮')
    self.o_btn.clicked.connect(self.on_btn_clicked)
    self.o_btn.clicked.connect(lambda: self.on_btn_clicked_with_arg(arg))

def on_btn_clicked(self):
    print('clicked')

def on_btn_clicked_with_arg(self, arg):
    print(f'clicked {arg}')

17 QThread

QThread用于多线程, 比如按下按钮后, 操作的比较复杂, 在单线程下会导致主界面卡死, 这时可以把复杂的操作放到QThread中, 这样主界面就不会卡死了.

import sys
import datetime
import time

from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui

class CDemo(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.o_btn = self._gen_btn()
        self.o_thread = self._gen_thread()

        self.setLayout(self._gen_layout())
        self.setGeometry(200, 200, 500, 200)
        #self.resize(400, 300)

    def _gen_btn(self):
        _o_btn = QtWidgets.QPushButton('调用QThread')
        _o_btn.clicked.connect(self._on_btn_clicked)
        return _o_btn

    def _gen_thread(self):
        _o_thread = CMyThread()
        _o_thread.thread_begin.connect(self._on_thread_begin)
        _o_thread.thread_running.connect(self._on_thread_running)
        _o_thread.thread_end.connect(self._on_thread_end)
        return _o_thread

    def _on_thread_begin(self, s): # 接收到开始信号时的操作
        print(f'{s}')
        self.o_btn.setText(s)

    def _on_thread_running(self, s):
        pass
        #self.o_btn.setText(s)

    def _on_thread_end(self, s):
        pass
        #self.o_btn.setText(s)

    def _on_btn_clicked(self):
        self.o_thread.my_start() #调用thread的函数, 主界面不会卡死

    def _gen_layout(self):
        _o_layout = QtWidgets.QHBoxLayout()

        _o_layout.addStretch(1)
        _o_layout.addWidget(self.o_btn)
        _o_layout.addStretch(1)

        return _o_layout

class CMyThread(QtCore.QThread):
    thread_begin   = QtCore.pyqtSignal(str) #自定义信号, 用于向主界面传递信息
    thread_running = QtCore.pyqtSignal(str)
    thread_end     = QtCore.pyqtSignal(str)

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

        #self.thread_begin.connect(self._on_thread_begin)
        #self.thread_running.connect(self._on_thread_running)
        #self.thread_end.connect(self._on_thread_end)

    def my_start(self):
        self.thread_begin.emit(f'begin at {self._get_time_str()}') #发射信号, 可在主界面接收
        self.sleep(1)
        self.start() # QThread内置函数, 运行QThread, 它会调用run()

    def run(self): # QThread内置函数, 可以重新定义, 写上我们要做的操作, 会被start()调用
        for i in range(4, -1, -1):
            self.thread_running.emit(f'running {i} at {self._get_time_str()}')
            self.sleep(1)

        self.thread_end.emit(f'end at {self._get_time_str()}') # 线程结束, 向主界面发信号, 计算返回值也可以在这传回主界面.

    def _get_time_str(self):
        _o_dt = datetime.datetime.now()
        return _o_dt.isoformat()

if __name__ == '__main__':
    o_app = QtWidgets.QApplication(sys.argv)
    o_demo = CDemo()
    o_demo.show()
    sys.exit(o_app.exec_())

18 pyqtSignal(信号)

见第17节, QThread, 里面定义了信号

19 QDateEdit/QTimeEdit/QDateTimeEdit

# QDateEdit
o_date_edit = QtWidgets.QDateEdit(QtCore.QDate.currentDate(), self)
o_date_edit.setDisplayFormat('yyyy-MM-dd')
o_date_edit.setCalendarPopup(True)
o_date_edit.setFixedWidth(150)

# QDateEdit.date()返回类型为QtCore.QDate
i_year  = o_date_edit.date().year()
i_month = o_date_edit.date().month()
i_date  = o_date_edit.date().day()

# QTimeEdit
o_time_edit = QtWidgets.QTimeEdit()
o_time_edit.setTimeRange(QtCore.QTime(7, 0), QtCore.QTime(11, 0))
o_time_edit.setTime(QtCore.QTime(9, 0))
o_time_edit.setCalendarPopup(True)
o_time_edit.setFixedWidth(150)

# QTimeEdit.time()返回类型为QtCore.QTime
i_hour   = o_time_edit.time().hour()
i_minute = o_time_edit.time().minute()

# QDateTimeEdit
o_dte = QtWidgets.QDateTimeEdit(QtCore.QDateTime.currentDateTime(), self)
o_dte.setDisplayFormat("yyyy-MM-dd HH:mm")
o_dte.setCalendarPopup(True)
o_dte.dateTimeChanged.connect(self.on_date_time_changed)

19 关闭确认 closeEvent()


import sys
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui

class CDemo(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.o_btn = self._gen_btn()

        self.setLayout(self._gen_layout())

    # 重写closeEvent, 点击关闭按钮或执行close()函数时, 先确认再退出
    def closeEvent(self, event):
        reply = QtWidgets.QMessageBox.question(
            self,
            '警告',
            f'是否退出?',
            QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
            QtWidgets.QMessageBox.No,
        )

        if reply == QtWidgets.QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()


    def _gen_btn(self):
        _btn = QtWidgets.QPushButton(f'退出')
        #_btn.clicked.connect(QtCore.QCoreApplication.instance().quit) #直接退出
        _btn.clicked.connect(self.close) # 执行close(), 会触发clockEvent

        return _btn

    def _gen_layout(self):
        _o_layout = QtWidgets.QVBoxLayout()
        _o_layout.addWidget(self.o_btn)
        return _o_layout

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

20 QMainWindow/QMenuBar(主窗口/菜单)

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui

class CDemo(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.o_btn = self._gen_btn()
        self.o_menubar = self._get_menubar()

        # 注意, QMainWindow不般不使用setLayout(),
        #       而是使用setCentralWidget()
        #       在centralWidget中再setLayout()
        #self.setLayout(self._gen_layout())
        self.setCentralWidget(self._gen_central_widget())

    def _get_menubar(self):
        # 注意, 只有QMainWindow有menuBar(),
        #       QWidget()没有menuBar(),
        # 所以, 如果想使用菜单, 就需要使用QMainWindow, 不能用QWidget()
        _o_menubar = self.menuBar()

        _o_menu_file = _o_menubar.addMenu('&File')
        _o_menu_file.addAction(self._gen_action_exit())

        return _o_menubar

    def _gen_action_exit(self):
        _o_action = QtWidgets.QAction('&Exit', self)
        _o_action.triggered.connect(QtWidgets.qApp.quit)
        return _o_action

    def _gen_btn(self):
        _btn = QtWidgets.QPushButton(f'退出')
        _btn.clicked.connect(self.close) # 执行close(), 会触发clockEvent

        return _btn

    def _gen_central_widget(self):
        _o_widget = QtWidgets.QWidget()
        _o_widget.setLayout(self._gen_layout())
        return _o_widget

    def _gen_layout(self):
        _o_layout = QtWidgets.QVBoxLayout()
        _o_layout.addWidget(self.o_btn)
        return _o_layout

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

21 QPaint(绘制内容)

from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets

import sys

class CDeskClock(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        #self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        #self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

        self.setFixedSize(600, 250)

        self._mousePos = None
        self._tracking = False

        self.setWindowTitle('时间')

        # 计时器每100ms重新计时.
        # 计时到100ms时调用update(), 会触发paintEvent()
        self.timer = QtCore.QTimer()
        self.timer.start(100)
        self.timer.timeout.connect(self.update)

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.setPen(self._gen_pen())     #设置线条属性
        painter.setBrush(self._gen_brush()) #设置填充属性

        #在窗口对应坐标绘制文本, 还可以绘制线条, 形状, 图片等.
        time = QtCore.QTime.currentTime()
        painter.drawText( 50, 50, str(time.hour()))
        painter.drawText( 80, 50, str(time.minute()))
        painter.drawText(110, 50, str(time.second()))

    def _gen_pen(self):
        pen = QtGui.QPen()
        pen.setWidth(1)
        pen.setColor(QtCore.Qt.black)
        #pen.setColor(QtGui.QColor(240,240,240))
        pen.setStyle(QtCore.Qt.SolidLine)
        pen.setCapStyle(QtCore.Qt.FlatCap)
        pen.setJoinStyle(QtCore.Qt.BevelJoin)
        return pen

    def _gen_brush(self):
        brush = QtGui.QBrush()
        brush.setColor(QtCore.Qt.blue)
        brush.setStyle(QtCore.Qt.SolidPattern)
        return brush

if __name__=='__main__':
    app = QtWidgets.QApplication(sys.argv)
    win = CDeskClock()
    win.show()
    app.exec_()
posted @ 2020-07-09 10:48  编程驴子  阅读(781)  评论(0编辑  收藏  举报