添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

需要菜单栏的,通常是 QMainWindow 类型的窗口, 我们可以使用代码创建菜单栏内容,如下

from PySide6 import QtWidgets
from PySide6.QtGui import QIcon
class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.resize(600,200)
        # 创建 菜单栏QMenuBar 对象 并返回
        menuBar = self.menuBar()
        # 一级菜单
        fileMenu = menuBar.addMenu("文件")
        editMenu = menuBar.addMenu("编辑")
        helpMenu = menuBar.addMenu("帮助")
        # 一级Action
        actionHomePage = menuBar.addAction('主页')
        actionHomePage.triggered.connect(self.actionHomePageClicked)
        # 一级菜单的 action项
        actionAddNode = fileMenu.addAction(QIcon("./Images/folder.png"),"添加")
        fileMenu.addSeparator() # 分隔符
        actionDelNode = fileMenu.addAction("删除")
        actionAddNode.triggered.connect(self.actionAddNodeClicked)
        actionDelNode.triggered.connect(self.actionDelNodeClicked)
        # 二级菜单
        edit_1 = editMenu.addMenu("插入图表")
        edit_2 = editMenu.addMenu("插入图片")
        # 二级菜单的 action项
        action1 = edit_1.addAction("action1")
        action2 = edit_1.addAction("action2")
    def actionHomePageClicked(self):
        print('actionHomePageClicked')
    def actionAddNodeClicked(self):
        print('actionAddNodeClicked')
    def actionDelNodeClicked(self):
        print('actionDelNodeClicked')
app = QtWidgets.QApplication()
ex = Window()
ex.show()
app.exec()

大家可以运行一下上面的代码,看一下界面。

菜单层级结构是这样的:

菜单栏(QMenuBar) ->  菜单(QMenu) -> QAction

当然,菜单里面还可以有子菜单,就是这样

菜单栏(QMenuBar) ->  菜单(QMenu)->  子菜单(QMenu) -> QAction

注意:上面说的子菜单,并不是一种新的类型,也是 QMenu , 只是从属于其他Qmenu, 就像 QLayout 形成的层级关系一样。

菜单里面 点击能触发操作的条目,称之为 QAction ,中文叫 动作

也可以在 Qt Designer上很方便的为 QMainWindow 类型的窗口添加菜单,如下所示

点击 菜单Action, 会触发信号 triggered, 处理点击菜单的的代码如下

self.ui.actionOpenFile.triggered.connect(self.openPageFile)
        # 添加 工具栏 条目Action
        actionAddNode = toolbar.addAction(QIcon("./Images/folder.png"),"添加")
        actionAddNode.triggered.connect(self.actionAddNodeClicked)
        action = toolbar.addAction("删除")
        action = toolbar.addAction("修改")
        action = toolbar.addAction("查询")
        toolbar.addSeparator()             # 添加分隔符
        action = toolbar.addAction("帮助")
    def actionAddNodeClicked(self):
        print('actionAddNodeClicked')
app = QtWidgets.QApplication([])
ex = Window()
ex.show()
app.exec()

如果 菜单栏 和 工具栏有 相同的 action ,可以公用一个Action对象。

也可以在 Qt Designer上很方便的为 QMainWindow 类型的窗口添加工具栏。

右键点击 Main Window 类型的窗体空白处,如下所示

选择添加工具栏

注意,只有 Main Window 类型的窗体,才能添加工具栏,如下

添加工具栏后,还要在工具栏上添加 条目Action (中文称之为: 动作 )。

方法是点击右下角 动作编辑器 ,新建动作,如下图所示

然后如下图所示进行设置

添加动作成功后,就可以直接拖到工具栏上了。

然后,在代码中定义动作触发后的处理函数,如下所示

self.ui.actionAddNote.triggered.connect(self.actionAddNode)

如果菜单和工具栏有 相同的 action ,通常是先在 动作编辑器 创建一个action, 然后分别拖动到 菜单 和 工具栏

状态栏通常显示在窗口底部,对应的控件类型是: QStatusBar

需要底部状态栏的,通常是 QMainWindow 类型的窗口, 用 Qt Designer 设计的Qt Designer, 会自带状态栏,缺省属性名称为 statusbar

要在状态栏显示文本信息,只需要调用 状态栏 QStatusBar 的 showMessage 方法,如下

self.ui.statusbar.showMessage(f'打开文件{filePath}')

QMessageBox 类可以用来弹出各种提示框

该类可以通过一系列静态方法,显示 如下弹出框

使用 critical 方法

from PySide6.QtWidgets import QMessageBox
QMessageBox.critical(
    self.ui,
    '错误',
    '请选择爬取数据存储路径!')

使用 warning 方法

from PySide6.QtWidgets import QMessageBox
QMessageBox.warning(
    self.ui,
    '阅读太快',
    '阅读客户协议必须超过1分钟')

使用 information 方法

from PySide6.QtWidgets import QMessageBox
QMessageBox.information(
    self.ui,
    '操作成功',
    '请继续下一步操作')

也可以使用 about 方法

from PySide6.QtWidgets import QMessageBox
QMessageBox.about(
    self.ui,
    '操作成功',
    '请继续下一步操作')

使用 question 方法

from PySide6.QtWidgets import QMessageBox
choice = QMessageBox.question(
    self.ui,
    '确认',
    '确定要删除本文件吗?')
if choice == QMessageBox.Yes:
    print('你选择了yes')
if choice == QMessageBox.No:
    print('你选择了no')

输入对话框

QInputDialog 输入对话框 只让用户输入一行数据信息,比如 姓名、年龄等。

可以方便的用来获取简单的信息。

from PySide6.QtWidgets import QInputDialog,QLineEdit
# 返回值分别是输入数据 和 是否点击了 OK 按钮(True/False)
title, okPressed = QInputDialog.getText(
    self, 
    "输入目录名称",
    "名称:",
    QLineEdit.Normal,
if not okPressed:
    print('你取消了输入')

常用的方法有:

  • getText
  • 弹出对话框,让用户输入 单行文本

  • getMultiLineText
  • 弹出对话框,让用户输入 多行文本

  • getInt
  • 弹出对话框,让用户输入 整数

  • getItem
  • 弹出对话框,让用户选择 选项

    items = ["春天", "夏天", "秋天", "冬天"]
    item, ok = QInputDialog().getItem(self, 
                                      "请选择",
                                      "季节:", 
                                      items, 
                                      False)
    if ok and not item.isEmpty():
        itemLabel.setText(item)
    

    Qt程序可以获取和设置剪贴板内容

    from PySide6.QtGui import QGuiApplication
    cb = QGuiApplication.clipboard()
    # 获取剪贴板内容
    originalText = cb.text()
    # 设置剪贴板内容
    clipboard.setText(newText)
    

    点击这里,边看视频讲解,边学习下面的内容

    QTreeWidget 树控件 树控件, 是和 QTreeWidgetItem 树节点控件 结合使用的。

    如下图所示

    QTreeWidget 官网介绍

    QTreeWidgetItem 官网介绍

    树控件用来展示树状层级结构的数据,典型例子有: 文件目录结构、知识点结构

    如果用 Qt 设计师 工具 制作界面,首先应该设置好 QTreeWidget 的一些属性,如下

    其中,常见的设置是 :

  • 设置有几个column ,也就是有几列,
  • 双击树控件,就可以编辑列名,如下图

  • 可以设置列标头 是否可见
  • 设置好 QTreeWidget 的属性以后,代码中加载界面资源文件,就可以在树上添加节点了,如下代码所示

    注意:QTreeWidget 有一个不可见根节点 ,树的第一层节点 都是这个 不可见根节点的 子节点,它是整个树的根。 可以通过 invisibleRootItem 方法获得。

    from PySide6.QtWidgets import QApplication
    from PySide6.QtUiTools import QUiLoader
    # 导入 QTreeWidget, QTreeWidgetItem, QIcon
    from PySide6.QtWidgets import  QTreeWidget, QTreeWidgetItem
    from PySide6.QtGui import QIcon
    class SomeWindow:
        def __init__(self):
            self.ui = QUiLoader().load('main.ui')
            # 调用设置树控件
            self.setupTree()
        def setupTree(self):
            # 获取树控件对象
            tree = self.ui.tree
            # 隐藏标头栏
            tree.setHeaderHidden(True)
            # 获取树控件的不可见根节点
            root = tree.invisibleRootItem()
            # 准备一个folder节点
            folderItem = QTreeWidgetItem()
            # 创建图标对象
            folderIcon = QIcon("./Images/folder.png")
            # 设置节点图标
            folderItem.setIcon(0, folderIcon)
            # 设置该节点  第1个column 文本
            folderItem.setText(0, '学员李辉')
            # 添加到树的不可见根节点下,就成为第一层节点
            root.addChild(folderItem)
            # 设置该节点为展开状态
            folderItem.setExpanded(True)
            # 准备一个 叶子 节点
            leafItem = QTreeWidgetItem()
            leafIcon = QIcon("./Images/leaf.gif")
            # 设置节点图标
            leafItem.setIcon(0, leafIcon)
            # 设置该节点  第1个column 文本
            leafItem.setText(0, '作业 - web自动化1')
            # 设置该节点  第2个column 文本
            leafItem.setText(1, '提交日期20200101')
            # 添加到 叶子节点 到 folerItem 目录节点下
            folderItem.addChild(leafItem)
    app = QApplication([])
    w = SomeWindow()
    w.ui.show()
    app.exec_()
    

    上面的程序运行后,大概的结果图如下

    每个 节点 都是 QTreeWidgetItem 对象

    添加节点 必须通过 该节点的 父节点

    可以使用 addChild 方法,添加到最后

    也可以使用 insertChild 方法,插入到指定位置,比如

    folderItem.insertChild(2, leafItem)
    

    就插入到 第3个 子节点的位置上,因为第1个子节点的索引是0。

  • 如果控件宽度不够,字符串会显示为省略号。
  • 列的宽度,可以在界面上 该 QTreeWidget 控件的属性编辑里面, 调整 headerDefaultSectionSizeheaderStretchLastSection 来控制。

    也可以通过代码来控制,如下

        def setupTree(self):
            # 其他代码...
            tree.setColumnWidth(0, 200) # 设置第一列宽度,200px
            header = tree.header()
            header.setStretchLastSection(True)
    

    如果想让列宽度 自适应 内容,使用 下面的方法

    from PySide6.QtWidgets import QHeaderView
        def setupTree(self):
            # 其他代码...
            header = tree.header()
            # 让列宽度 自适应 内容
            header.setSectionResizeMode(QHeaderView.ResizeToContents)
    

    当前选中节点

    我们有时要在当前节点下面添加节点、删除当前节点,这就需要先获取当前节点。

    QTreeWidgetcurrentItem 方法可以获取当前节点。

    下面是一段 在选中节点下面添加子节点 的示例代码

    from PySide6.QtWidgets import QLineEdit, QInputDialog
    from PySide6.QtCore import Qt
    class SomeWindow:
        def __init__(self):
            self.ui = QUiLoader().load('main.ui')
            self.ui.btn_add.clicked.connect(self.addChildNode)
        # 其他部分代码
        def addChildNode(self):
            # 获取树控件对象
            tree = self.ui.tree
            # 获取当前用户点选的节点
            currentItem = tree.currentItem()
            # 没有当前选中节点,不可见根节点作为当前节点
            if not currentItem:
                currentItem = tree.invisibleRootItem()
            # 当前节点的第一列文本内容
            col1Text = currentItem.text(0)
            print(col1Text)
            # 让用户输入信息
            title, okPressed = QInputDialog.getText(
                    self.ui, "输入名称", "名称:",
                    QLineEdit.Normal, "")
            if not okPressed or title == '':
                return
            # 准备一个folder节点
            folderItem = QTreeWidgetItem()
            # 创建图标对象
            folderIcon = QIcon("./images/folder_close.png")
            # 设置节点图标
            folderItem.setIcon(0, folderIcon)
            # 设置该节点  第1个column 文本
            folderItem.setText(0, title.strip())
            # 设置该节点在以前的flag基础上,多一个可编辑 ItemIsEditable
            folderItem.setFlags(folderItem.flags() | Qt.ItemIsEditable)
            currentItem.addChild(folderItem)
            currentItem.setExpanded(True)
    

    获取父节点

    获取一个节点的父节点是调用 该QTreeWidgetItem节点的 parent 方法

        # 获取某个节点item的父节点item
        parentItem = treeItem.parent()
        # 如果返回值为None,其必定为顶层节点,它的父节点是不可见的根节点
        if not parentItem:
            parentItem = tree.invisibleRootItem()
    

    删除 QTreeWidget 的节点, 是 通过其父节点调用 removeChild 方法。

    示例代码如下:

        # 获取当前用户点选的节点
        currentItem = tree.currentItem()
        # 如果没有当前选中节点
        if not currentItem:
            return
        # 找到改节点的父节点
        parentItem = currentItem.parent()
        # 如果没有父节点,就是不可见父节点
        if not parentItem:
            parentItem = tree.invisibleRootItem()
        # 删除该节点
        parentItem.removeChild(currentItem)
    

    注意:删除一个节点,同时也就删除了其所有子节点。

    删除所有节点

    可以使用 QTreeWidget 的方法 clear,如下

    tree.clear()
    

    也可以删除不可见根节点的所有子节点的方法,如下

    root = tree.invisibleRootItem()
    for i in reversed(range(root.childCount())):
        root.removeChild(root.child(i))
    

    编辑节点文本

    假设我们要 实现,双击节点文本 可以 编辑节点文本,就是如下所示

    首先必须设置节点flag为 : 可编辑 ItemIsEditable

    可以在创建节点的时候设置,如下代码所示

    from PySide6.QtCore import Qt
    # 准备一个folder节点
    folderItem = QTreeWidgetItem()
    folderItem.setIcon(0, folderIcon)
    folderItem.setText(0, '白月黑羽学员李辉')
    # 设置该节点在以前的flag基础上,多一个可编辑 ItemIsEditable
    folderItem.setFlags(folderItem.flags()  | Qt.ItemIsEditable)
    

    QTreeWidget 常见的信号有:单击节点、双击节点、当前节点变动 等等。

    详细信息参考官方文档

    大家可以根据自己的需要,定义信号处理函数

    比如,可以这样定义节点 被点击事件 的处理函数

    class SomeWindow:
        def __init__(self):
            self.ui = QUiLoader().load('main.ui')
            # 添加 被点击事件 的处理函数
            self.ui.tree.itemClicked.connect(self.itemClicked)
        # 参数 item 是被点击节点对应的 QTreeWidgetItem 对象
        # 参数 column 是被点击的column号
        def itemClicked(self, item, column):
            # 获取被点击的节点文本
            clickedText = item.text(column)
            if 'bottle' in clickedText:
                self.bottle()
            elif 'cube' in clickedText:
                self.cube()
    

    再看一个例子,可编辑的节点文本被编辑改变后,其所属 QTreeWidget 就会触发 itemChanged 信号

    我们应该定义该信号的处理函数,如下所示

    class SomeWindow:
        def __init__(self):
            self.ui = QUiLoader().load('main.ui')
            # 必须先加载树,然后再设置信号处理,
            # 否则加载过程也会触发 itemChanged 信号
            self.ui.tree.itemChanged.connect(self.itemChanged)
        def itemChanged(self, item, column):
            # 参数 item 是修改的 节点 QTreeWidgetItem对象
            # 参数 column 是修改的 列号
            newText = item.text(column)
            # 根据改变的 column 得知是哪个数据修改了
            # 保存到对应的数据库表记录中
    

    节点关联数据

    Python中, 类对象可以动态添加属性,

    所以我们可以根据需要,给 QTreeWidgetItem 节点对象 关联 各种类型的数据。

    如下所示:

        nodeitem = QTreeWidgetItem()
        nodeitem.setText(0, filename)
         # python 对象可以动态添加属性
        nodeitem._my_data1 = '给节点关联的数据,根据需要设定'
        nodeitem._my_data2 = ['其它数据1','其它数据2']
    

    这些关联的数据,可以在后续操作节点时,根据需要取出来使用。

    关联数据又什么用呢? 想象这样一个案例:

    我们用树控件显示一个目录下的文件。

    当用户双击节点,修改了文件的名字,代码应该 在 itemChanged 信号处理函数中真正修改该文件名。

    但是如果,用户修改的名字已经存在同名文件,就会产生冲突,这时,应该恢复该节点的名字为原来的名字。

    但是,界面上改为新名字了,原来名字丢失了怎么办?

    这时就可以使用 关联数据的方案:

  • 添加节点时,除了 这样 设置文件名,
  • nodeitem.setText(0, filename)
    

    再给节点动态添加一个属性,比如

    nodeitem._my_filename = filename
    

    备份一个文件名的记录

    后面如果修改节点名方法,发现名字冲突了,就改回原来的名字

        # 修改节点名
        def treeItemNameChanged(self, item, column):
            newName = item.text(column)
            filepath = os.path.join(self.thisFolderPath, newName)
            # 文件名冲突,还原
            if os.path.exists(filepath):
                QMessageBox.information(
                    self.ui, '错误',
                    f'文件 {filepath} 已经存在,请重新修改')
                # 原来的名字保存在自己定义的 _my_filename 属性中
                item.setText(0, item._my_filename)
                return
            # 不冲突的后续处理
            oriFilePath = os.path.join(self.thisFolderPath, item._my_filename)
            # 修改文件名
            os.rename(oriFilePath, filepath)
    

    获取节点的子节点

    QTreeWidgetItemchildCount() 方法可以获取子节点的个数

    QTreeWidgetItemchild() 方法可以获取子节点对应的 QTreeWidgetItem 对象,参数就是子节点的编号,从0开始。

    tree.child(0) 就是获取第1个子节点

    下面这段代码就是打印某个树节点的 所有直接子节点 文本

        def printChildren(parent):
            child_count = parent.childCount()
            for i in range(child_count):
                item = parent.child(i)
                print(item.text(0))
    

    如果要打印某个树节点的 所有后代节点 文本,最方便的就是用递归

        def printChildren(parent):
            child_count = parent.childCount()
            for i in range(child_count):
                item = parent.child(i)
                print(item.text(0))
                printChildren(item) # 调用自身,进行递归
    

    <!-- 下面是一段 递归的遍历树节点,保存数据的示例代码

        # 递归调用函数,完成整个树的遍历
        def iterateFunc(parent,dataTree):
            child_count = parent.childCount()
            for i in range(child_count):
                item = parent.child(i)
                # 保存这个节点item的信息到字典对象中
                subDictNode = {}
                dataTree.append(subDictNode)
                subDictNode['data'] = item.data
                subDictNode['children'] = []
                # 对该子节点递归调用遍历处理函数
                iterateFunc(item,subDictNode['children'])
        # 获取不可见根节点
        root = tree.invisibleRootItem()
        # 用嵌套列表来对应树的数据结构,方便保存到文件
        dataTree = [] 
        iterateFunc(root,dataTree)
        # 序列化到json文件,保存
        jsonStr = json.dumps(dataTree,ensure_ascii=False,indent=2)
        with open('data.json','w',encoding='utf8') as f:
            f.write(jsonStr)
    ``` -->
    ### 上下文菜单
    要用 树节点右键菜单功能可以这样处理
    先这样做初始化设置
    ```py
    from PySide6.QtWidgets import QMenu, QAction 
    from PySide6.QtCore import Qt
    # 其它代码
        def __init__(self):
            # 其它初始化代码
            # 设置上下文菜单策略,也可以在 Qt Designer上设置
            self.ui.tree.setContextMenuPolicy(Qt.CustomContextMenu)
            # 定义信号处理方法
            self.ui.tree.customContextMenuRequested.connect(
                self.show_context_menu_onfiletree)    
    

    然后,定义信号处理方法