Reisen1969 / Reisen1969.github.io.back

1 stars 0 forks source link

233 #2

Open Reisen1969 opened 2 years ago

Reisen1969 commented 2 years ago

https://blog.csdn.net/stpeace/article/details/80101441

本笔记 使用typora在本地编辑,图片无法显示,大多都是书上的截图

1.引言

1.1 研究的内容

应用程序
API(C Java JavaScript QT)
操作系统 (Java虚拟机 浏览器 图形系统 内核)
ISA(X86 ARM MIPS LoogArch)
硬件(CPU 内存 GPU 南北桥)
工艺模型
晶体管

API

一份相同的代码经过编译可以在API兼容的 不同系统 不同ISA上 运行

ABI

是编译器构建应用程序的方式。 它定义了事物(但不限于):

编译过生成的二进制文件,可以在ABI兼容的 不同系统上运行

CPU内部

控制单元

程序计数器PC(Program Counter) 指令寄存器IR(Instruction Register) 指令译码器ID(Instruction Decoder) 操作控制器OC(Operation Controller)

运算单元

完全由控制单元指挥, 算术运算 逻辑运算

存储单元

片内缓存 寄存器组 是cpu暂时存放数据的地方

控制单元在时序脉冲的作用下,将指令计数器里所指向的指令地址(这个地址是在内存里的)送到地址总线上去,然后CPU将这个地址里的指令读到指令寄存器进行译码。对于执行指令过程中所需要用到的数据,会将数据地址也送到地址总线,然后CPU把数据读到CPU的内部存储单元(就是内部寄存器)暂存起来,最后命令运算单元对数据进行处理加工。周而复始,一直这样执行下去

取指令-指令译码-执行指令(取操作数 进行运算)-修改指令计数器

总线

cpu和内存之间 直接 地址总线 数据总线相连接

cpu和pcie总线直接连接 pcie连gpu

image-20220314194750620

南北桥

CPU--北桥--南桥和高速设备(如内存 gpu)

北桥--南桥--低速设备(如硬盘 USB)

现在北桥被集成到CPU内部,

南桥改名为 IO HUB dma在南桥里

DMA

Direct Memory AccessDMA

传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。

DMA请求、DMA响应、DMA传输、DMA结束

按下按键发生的事

1.键盘会把键盘的编码传给 南桥,

2.南桥 通过 北桥 给CPU发送一个外部中断,产生例外指令

3.重排列缓冲将例外指令之前的执行完成,将之后的指令取消

4.把系统设置为 核心态 保存例外原因 发生例外的PC到相应的寄存器中,然后设置PC的值

5.OS保存处理器现场,发现是外部中断,向南桥读原因,发现是按下了某个键

6.OS查找这个键是给谁的?哪个程序正在堵塞等着键盘

7.程序读到案件后发现要显示图像,需要把内存中的内容送到显存

8.不通过CPU,使用DMA技术,将内容从内存送到显存

冯诺依曼架构

img

指令和数据都存储在一起,CPU 从内存中取指令和数据进行运算, 并且把结果也放在内存中。 概括起来 就是存储程序和指令驱动执行

什么都保存在内存中使访存成为性能瓶颈

哈佛架构

哈佛结构是一种将程序指令存储数据存储分开的存储器结构,如下图所示。中央处理器首先到程序指令存储器中读取程序指令内容,解码后得到数据地址,再到相应的数据存储器中读取数据,并进行下一步的操作(通常是执行)。程序指令存储和数据存储分开,可以使指令和数据有不同的数据宽度,如Microchip公司的PIC16芯片的程序指令是14位宽度,而数据是8位宽度。

img

哈佛结构的微处理器通常具有较高的执行效率。其程序指令和数据指令分开组织和存储的,执行时可以预先读取下一条指令。

哈佛结构是指程序和数据空间独立的体系结构, 目的是为了减轻程序运行时的访存瓶颈。

哈佛结构能基本上解决取指和取数的冲突问题。

1.2 衡量计算机的指标

性能

MFLOPS (Million FLoating point Operations Per Second), 即每秒做多少百万次浮点运算,

在指令系统确定后, 结构设计需要重点考虑如何降低每条指令的平均执行周期 (Cycles Per Instruction, 简称 CPI), 或提高每个时钟周期平均执行的指令数 ( Instructions Per Cycle, 简 称 IPC), 这是处理器微结构研究的主要内容。 CPI 就是一个程序执行所需要的总的时钟周期数 除以它所执行的总指令数, 反之则是 IPC。

价格

功耗

1.3 计算机体系结构的发展

指令集ISA

CISC复杂指令集

RISC复杂指令集

操作系统

虚拟内存

线性地址 = 虚拟地址

逻辑地址:程序自身看到的内存空间,需要被映射到物理地址中才能完成对内存的操作

基址寄存器 界限寄存器 用来划分进程的空间

MOV REG 1000

cpu需要访问1000这个虚拟地址

需要通过mmu 将虚拟地址转换成 物理地址

摩尔定律

发展障碍

1.复杂度

2.主频

3.功耗

2.指令系统

2.1 简介

指令系统是硬件和软件之间的桥梁

应用软件
操作系统 编译器 虚拟机
指令系统
微体系结构
电路与器件

2.2 指令系统的设计原则

兼容性 通用性 高效性 安全性

2.3 指令系统发展历程

CISC

指令长度可变

RISC

指令长度比较固定

VLIW

本质上是多条同时执行的指令的组合

CISC RISC 区别

CISC 的指令隐含有对总线的 load / store 操作,即 add, sub 等算术逻辑指令的操作数允许是一个内存地址,执行操作数为内存地址的算术逻辑指令会先将数据从内存加载到 位于 Execute Unit 里面的 ALU 中进行运算

RISC 的指令若要读写总线则需要使用显式 load / store 指令,除此之外其他类型的指令不能读写总线

2.4 指令系统组成

地址空间

寄存器空间 : 通用寄存器 专用寄存器 控制寄存器 通过寄存器号寻址

系统内存空间 : 通过访取指令中的访存地址寻址

广义的系统内存空间包括 IO 空间和内存空间, 不同指令集对系统内存空间的定义各不相 同。

IO空间

与内存空间独立的空间,同样利用IO空间可以操作数据,只不过是利用对应的IO端口操作函数

(类似单片机GPIO?)

指令使用数据的方式

堆栈型

堆栈型指令又称零地址指令, 其操作数都在栈顶, 在运算指令中不需要指定 操作数, 默认对栈顶数据进行运算并将结果压回栈顶。

PUSH A
PUSH B
ADD
POP C
累加器型

累加器型指令又称单地址指令, 包含一个隐含操作数———累加器, 另一个 操作数在指令中指定, 结果写回累加器中。

LOAD A
ADD B
STORE C
寄存器-存储器型

操作数为寄存器 和内存单元

LOAD R1,A
ADD R1,B
STORE C
寄存器-寄存器型

操作数只能来自寄存器

LOAD R1,A
LOAD R2,B
ADD R3,R1,R2
STORE C,R3

操作数

寻址方式

大小端

指令的操作分类

运算指令

负责算术 移位 逻辑运算

访存指令

负责对存储器的读写

转移指令

控制程序的流向 操作PC指针

条件转移、 无条 件转移、 过程调用和过程返回等类型

特殊指令

负责操作系统的特定用途

指令编码格式

指令编码就是操作数和操作码在整个指令码中的摆放方式。 CISC 指令系统的指令码长度 可变, 其编码也比较自由, 可依据类似于赫夫曼 (Huffman) 编码的方式将操作码平均长度缩 小。 RISC 指令系统的指令码长度固定, 因此需要合理定义来保证各指令码能存放所需的操作 码、 寄存器号、 立即数等元素。

2.5 RISC指令

image-20220314165118167

OP:操作码

OPX:辅助操作码

RS:源操作数

RD:目标操作数

Const:立即数

公共指令

load store指令

load: 内存 --> 通用寄存器

store:通用寄存器 --> 内存

ALU指令

算数运算 逻辑运算 移位运算 比较 等

控制流指令

跳转指令,会操作PC指针

2.6 C语言的机器表示

过程(函数)调用

• 调用者 (S) 将参数 (实参) 放入寄存器或栈中;

• 使用 BL 指令调用被调用者 (R);

• R 在栈中分配自己所需要的局部变量空间;

• 执行 R 过程;

• R 释放局部变量空间 (将栈指针还原); • R 使用 JR 指令返回调用者

流控制

辅助控制语句

goto

选择语句

if else

switch case

循环语句

4 软硬件协同

TLB就是软硬件协同的例子

4.1 ABI

abi一般由 指令架构 操作系统 编译工具链 决定

包含了:

处理器基础数据类型的大小、布局和对齐要求等; 寄存器使用约定。它约定通用寄存器的使用方法、别名等; 函数调用约定。它约定参数如何传递给被调用的函数、结果如何返回、函数栈帧如何组织等; 目标文件和可执行文件格式; 程序装载和动态链接相关信息; 系统调用和标准库接口定义; 开发环境和执行环境等相关约定。

寄存器约定

整数寄存器约定

这些寄存器在物理上没有什么区别,但是 人为的 给这些寄存器做了区别.给了他们特定的名字和使用方式

函数调用约定

整型调用规范

a.8个寄存器参与参数传递

b. 宽度XLEN,在一个 寄存器内传递 无可用的话 去栈上传递

​ 小于2*XLEN 两个寄存器传递,

大于 2*XLEN引用传递

c.struct /array 传递规则 跟上面一致

d.位域 小端排列

e.栈向下生长

硬件浮点调用规范

有一些浮点参数寄存器,跟上面差不多一致

进程虚拟地址空间

ISA OS ToolsChain 会管理,给每个进程提供了一个独立的虚拟地址空间

把内存空间分为:

用户可以访问的空间 只能操作系统访问的空间 操作系统也不允许访问的保留空间

栈帧布局

栈帧布局等 , 一些编译选项 是否不使用栈等 还有栈帧大小

栈帧相关的寄存器等

一些编译器选项 可以 让 简单的程序 不生成栈帧

4.2 上下文切换场景

cpu运行指令中 会根据需要 改变指令的执行流

函数调用

用户发起的指令流 和 上下文改变

伴随着栈帧的变化 外部寄存器的变化

异常和中断

会改变执行流

系统调用

会发生特权等级的切换

进程

进程控制块 程序 数据集合 组成

进程控制块 记录 虚拟地址 和 其他一些资源 属于进程上下文

线程

线程是程序代码的一个执行路径。一个进程可以包含多个线程,这些线程之间共享内存空间和打开文件等资源,但逻辑上拥有独立的寄存器状态和栈。现代系统的线程一般也支持线程私有存储区(Thread Local Storage, 简称 TLS) 。例如,GCC 编译器支持用 __thread int number; 这样的语句来定义一个线程私有的全局变量,不同线程看到的 number 地址是不一样的。

虚拟机

线程把一份 CPU 计算资源虚拟成多份独立的 CPU 计算资源,进程把 CPU 和物理内存的组合虚拟成多份独立的虚拟 CPU 和虚拟内存组合。更进一步,我们可以把一台物理计算机虚拟成多台含CPU、内存和各种外设的虚拟计算机。虚拟机可以更好地隔离不同的服务运行环境,更充分地利用越来越丰富的物理机资源,更方便地迁移和管理,因此得到了广泛的应用,成为云计算的基础技术。

4.3 同步机制

当线程之间出现资源访问的冲突时,需要有同步和通信的机制来保证并发数据访问的正确性。

同步机制通常包括基于互斥(Mutual Exclusive)和非阻塞(Non‐Blocking)两类。

互斥

对数据进行原子操作的程序段叫作临界区,在临界区前后应该包含申请锁和释放锁的过程,申请锁失败的线程被阻塞,占有锁的进程在完成临界区操作后应该及时释放锁。

非堵塞

5 计算机组成原理结构

5.1 冯诺依曼结构 哈佛结构

存储器 控制器 运算器 输入 输出 5部分

冯诺依曼结构的优化

以运算器为中心改进为以存储器为中心

速度差异,改为分散控制,异步的

流水线处理器

5.2 计算机的组成部件

运算器

算术计算 逻辑计算

ALU FPU 移位部件 条件码寄存器(保存当前运算结果的状态)

控制器

运算器和控制器 合称CPU

取指:从存储器取指

译码、

执行、

访存、

写回

使用流水线技术 提高效率

转移预测技术

典型应用程序平均每 5‐10 条指令中就有一条转移指令,而转移指令的后续指令需要等待转移指令执行结果确定后才能取指,导致转移指令和后续指令之间不能重叠执行,降低了流水线效率。

转移预测技术可以消除转移指令引起的指令流水线阻塞。转移预测器根据当前转移指令或其他转移指令的历史行为,在转移指令的取指或译码阶段预测该转移指令的跳转方向和目标地址并进行后续指令的取指。转移指令执行后,根据已经确定的跳转方向和目标地址对预测结果进行修正。如果发生转移预测错误,还需要取消指令流水线中的后续指令。为了提高预测精度并降低预测错误时的流水线开销,现代高性能处理器采用了复杂的转移预测器

乱序执行技术

乱序执行技术。如果指令 i 是条长延迟指令,如除法指令或 Cache 不命中的访存指令,那么在顺序指令流水线中指令 i 后面的指令需要在流水线中等待很长时间。乱序执行技术通过指令动态调度允许指令 i 后面的源操作数准备好的指令越过指令 i 执行(需要使用指令 i 的运算结果的指令由于源操作数没有准备好,不会越过指令 i 执行),以提高指令流水线效率

超标量技术

超标量结构允许指令流水线的每一阶段同时处理多条指令。

存储器

主存储器 or 内存

高速缓存
内存

输入输出设备(IO设备)

处理器通过读写 IO 设备控制器(南桥) 中的寄存器来访问及控制 IO 设备

高速 IO 设备可以在处理器安排下直接与主存储器成批交换数据,称为直接存储器访问(Directly Memory Access,简称 DMA)。处理器可以通过查询设备控制器状态与 IO 设备进行同步,也可以通过中断与 IO 设备进行同步。

DMA在南桥芯片内部.

GPU

cpu要将原始数据放入到内存

GPU通过DMA的方式读取数据,将计算结果写入到显存,最后将结果显示到屏幕

GPU 的作用是对图形 API 定义的流水线实现硬件加速

磁盘

永久记忆功能的存储体

容量大 价格便宜 速度慢

闪存

sd卡 固态硬盘 U盘等 价格贵

闪存的存储单元随着擦写次数的增多存在损坏的风险。为了解决这个问题,大多数 NAND 型闪存产品内部的控制器采用地址块重映射的方式来分布写操作,目的是将写次数多的地址转移到写次数少的块中。

5.3 计算机系统硬件结构

cpu

控制器 和 运算器

gpu

图形处理 计算加速

北桥

是离 CPU 最近的芯片,主要负责控制显卡、内存与 CPU 之间的数据交换,向上连接处理器,向下连接南桥。

已经被集成到cpu内部

南桥

低速IO接口

最终发展为:CPU-南桥两片结构

北桥被集成到CPU

CPU直接与内存和GPU通信

南桥与"北桥"连接

南桥上有SATA PCIE GMAC HDA USB等低速接口

SOC片上系统

它在单个芯片上集成了处理器、内存控制器、 GPU 以及硬盘、 USB、网络等 IO 接口,使得用户搭建计算机系统时只需要使用单个主要芯片即可,

5.4 处理器

控制器处于中心位置需要控制其他各个部件的运行。

对存储器的控制是通过读写指令来完成的。存储器是存储单元阵列,对某个地址的读写不会影响其他存储单元。

而 IO 设备大都是具有特定功能的部件,不能当作简单的存储阵列来处理。处理器通过读写 IO 寄存器来访问设备。写入这些寄存器的数据,会被设备控制器解析成命令,因此有些情况下将处理器对 IO 寄存器的访问称为命令字

IO寄存器寻址

内存映射

把 IO 寄存器的地址映射到内存地址空间中,这些寄存器和内存存储单元被统一编址。读写 IO 地址和读写内存地址使用相同的指令来执行。处理器需要通过它所处的状态来限制应用程序可以访问的地址空间,使其不能直接访问 IO 地址空间,从而保证应用程序不能直接操作IO 设备。

特殊 IO 指令

特殊 IO 指令使用专用指令来执行 IO 操作。因此, IO 地址空间可以和内存地址空间重叠,但实际指向不同的位置。操作系统可以通过禁止应用程序执行 IO 指令的方式来阻止应用程序直接访问 IO 设备

处理器和IO设备之间的同步

查询

处理器向 IO 设备发出访问请求后,需要不断读取 IO 设备的状态寄存器,所以查询方式也被称为轮询。由于 IO 设备的速度一般都较慢,使用查询方式会浪费处理器的指令周期。而且,执行轮询的处理器无法同时执行其他工作,造成了性能的浪费

中断

在中断方式下,处理器不需要轮询状态寄存器的值,而是在等待设备完成某个操作时转去执行其他进程。当设备完成某个操作后,自行产生一个中断信号来中断处理器的执行。处理器被中断后,再去读取设备的状态寄存器。中断方式将处理器从等待 IO 中解放了出来,大大提高了处理器的利用率,因此现代计算机的绝大部分 IO 设备都支持中断式。

a.中断源发出中断信号给中断控制器

b.中断控制器产生中断请求给CPU

c.CPU发出中断响应

d.CPU根据中断,执行对应的中断服务

e.CPU从中断服务程序返回,中断结束

中断控制器

用户敲击一下键盘,单击一下鼠标,或者 DMA 的一次传输完成了,对应的控制器会产生一个中断信号。中断信号可以是一根信号线,也可以是一个消息包。这个中断信息会传送到中断控制器中。中断控制器是负责中断汇集、记录和转发的硬件逻辑。中断控制器一般都具有可编程功能,因此被称为可编程中断控制器(Programmable InterruptController,简称PIC) 。典型的中断控制器如 Intel 的 8259A。 8259A 支持中断嵌套和中断优先级,可以支持 8 个中断源,并可以通过级联的方式进行扩展。

存储器和IO设备之间的数据传送

DMA

a.处理器为DMA请求预先分配一段时间

b.处理器设置DMA控制参数:设备标识,数据传送方向,内存中用于数据传送的 源地址 和 目标地址 传输的字节数量

c. DMA 控制器进行数据传输。 DMA 控制器发起对内存和设备的读写操作,控制数据传输。 DMA传输相当于用 IO 设备直接读写内存。

d.DMA控制器向处理器发出一个中断,同志处理器数据传送的结果

e.处理器完成本次DMA请求,可以开始新的DMA请求

一个计算机系统通常包含多个DMA控制器

6 计算机总线接口技术

6.1 概述

总线的本质作用是完成数据交换。总线用于将两个或两个以上的部件连接起来,使得它们之间可以进行数据交换,或者说通信。

1)机械层。接口的外形、尺寸、信号排列、连接线的长度范围等。 2)电气层。信号描述、电源电压、电平标准、信号质量等。 3)协议层。信号时序、握手规范、命令格式、出错处理等。 4)架构层。硬件模型、软件架构等。

6.2 分类

单向总线

双向总线 又称双工总线 (全 半)

并行总线 pci ddr

串行总线 usb sata pcie

6.3 片上总线

芯片上不同模块之间的总线

AMBA (Advanced Microcontroller Bus Architecture,高级微控制器总线架构

AXI总线

AXI 总线主要分为 5 个独立的通道,分别为写请求、写数据、写响应、读请求、读响应。每个通道采用握手协议独立传输。

1)单向通道体系结构。信息流只以单方向传输,符合片内设计的要求。 2)支持多项数据交换。AXI 协议支持的数据宽度很宽,最大可到 1024 位。通过并行执行突发(Burst)操作,极大地提高了数据吞吐能力,可在更短的时间内完成任务。 3)独立的地址和数据通道。地址和数据通道分开便于对每一个通道进行单独优化,可以根据需要很容易地插入流水级,有利于实现高时钟频率。

架构
互联
高频设计
基本事务

AHB ASB APB总线

都是在AXI之前的系统总线

6.4 内存总线

连接处理器 和 主存储器

DRAM 存储单元是按照 Bank、行、列来组织的,因此对 DRAM 的寻址是通过 bank 地址、行地址 和列地址来进行的。此外,计算机系统中可以将多组 DRAM 串接在一起,不同组之间通过片选(CS) 信号来区分。在计算机系统中,程序的地址空间是线性的,处理器发出的内存访问地址也是线性 的,由内存控制器负责将地址转换为对应于 DRAM 的片选、Bank 地址、行地址、列地址。

6.5 系统总线

系统总线通常用于处理器与桥片的连接,同时也作为多处理器间的连接以构成多路系统

HyperTransport 总线

用于连接 cpu和南桥

设备总线

pci总线

PCIE

设备总线用于计算机系统中与 IO 设备的连接

pcie总线 连接 北桥 和 其他设备(如网卡)

7 计算机启动过程分析

复位后第一条指令 都会从一个预先定义的特定地址取回

处理器的启动过程,实际上就是一个特定程序的执行过程。这个程序我们称之为固件,又称为 BIOS

7.1 处理器核初始化

寄存器不确定 --> 确定

处理器复位

7.2 总线接口初始化

7.3 设备的检测 及 驱动加载

7.4 多核启动过程

8 运算器

定点数整数原码

符号--数值

0正数

1负数

缺点:

1.存在两个0

2.原码加减运算复杂,加减时需要首先判断符号

现代计算机基本不用原码便是整数,原码仅在表示浮点数的尾数部分采用

定点数整数补码

最高位也同样是符号位

0正数

1负数

最高位是0时,原码与补码相同

最高位是1时,原码最高位不变,其余位按位取反后+1

减去一个数等于加上这个数的补码 ,只要有加法器就可以做减法。

溢出

加法溢出的判断方法是:如果 A 和 B 的最高位一样,但是 A+B 结果的最高位与 A 和 B 的最高位不一样,表示溢出,即两个正数相加得到负数,或两个负数相加得到正数。减法溢出的判断方法类似,即负数减正数结果是正数,或正数减负数结果是负数,这就表示溢出。

二进制科学记数法

(−1)^s × f × 2^e 其中 s 表示符号,f 为尾数域的值,e 为指数域的值(阶码)。

浮点数

image-20220318153923826

image-20220318154336300

MOS晶体管工作原理

image-20220318161110064

CMOS逻辑电路

尽管 MOS 晶体管可以表现出开关的行为,但是单纯的 PMOS 晶体管或者 NMOS 晶体管都不是理想的开关。例如,NMOS 晶体管适合传输 0 而不适合传输 1;PMOS 晶体管恰好相反,适合传输1而适合传输 0。

数字逻辑电路

根据电路是否具有数据存储功能,可将数字逻辑电路分为组合逻辑电路和时序逻辑电路两类

组合逻辑

image-20220318161340080

时序逻辑

时序逻辑电路包含时钟信号和数据存储单元两个要素。时序逻辑电路的特点在于,其输出不但与当前输入的逻辑有关,而且与在此之前曾经输入过的逻辑值有关。

image-20220318161459616

锁存器

image-20220318162548330

当(R,S)的值 从 (0,1) 或(1,0) 变成(1,1)时,能够保持输出值不变.

image-20220318162659968

非门

image-20220318103457636

与非门

image-20220318104954768

或非门

image-20220318105021556

传输门

image-20220318105043339

D触发器

image-20220318110028586

9 指令流水线

指令系统:为运算指令、访存指令、转移指令和特殊指令

・ 程序计数器,又称 PC,指示当前指令的地址; ・ 指令存储器,按照指令地址存储指令码,接收 PC,读出指令; ・ 译码部件,用于分析指令,判定指令类别; ・ 通用寄存器堆,用于承载寄存器的值,绝大多数指令都需要读取及修改寄存器; ・ 运算器,用于执行指令所指示的运算操作; ・ 数据存储器,按照地址存储数据,主要用于访存指令。

image-20220318142231051

根据pc从指令存储器中取出指令

译码部件解析出相关控制信号,并读取通用寄存器堆

运算器对通用寄存器堆读出的操作数进行计算,得到计算指令的结果写回通用寄存器堆,或者得到访存指令的地址,或者得到转移指令的跳转目标

load 指令访问数据存储器后,需要将结果写回通用寄存器堆。

通用寄存器堆写入数据在计算结果和访存结果之间二选一。

由于有控制流指令的存在,因此新指令的 PC 既可能等于顺序下一条指令的 PC(当前指令 PC 加 4),也可能来自转移指令计算出的跳转目标。

单周期处理器:每个指令在一个周期内完成

1.1

计算机系统可划分为哪几个层次,各层次之间的界面是什么?你认为这样划分层次的意义何在?

计算机系统可划分为四个层次:应用程序、操作系统、硬件系统、晶体管。应用程序与操作系统之间的界面是应用程序编程接口 API,操作系统与硬件系统之间的界面是指令系统 ISA,硬件系统与晶体管之间的界面是工艺模型。

这样划分层次,通过抽象的方式降低了上层用户操作计算机的复杂度,通过统一界面的方式提升了各个层次的可移植性和通用性。各个层次之间,在满足界面要求的情况下可以独立发展,促进了计算机的发展。

1.2

在三台不同指令系统的计算机上运行同一程序 P 时,A 机器需要执行 1.0 × 10^9 条指令,B 机器需要执行 2.0 × 10^9 条指令, C 机器需要执行 3.0 × 10^9 条指令,但三台机器的实际执行时间都是 100 秒。请分别计算出这三台机器的 MIPS,并指出运行程序 P 时哪台机器的性能最高。

mips即每秒执行多少百万条指令

A:10

B:20

C:30

性能一样高

1.3

假设某程序中可向量化的百分比为 P,现在给处理器中增加向量部件以提升性能,向量部件的加速比是 S。请问增加向量部件后处理器运行该程序的性能提升幅度是多少?

设原处理器执行这一程序需要 t 秒,则可向量化的部分需要 tP秒,其余部分需要 t - tP 秒。

增加向量部件后,可向量化的部分仅需 tP / S 秒,总运行时间为 t - tP + tP/S,其时间消耗是原来的 (1 - P + P/S) 倍。

性能提升幅度为:1 / (1 - P + P/S) - 1 = {PS - P}{S - PS + P}

1.4

处理器的功耗可简单分为静态功耗和动态功耗两部分,其中静态功耗的特性满足欧姆定律,动态功耗在其他条件相同的情况下与频率成正比。现对某处理器进行功耗测试,得到如下数据:关闭时钟,电压 1.0V 时,电流为 100mA;时钟频率为 1GHz,电压 1.1V 时,电流为 2100mA。请计算此处理器在时钟频率为 2GHz、电压为 1.1V 时的总功耗。

R = 10

P静 = U^2/R = 0.121

P动 = (1.12.1 - 0.121)\2

P总 = 4.499

2.1

请以某一种指令系统为例,说明其定义了哪些运行级别,以及这些运行级别之间的区别与联系。

img

2.2

Page_table * page_base = segment_table[segment];
void * phys_addr = offset + page_base[virtual_page] << PAGE_SIZE;

2.3

请简述桌面电脑 PPT 翻页过程中用户态和核心态的转换过程。

当按下键盘后,处理器接受到中断信号,从用户态切换到核心态以响应中断,随后回到用户态运行 PowerPoint;PowerPoint 调用显示驱动程序,处理器进入核心态以执行显示操作,随后回到用户态。

2.4

A = B + C
B = A + C
C = B + A

堆栈型

PUSH  B
PUSH  C
ADD
POP   A
PUSH  A
PUSH  C
ADD
POP   B
PUSH  B
PUSH  A
ADD
POP   C

累加器型

LOAD  B
ADD   C
STORE A
ADD   C
STORE B
ADD   A
STORE C

寄存器-存储器型

LOAD  R1, B
ADD   R1, C
STORE A, R1
ADD   R1, C
STORE B, R1
ADD   R1, A
STORE C, R1

寄存器-寄存器型

LOAD  R2, B
LOAD  R3, C
ADD   R1, R2, R3
STORE A, R1
ADD   R2, R1, R3
STORE B, R2
ADD   R3, R1, R2
STORE C, R3

令指令码宽度为 x 位,寄存器操作数宽度为 y 位,内存地址操作数宽度为 z 位,数据宽度为 w 位。

对于堆栈型:

对于累加器型:

对于寄存器-存储器型:

对于寄存器-寄存器型:

微处理器由 32 位时代进入 64 位时代,寄存器-寄存器型指令系统更好。其访问速度快,便于编译器调度优化,容易判断相关性,容易实现流水线、多发射、乱序执行等。

2.5

写出 0xDEADBEEF 在大尾端和小尾端下在内存中的排列 (由地址 0 开始)

每个内存可存放1个字节

大尾端:

0   1   2   3
DE  AD  BE  EF

小尾端:

0   1   2   3
EF  BE  AD  DE

2.6

在你的机器上编写 C 程序来得到不同数据类型占用的字节数,给出程序和结果。

#include <stdio.h>int main() {    printf("char            %ld\n", sizeof(char));    printf("short           %ld\n", sizeof(short));    printf("int             %ld\n", sizeof(int));    printf("long            %ld\n", sizeof(long));    printf("long long       %ld\n", sizeof(long long));    printf("float           %ld\n", sizeof(float));    printf("double          %ld\n", sizeof(double));    printf("long double     %ld\n", sizeof(long double));    return 0;}
char            1short           2int             4long            8long long       8float           4double          8long double     16

2.7

根据 LoongArch 指令集的编码格式计算 2RI16、1RI21 和 I26 三种编码格式的直接转移指令各自的跳转范围。

image-20220317154520147

rd目的操作数寄存器

rj rk ra 源操作数寄存器

Ixx 用于存放指令 立即数

指令中的立即数不仅作为运算型指令的源操作数,也作为 load/store 指令中相对于基地址的地址偏移以及转移指令中转移目标的偏移量

直接转移的转移目标地址可直接由指令得到,

2RI16 立即数 16位 寻址 2^16字节

1RI21 立即数 21位 寻址 2^21字节

I26 立即数 26位 寻址 2^26字节

2.8

仅使用对齐访存指令写出如图2.9的不对齐加载(小尾端)

image-20220317160557578

LW    R1, 0SRL   R1, 8LW    R2, 4SLL   R2, 24OR    R1, R1, R2

3.1

请说明 LoongArch 指令系统中为何要定义 ERTN 指令用于异常处理的返回

5.1

分别说明图5.6 – 5.9中四种结构中每个芯片包含冯·诺依曼结构五个部分的哪部分功能。

5.2

查阅资料,比较 Skylake 处理器和 Zen 处理器的运算器结构。

5.3

说明 ROB、保留站(发射队列)、重命名寄存器在指令流水线中的作用,并查阅资料,比较Skylake 处理器和 Zen 处理器的 ROB、发射队列、重命名寄存器项数。

5.4

假设 A 处理器有两级 Cache,一级 Cache 大小为 32KB,命中率为 95%,命中延迟为 1 拍;二级Cache 大小为 1MB,命中率为 80%,命中延迟为 30 拍,失效延迟为 150 拍。 B 处理器有三级 Cache,一级 Cache 大小为 32KB,命中率为 95%,命中延迟为 1 拍;二级 Cache 大小为 256KB,命中率为 75%,命中延迟为 20 拍;三级 Cache 大小为 4MB,命中率为 80%,命中延迟为 50 拍,失效延迟为 150 拍。比较两款处理器的平均访问延迟。

5.5

假设某内存访问,行关闭、打开、读写各需要两拍,在行缓存命中率为 70% 和 30% 的情况下,采用 open page 模式还是 close page 模式性能更高?

5.6

简要说明处理器和 IO 设备之间的两种通信方式的通信过程。

5.7

简要说明处理器和 IO 设备之间的两种同步方式的同步过程。

5.8

在一个两片系统中,CPU 含内存控制器,桥片含 GPU、DC 和显存,简要说明在 PPT 翻页过程 中,CPU、GPU、DC、显存、内存之间的同步和通信过程。

5.9

调查目前市场主流光盘、硬盘、SSD 盘、内存的价格,并计算每 GB 存储容量的价格。

6.1

找一台电脑,打开机箱,说明每条连线都是什么总线

img

6.2

说明总线包含哪些层次

6.3

假定一组 AXI 3.0 总线, ID 宽度为 8,数据宽度为 64,地址宽度为 32,请计算该组 AXI 总线的信号线数量。

这里仅考虑课本上给出的主要信号。

设标识号位数为 $i$,地址位数为 $m$,数据位数为 $n$.

时钟与复位: $2$ 写请求通道:$i + m + 3 + 4 + 2 + 1 + 1 = 11 + i + m$; 写数据通道:$i + n + \frac{1}{8}n + 1 + 1 = 2 + i + \frac{9}{8}n$; 写响应通道:$i + 2 + 1 + 1 = 4 + i$; 读请求通道:$i + m + 3 + 4 = 2 + 1 + 1 = 11 + i + m$; 读响应通道:$i + n + 2 + 1 + 1 = 4 + i + n$。 总计 $34 + 5i + 2m + \frac{17}{8}n$ 条信号。对于 4 位 ID、32 位地址线、32 位数据线的情况,总计需要 $186$ 条信号。

6.5

DRAM 的寻址包含哪几部分?

DRAM 的寻址包含 Bank 地址、行地址、列地址三部分。在计算机系统中,若将多组 DRAM 串接在一起,则会增加片选信号。

6.6

假设一个处理器支持两个 DDR3 内存通道,每个通道为 64 位宽,内存地址线个数为 15,片选个数为 4,计算该处理器实际支持的最大内存容量。

每个通道为 64 位宽,则一次读写 8 字节数据。

根据题意:

综上,该处理器实际支持 2 × 2^4 × 2^4 × 2^15 × 2^15 × 8 Byte = 2^42 Byte = 4 TB 的内存容量。

7.1

什么情况下需要对 Cache 进行初始化?LoongArch 中 Cache 初始化过程中所使用的 Cache 指令Index Store Tag 的作用是什么?

在系统复位后 Cache 处于未经初始化的状态,如果使用 Cache 空间可能导致错误命中,此时需要对 Cache 进行初始化。

Cache 指令 Index Store Tag 的作用是:使用地址索引到 Cache 行,根据 CP0 寄存器 LagLo 指定的 Tag、V、D 域更新该 Cache 行。

7.2

Cache 初始化和内存初始化的目的有什么不同?系统如何识别内存的更换?

Cache 初始化的目的,是为了清除 Cache 中残留或随机的数据,防止访问 Cache 空间时导致错误的命中,涉及的是 Cache 内部存储的初始数据;内存初始化的目的,则是根据内存的配置信息正确初始化,以便后续程序能够正确地访问内存,并不涉及存储的初始数据。

内存的更换应当是在系统关闭的情况下进行的。在系统复位后的初始化过程中,BIOS 会自动通过 I2C 总线读取内存条上的 SPD 芯片,从中加载内存配置信息,并根据这些信息配置 CPU 或桥片中的内存控制器。如果系统发现此次读取的 SPD 信息与上一次初始化时读取的不相同,这说明内存发生了更换。

7.3

从 HyperTransport 配置地址空间的划分上,计算整个系统能够支持的总线数量、设备数量及功能数量。

在 HyperTransport 总线中:

7.4

根据 PCI 地址空间命中方法及 BAR 的配置方式,给出地址空间命中公式。

IO、Memory 空间命中公式为:(ADDR & MASK) == {BAR[31:4], 4'b0000}

其中 MASK 由 BAR 空间的大小决定,可由下面的方法得到:

配置空间命中公式为:(ADDR & 32'h_ff000000) == {PCI_Config[31:24], 24'h000000}

7.5

多核唤醒时,如果采用核间中断方式,从核的唤醒流程是怎样的?

唤醒流程如下:

  1. 主核准备好唤醒程序的目标地址、执行参数等数据;
  2. 主核触发从核的核间中断,并挂起主核;
  3. 从核处理中断,跳转到唤醒程序开始执行唤醒过程;
  4. 从核完成唤醒,触发主核的核间中断;
  5. 主核处理中断,继续尝试唤醒下一个从核。

7.6

在一台 Linux 机器上,通过 “lspci ‐v” 命令查看该机器的设备列表,并列举其中三个设备的总线号、设备号和功能号,通过其地址空间信息写出该设备 BAR 的实际内容。

Reisen1969 commented 3 weeks ago

好的,这里是一个示例程序,在一个界面里使用所有主要的布局类型 (QVBoxLayout, QHBoxLayout, QGridLayout, QFormLayout) 并在这些布局里放置一些部件以展示它们的功能。

import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                               QGridLayout, QFormLayout, QLabel, QLineEdit, QPushButton, QGroupBox)

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("布局示例")
        self.setGeometry(100, 100, 800, 600)

        # 设置主窗口的中心部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        # 创建主布局(QVBoxLayout)
        main_layout = QVBoxLayout(self.central_widget)

        # 创建垂直布局示例
        vbox_layout = QVBoxLayout()
        vbox_group = QGroupBox("QVBoxLayout 示例")
        vbox_group.setLayout(vbox_layout)

        vbox_layout.addWidget(QLabel("标签 1"))
        vbox_layout.addWidget(QPushButton("按钮 1"))
        vbox_layout.addWidget(QLabel("标签 2"))
        vbox_layout.addWidget(QPushButton("按钮 2"))

        # 创建水平布局示例
        hbox_layout = QHBoxLayout()
        hbox_group = QGroupBox("QHBoxLayout 示例")
        hbox_group.setLayout(hbox_layout)

        hbox_layout.addWidget(QLabel("标签 1"))
        hbox_layout.addWidget(QPushButton("按钮 1"))
        hbox_layout.addWidget(QLabel("标签 2"))
        hbox_layout.addWidget(QPushButton("按钮 2"))

        # 创建网格布局示例
        grid_layout = QGridLayout()
        grid_group = QGroupBox("QGridLayout 示例")
        grid_group.setLayout(grid_layout)

        grid_layout.addWidget(QLabel("标签 1"), 0, 0)
        grid_layout.addWidget(QPushButton("按钮 1"), 0, 1)
        grid_layout.addWidget(QLabel("标签 2"), 1, 0)
        grid_layout.addWidget(QPushButton("按钮 2"), 1, 1)

        # 创建表单布局示例
        form_layout = QFormLayout()
        form_group = QGroupBox("QFormLayout 示例")
        form_group.setLayout(form_layout)

        form_layout.addRow("标签 1:", QLineEdit())
        form_layout.addRow("标签 2:", QLineEdit())
        form_layout.addRow(QLabel("按钮:"), QPushButton("提交"))

        # 将所有布局示例添加到主布局
        main_layout.addWidget(vbox_group)
        main_layout.addWidget(hbox_group)
        main_layout.addWidget(grid_group)
        main_layout.addWidget(form_group)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

代码解释

  1. 导入模块

    from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                                   QGridLayout, QFormLayout, QLabel, QLineEdit, QPushButton, QGroupBox)
  2. 主窗口 MainWindow

    class MainWindow(QMainWindow):
        def __init__(self):
            super(MainWindow, self).__init__()
            self.setWindowTitle("布局示例")
            self.setGeometry(100, 100, 800, 600)
    
            # 设置主窗口的中心部件
            self.central_widget = QWidget()
            self.setCentralWidget(self.central_widget)
    
            # 创建主布局(QVBoxLayout)
            main_layout = QVBoxLayout(self.central_widget)
  3. 创建 QVBoxLayout 示例

            vbox_layout = QVBoxLayout()
            vbox_group = QGroupBox("QVBoxLayout 示例")
            vbox_group.setLayout(vbox_layout)
    
            vbox_layout.addWidget(QLabel("标签 1"))
            vbox_layout.addWidget(QPushButton("按钮 1"))
            vbox_layout.addWidget(QLabel("标签 2"))
            vbox_layout.addWidget(QPushButton("按钮 2"))
  4. 创建 QHBoxLayout 示例

            hbox_layout = QHBoxLayout()
            hbox_group = QGroupBox("QHBoxLayout 示例")
            hbox_group.setLayout(hbox_layout)
    
            hbox_layout.addWidget(QLabel("标签 1"))
            hbox_layout.addWidget(QPushButton("按钮 1"))
            hbox_layout.addWidget(QLabel("标签 2"))
            hbox_layout.addWidget(QPushButton("按钮 2"))
  5. 创建 QGridLayout 示例

            grid_layout = QGridLayout()
            grid_group = QGroupBox("QGridLayout 示例")
            grid_group.setLayout(grid_layout)
    
            grid_layout.addWidget(QLabel("标签 1"), 0, 0)
            grid_layout.addWidget(QPushButton("按钮 1"), 0, 1)
            grid_layout.addWidget(QLabel("标签 2"), 1, 0)
            grid_layout.addWidget(QPushButton("按钮 2"), 1, 1)
  6. 创建 QFormLayout 示例

            form_layout = QFormLayout()
            form_group = QGroupBox("QFormLayout 示例")
            form_group.setLayout(form_layout)
    
            form_layout.addRow("标签 1:", QLineEdit())
            form_layout.addRow("标签 2:", QLineEdit())
            form_layout.addRow(QLabel("按钮:"), QPushButton("提交"))
  7. 将所有布局示例添加到主布局

            main_layout.addWidget(vbox_group)
            main_layout.addWidget(hbox_group)
            main_layout.addWidget(grid_group)
            main_layout.addWidget(form_group)
  8. 运行程序

    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = MainWindow()
        window.show()
        sys.exit(app.exec())

运行程序

将上述代码保存为一个 Python 文件(例如 layout_example.py),然后在命令行中运行:

python layout_example.py

这将启动一个包含不同布局示例的窗口。每个布局示例都在一个 QGroupBox 中,以便更清晰地展示它们的功能。

Reisen1969 commented 3 weeks ago

好的,这里是一个示例程序,展示如何在一个界面中使用所有主要的布局类型 (QVBoxLayout, QHBoxLayout, QGridLayout, QFormLayout, QStackedLayout) 以及所有的部件类型 (QToolBar, QStatusBar, QMenuBar, QTabWidget, QScrollArea, QMessageBox):

import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QGridLayout, 
                               QFormLayout, QStackedLayout, QWidget, QLabel, QLineEdit, 
                               QPushButton, QGroupBox, QToolBar, QStatusBar, QMenuBar, 
                               QTabWidget, QScrollArea, QMessageBox, QAction, QDialog)
from PySide6.QtGui import QIcon

class CustomDialog(QDialog):
    def __init__(self):
        super(CustomDialog, self).__init__()
        self.setWindowTitle("Custom Dialog")
        layout = QVBoxLayout()
        label = QLabel("This is a custom dialog")
        layout.addWidget(label)
        self.setLayout(layout)

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Complete Example with All Layouts and Widgets")
        self.setGeometry(100, 100, 800, 600)

        # 设置主窗口的中心部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        # 创建主布局(QVBoxLayout)
        main_layout = QVBoxLayout(self.central_widget)

        # 创建工具栏
        self.toolbar = QToolBar("Main Toolbar", self)
        self.addToolBar(self.toolbar)

        # 创建操作并添加到工具栏
        action_open = QAction(QIcon(), "Open", self)
        action_open.setStatusTip("Open file")
        action_open.triggered.connect(lambda: self.show_message("Open action triggered"))

        action_save = QAction(QIcon(), "Save", self)
        action_save.setStatusTip("Save file")
        action_save.triggered.connect(lambda: self.show_message("Save action triggered"))

        action_exit = QAction(QIcon(), "Exit", self)
        action_exit.setStatusTip("Exit application")
        action_exit.triggered.connect(self.close)

        self.toolbar.addAction(action_open)
        self.toolbar.addAction(action_save)
        self.toolbar.addSeparator()
        self.toolbar.addAction(action_exit)

        # 创建状态栏
        self.status_bar = QStatusBar(self)
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage("Ready")

        # 创建菜单栏
        self.menu_bar = QMenuBar(self)
        self.setMenuBar(self.menu_bar)

        file_menu = self.menu_bar.addMenu("File")
        file_menu.addAction(action_open)
        file_menu.addAction(action_save)
        file_menu.addSeparator()
        file_menu.addAction(action_exit)

        help_menu = self.menu_bar.addMenu("Help")
        about_action = QAction("About", self)
        about_action.triggered.connect(self.show_about_dialog)
        help_menu.addAction(about_action)

        # 创建 TabWidget
        self.tab_widget = QTabWidget(self)
        main_layout.addWidget(self.tab_widget)

        tab1 = QWidget()
        tab1_layout = QVBoxLayout(tab1)
        tab1_label = QLabel("This is the first tab")
        tab1_layout.addWidget(tab1_label)

        tab2 = QWidget()
        tab2_layout = QHBoxLayout(tab2)
        tab2_label = QLabel("This is the second tab")
        tab2_layout.addWidget(tab2_label)

        self.tab_widget.addTab(tab1, "Tab 1")
        self.tab_widget.addTab(tab2, "Tab 2")

        # 创建垂直布局示例
        vbox_layout = QVBoxLayout()
        vbox_group = QGroupBox("QVBoxLayout Example")
        vbox_group.setLayout(vbox_layout)

        vbox_layout.addWidget(QLabel("Label 1"))
        vbox_layout.addWidget(QPushButton("Button 1"))
        vbox_layout.addWidget(QLabel("Label 2"))
        vbox_layout.addWidget(QPushButton("Button 2"))

        # 创建水平布局示例
        hbox_layout = QHBoxLayout()
        hbox_group = QGroupBox("QHBoxLayout Example")
        hbox_group.setLayout(hbox_layout)

        hbox_layout.addWidget(QLabel("Label 1"))
        hbox_layout.addWidget(QPushButton("Button 1"))
        hbox_layout.addWidget(QLabel("Label 2"))
        hbox_layout.addWidget(QPushButton("Button 2"))

        # 创建网格布局示例
        grid_layout = QGridLayout()
        grid_group = QGroupBox("QGridLayout Example")
        grid_group.setLayout(grid_layout)

        grid_layout.addWidget(QLabel("Label 1"), 0, 0)
        grid_layout.addWidget(QPushButton("Button 1"), 0, 1)
        grid_layout.addWidget(QLabel("Label 2"), 1, 0)
        grid_layout.addWidget(QPushButton("Button 2"), 1, 1)

        # 创建表单布局示例
        form_layout = QFormLayout()
        form_group = QGroupBox("QFormLayout Example")
        form_group.setLayout(form_layout)

        form_layout.addRow("Label 1:", QLineEdit())
        form_layout.addRow("Label 2:", QLineEdit())
        form_layout.addRow(QLabel("Button:"), QPushButton("Submit"))

        # 创建 StackedLayout 示例
        stacked_layout = QStackedLayout()
        stacked_group = QGroupBox("QStackedLayout Example")
        stacked_group.setLayout(stacked_layout)

        page1 = QWidget()
        page1_layout = QVBoxLayout(page1)
        page1_label = QLabel("This is the first page")
        switch_to_page2_btn = QPushButton("Switch to Page 2")
        switch_to_page2_btn.clicked.connect(lambda: stacked_layout.setCurrentWidget(page2))
        page1_layout.addWidget(page1_label)
        page1_layout.addWidget(switch_to_page2_btn)

        page2 = QWidget()
        page2_layout = QVBoxLayout(page2)
        page2_label = QLabel("This is the second page")
        switch_to_page1_btn = QPushButton("Switch to Page 1")
        switch_to_page1_btn.clicked.connect(lambda: stacked_layout.setCurrentWidget(page1))
        page2_layout.addWidget(page2_label)
        page2_layout.addWidget(switch_to_page1_btn)

        stacked_layout.addWidget(page1)
        stacked_layout.addWidget(page2)

        # 创建 ScrollArea 示例
        self.scroll_area = QScrollArea(self)
        self.scroll_content = QWidget()
        self.scroll_area.setWidget(self.scroll_content)
        self.scroll_area.setWidgetResizable(True)

        scroll_layout = QVBoxLayout(self.scroll_content)
        for i in range(20):
            scroll_layout.addWidget(QLabel(f"Label {i}"))

        scroll_group = QGroupBox("QScrollArea Example")
        scroll_layout = QVBoxLayout(scroll_group)
        scroll_layout.addWidget(self.scroll_area)

        # 将所有布局示例添加到 TabWidget 的第一个标签页
        tab1_layout.addWidget(vbox_group)
        tab1_layout.addWidget(hbox_group)
        tab1_layout.addWidget(grid_group)
        tab1_layout.addWidget(form_group)
        tab1_layout.addWidget(stacked_group)
        tab1_layout.addWidget(scroll_group)

    def show_message(self, message):
        msg_box = QMessageBox(self)
        msg_box.setText(message)
        msg_box.exec()

    def show_about_dialog(self):
        about_msg_box = QMessageBox(self)
        about_msg_box.setWindowTitle("About")
        about_msg_box.setText("This is an about dialog")
        about_msg_box.exec()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

代码解释

  1. 导入必要的模块

    from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QGridLayout, 
                                   QFormLayout, QStackedLayout, QWidget, QLabel, QLineEdit, 
                                   QPushButton, QGroupBox, QToolBar, QStatusBar, QMenuBar, 
                                   QTabWidget, QScrollArea, QMessageBox, QAction, QDialog)
    from PySide6.QtGui import QIcon
  2. 自定义对话框 CustomDialog

    class CustomDialog(QDialog):
        def __init__(self):
            super(CustomDialog, self).__init__()
            self.setWindowTitle("Custom Dialog")
            layout = QVBoxLayout()
            label = QLabel("This is a custom dialog")
            layout.addWidget(label)
            self.setLayout(layout)
  3. 主窗口 MainWindow

    
    class MainWindow(QMainWindow):
        def __init__(self):
            super(MainWindow, self).__init__()
            self.setWindowTitle("Complete Example with All Layouts and Widgets")
            self.setGeometry(100, 100, 800, 600)
    
            # 设置主窗口的中心部件
            self.central_widget = QWidget()
            self.setCentralWidget(self.central_widget)
    
            # 创建主布局(QVBox
Reisen1969 commented 3 weeks ago

好的,我们可以通过将与部件的交互事件绑定到创建新线程的函数来实现这一目标。以下是一个示例程序,展示如何在上述示例的基础上加入多线程,并通过与部件交互时动态地创建新线程:

import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QGridLayout, 
                               QFormLayout, QStackedLayout, QWidget, QLabel, QLineEdit, 
                               QPushButton, QGroupBox, QToolBar, QStatusBar, QMenuBar, 
                               QTabWidget, QScrollArea, QMessageBox, QAction, QDialog)
from PySide6.QtCore import QThread, Signal
from PySide6.QtGui import QIcon
import time

class Worker(QThread):
    progress = Signal(int)

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

    def run(self):
        for i in range(1, 11):
            time.sleep(1)
            self.progress.emit(i)

class CustomDialog(QDialog):
    def __init__(self):
        super(CustomDialog, self).__init__()
        self.setWindowTitle("Custom Dialog")
        layout = QVBoxLayout()
        label = QLabel("This is a custom dialog")
        layout.addWidget(label)
        self.setLayout(layout)

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Complete Example with All Layouts and Widgets")
        self.setGeometry(100, 100, 800, 600)

        # 设置主窗口的中心部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        # 创建主布局(QVBoxLayout)
        main_layout = QVBoxLayout(self.central_widget)

        # 创建工具栏
        self.toolbar = QToolBar("Main Toolbar", self)
        self.addToolBar(self.toolbar)

        # 创建操作并添加到工具栏
        action_open = QAction(QIcon(), "Open", self)
        action_open.setStatusTip("Open file")
        action_open.triggered.connect(lambda: self.show_message("Open action triggered"))

        action_save = QAction(QIcon(), "Save", self)
        action_save.setStatusTip("Save file")
        action_save.triggered.connect(lambda: self.show_message("Save action triggered"))

        action_exit = QAction(QIcon(), "Exit", self)
        action_exit.setStatusTip("Exit application")
        action_exit.triggered.connect(self.close)

        self.toolbar.addAction(action_open)
        self.toolbar.addAction(action_save)
        self.toolbar.addSeparator()
        self.toolbar.addAction(action_exit)

        # 创建状态栏
        self.status_bar = QStatusBar(self)
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage("Ready")

        # 创建菜单栏
        self.menu_bar = QMenuBar(self)
        self.setMenuBar(self.menu_bar)

        file_menu = self.menu_bar.addMenu("File")
        file_menu.addAction(action_open)
        file_menu.addAction(action_save)
        file_menu.addSeparator()
        file_menu.addAction(action_exit)

        help_menu = self.menu_bar.addMenu("Help")
        about_action = QAction("About", self)
        about_action.triggered.connect(self.show_about_dialog)
        help_menu.addAction(about_action)

        # 创建 TabWidget
        self.tab_widget = QTabWidget(self)
        main_layout.addWidget(self.tab_widget)

        tab1 = QWidget()
        tab1_layout = QVBoxLayout(tab1)
        tab1_label = QLabel("This is the first tab")
        tab1_layout.addWidget(tab1_label)

        tab2 = QWidget()
        tab2_layout = QHBoxLayout(tab2)
        tab2_label = QLabel("This is the second tab")
        tab2_layout.addWidget(tab2_label)

        self.tab_widget.addTab(tab1, "Tab 1")
        self.tab_widget.addTab(tab2, "Tab 2")

        # 创建垂直布局示例
        vbox_layout = QVBoxLayout()
        vbox_group = QGroupBox("QVBoxLayout Example")
        vbox_group.setLayout(vbox_layout)

        vbox_layout.addWidget(QLabel("Label 1"))
        vbox_layout.addWidget(QPushButton("Button 1"))
        vbox_layout.addWidget(QLabel("Label 2"))
        vbox_layout.addWidget(QPushButton("Button 2"))

        # 创建水平布局示例
        hbox_layout = QHBoxLayout()
        hbox_group = QGroupBox("QHBoxLayout Example")
        hbox_group.setLayout(hbox_layout)

        hbox_layout.addWidget(QLabel("Label 1"))
        hbox_layout.addWidget(QPushButton("Button 1"))
        hbox_layout.addWidget(QLabel("Label 2"))
        hbox_layout.addWidget(QPushButton("Button 2"))

        # 创建网格布局示例
        grid_layout = QGridLayout()
        grid_group = QGroupBox("QGridLayout Example")
        grid_group.setLayout(grid_layout)

        grid_layout.addWidget(QLabel("Label 1"), 0, 0)
        grid_layout.addWidget(QPushButton("Button 1"), 0, 1)
        grid_layout.addWidget(QLabel("Label 2"), 1, 0)
        grid_layout.addWidget(QPushButton("Button 2"), 1, 1)

        # 创建表单布局示例
        form_layout = QFormLayout()
        form_group = QGroupBox("QFormLayout Example")
        form_group.setLayout(form_layout)

        form_layout.addRow("Label 1:", QLineEdit())
        form_layout.addRow("Label 2:", QLineEdit())
        form_layout.addRow(QLabel("Button:"), QPushButton("Submit"))

        # 创建 StackedLayout 示例
        stacked_layout = QStackedLayout()
        stacked_group = QGroupBox("QStackedLayout Example")
        stacked_group.setLayout(stacked_layout)

        page1 = QWidget()
        page1_layout = QVBoxLayout(page1)
        page1_label = QLabel("This is the first page")
        switch_to_page2_btn = QPushButton("Switch to Page 2")
        switch_to_page2_btn.clicked.connect(lambda: stacked_layout.setCurrentWidget(page2))
        page1_layout.addWidget(page1_label)
        page1_layout.addWidget(switch_to_page2_btn)

        page2 = QWidget()
        page2_layout = QVBoxLayout(page2)
        page2_label = QLabel("This is the second page")
        switch_to_page1_btn = QPushButton("Switch to Page 1")
        switch_to_page1_btn.clicked.connect(lambda: stacked_layout.setCurrentWidget(page1))
        page2_layout.addWidget(page2_label)
        page2_layout.addWidget(switch_to_page1_btn)

        stacked_layout.addWidget(page1)
        stacked_layout.addWidget(page2)

        # 创建 ScrollArea 示例
        self.scroll_area = QScrollArea(self)
        self.scroll_content = QWidget()
        self.scroll_area.setWidget(self.scroll_content)
        self.scroll_area.setWidgetResizable(True)

        scroll_layout = QVBoxLayout(self.scroll_content)
        for i in range(20):
            scroll_layout.addWidget(QLabel(f"Label {i}"))

        scroll_group = QGroupBox("QScrollArea Example")
        scroll_layout = QVBoxLayout(scroll_group)
        scroll_layout.addWidget(self.scroll_area)

        # 将所有布局示例添加到 TabWidget 的第一个标签页
        tab1_layout.addWidget(vbox_group)
        tab1_layout.addWidget(hbox_group)
        tab1_layout.addWidget(grid_group)
        tab1_layout.addWidget(form_group)
        tab1_layout.addWidget(stacked_group)
        tab1_layout.addWidget(scroll_group)

        # 为按钮添加动态创建线程的功能
        create_thread_btn = QPushButton("Create and Start Thread")
        create_thread_btn.clicked.connect(self.create_and_start_thread)
        tab1_layout.addWidget(create_thread_btn)

        self.threads = []

    def create_and_start_thread(self):
        worker = Worker()
        worker.progress.connect(self.update_status)
        worker.start()
        self.threads.append(worker)  # 保持对线程的引用

    def update_status(self, value):
        self.status_bar.showMessage(f"Thread Progress: {value}")

    def show_message(self, message):
        msg_box = QMessageBox(self)
        msg_box.setText(message)
        msg_box.exec()

    def show_about_dialog(self):
        about_msg_box = QMessageBox(self)
        about_msg_box.setWindowTitle("About")
        about_msg_box.setText("This is an about dialog")
        about_msg_box.exec()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

代码解释

  1. 定义 Worker

    class Worker(QThread):
        progress = Signal(int)
    
        def __init__(self):
            super(Worker, self).__init__()
    
        def run(self):
            for i in range(1, 11):
                time.sleep(1)
                self.progress.emit(i)

    这个类继承自 QThread,用于在线程中执行任务,并通过 progress 信号发送进度更新。

  2. MainWindow 中添加动态创建线程的功能

    
    class MainWindow(QMainWindow):
        def __init__(self):
            ...
    
            # 为
Reisen1969 commented 3 weeks ago

当然可以。我们可以让每个线程在部件上显示进度。下面的示例代码展示了如何在每个线程中更新一个 QLabel,以显示当前的进度。

import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QGridLayout, 
                               QFormLayout, QStackedLayout, QWidget, QLabel, QLineEdit, 
                               QPushButton, QGroupBox, QToolBar, QStatusBar, QMenuBar, 
                               QTabWidget, QScrollArea, QMessageBox, QAction, QDialog)
from PySide6.QtCore import QThread, Signal
from PySide6.QtGui import QIcon
import time

class Worker(QThread):
    progress = Signal(int)
    finished = Signal()

    def __init__(self, label):
        super(Worker, self).__init__()
        self.label = label

    def run(self):
        for i in range(1, 11):
            time.sleep(1)
            self.progress.emit(i)
        self.finished.emit()

class CustomDialog(QDialog):
    def __init__(self):
        super(CustomDialog, self).__init__()
        self.setWindowTitle("Custom Dialog")
        layout = QVBoxLayout()
        label = QLabel("This is a custom dialog")
        layout.addWidget(label)
        self.setLayout(layout)

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Complete Example with All Layouts and Widgets")
        self.setGeometry(100, 100, 800, 600)

        # 设置主窗口的中心部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        # 创建主布局(QVBoxLayout)
        main_layout = QVBoxLayout(self.central_widget)

        # 创建工具栏
        self.toolbar = QToolBar("Main Toolbar", self)
        self.addToolBar(self.toolbar)

        # 创建操作并添加到工具栏
        action_open = QAction(QIcon(), "Open", self)
        action_open.setStatusTip("Open file")
        action_open.triggered.connect(lambda: self.show_message("Open action triggered"))

        action_save = QAction(QIcon(), "Save", self)
        action_save.setStatusTip("Save file")
        action_save.triggered.connect(lambda: self.show_message("Save action triggered"))

        action_exit = QAction(QIcon(), "Exit", self)
        action_exit.setStatusTip("Exit application")
        action_exit.triggered.connect(self.close)

        self.toolbar.addAction(action_open)
        self.toolbar.addAction(action_save)
        self.toolbar.addSeparator()
        self.toolbar.addAction(action_exit)

        # 创建状态栏
        self.status_bar = QStatusBar(self)
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage("Ready")

        # 创建菜单栏
        self.menu_bar = QMenuBar(self)
        self.setMenuBar(self.menu_bar)

        file_menu = self.menu_bar.addMenu("File")
        file_menu.addAction(action_open)
        file_menu.addAction(action_save)
        file_menu.addSeparator()
        file_menu.addAction(action_exit)

        help_menu = self.menu_bar.addMenu("Help")
        about_action = QAction("About", self)
        about_action.triggered.connect(self.show_about_dialog)
        help_menu.addAction(about_action)

        # 创建 TabWidget
        self.tab_widget = QTabWidget(self)
        main_layout.addWidget(self.tab_widget)

        tab1 = QWidget()
        tab1_layout = QVBoxLayout(tab1)
        tab1_label = QLabel("This is the first tab")
        tab1_layout.addWidget(tab1_label)

        tab2 = QWidget()
        tab2_layout = QHBoxLayout(tab2)
        tab2_label = QLabel("This is the second tab")
        tab2_layout.addWidget(tab2_label)

        self.tab_widget.addTab(tab1, "Tab 1")
        self.tab_widget.addTab(tab2, "Tab 2")

        # 创建垂直布局示例
        vbox_layout = QVBoxLayout()
        vbox_group = QGroupBox("QVBoxLayout Example")
        vbox_group.setLayout(vbox_layout)

        vbox_layout.addWidget(QLabel("Label 1"))
        vbox_layout.addWidget(QPushButton("Button 1"))
        vbox_layout.addWidget(QLabel("Label 2"))
        vbox_layout.addWidget(QPushButton("Button 2"))

        # 创建水平布局示例
        hbox_layout = QHBoxLayout()
        hbox_group = QGroupBox("QHBoxLayout Example")
        hbox_group.setLayout(hbox_layout)

        hbox_layout.addWidget(QLabel("Label 1"))
        hbox_layout.addWidget(QPushButton("Button 1"))
        hbox_layout.addWidget(QLabel("Label 2"))
        hbox_layout.addWidget(QPushButton("Button 2"))

        # 创建网格布局示例
        grid_layout = QGridLayout()
        grid_group = QGroupBox("QGridLayout Example")
        grid_group.setLayout(grid_layout)

        grid_layout.addWidget(QLabel("Label 1"), 0, 0)
        grid_layout.addWidget(QPushButton("Button 1"), 0, 1)
        grid_layout.addWidget(QLabel("Label 2"), 1, 0)
        grid_layout.addWidget(QPushButton("Button 2"), 1, 1)

        # 创建表单布局示例
        form_layout = QFormLayout()
        form_group = QGroupBox("QFormLayout Example")
        form_group.setLayout(form_layout)

        form_layout.addRow("Label 1:", QLineEdit())
        form_layout.addRow("Label 2:", QLineEdit())
        form_layout.addRow(QLabel("Button:"), QPushButton("Submit"))

        # 创建 StackedLayout 示例
        stacked_layout = QStackedLayout()
        stacked_group = QGroupBox("QStackedLayout Example")
        stacked_group.setLayout(stacked_layout)

        page1 = QWidget()
        page1_layout = QVBoxLayout(page1)
        page1_label = QLabel("This is the first page")
        switch_to_page2_btn = QPushButton("Switch to Page 2")
        switch_to_page2_btn.clicked.connect(lambda: stacked_layout.setCurrentWidget(page2))
        page1_layout.addWidget(page1_label)
        page1_layout.addWidget(switch_to_page2_btn)

        page2 = QWidget()
        page2_layout = QVBoxLayout(page2)
        page2_label = QLabel("This is the second page")
        switch_to_page1_btn = QPushButton("Switch to Page 1")
        switch_to_page1_btn.clicked.connect(lambda: stacked_layout.setCurrentWidget(page1))
        page2_layout.addWidget(page2_label)
        page2_layout.addWidget(switch_to_page1_btn)

        stacked_layout.addWidget(page1)
        stacked_layout.addWidget(page2)

        # 创建 ScrollArea 示例
        self.scroll_area = QScrollArea(self)
        self.scroll_content = QWidget()
        self.scroll_area.setWidget(self.scroll_content)
        self.scroll_area.setWidgetResizable(True)

        scroll_layout = QVBoxLayout(self.scroll_content)
        for i in range(20):
            scroll_layout.addWidget(QLabel(f"Label {i}"))

        scroll_group = QGroupBox("QScrollArea Example")
        scroll_layout = QVBoxLayout(scroll_group)
        scroll_layout.addWidget(self.scroll_area)

        # 将所有布局示例添加到 TabWidget 的第一个标签页
        tab1_layout.addWidget(vbox_group)
        tab1_layout.addWidget(hbox_group)
        tab1_layout.addWidget(grid_group)
        tab1_layout.addWidget(form_group)
        tab1_layout.addWidget(stacked_group)
        tab1_layout.addWidget(scroll_group)

        # 为按钮添加动态创建线程的功能
        create_thread_btn = QPushButton("Create and Start Thread")
        create_thread_btn.clicked.connect(self.create_and_start_thread)
        tab1_layout.addWidget(create_thread_btn)

        self.threads = []
        self.labels = []

    def create_and_start_thread(self):
        # 创建一个新的 QLabel 并添加到布局中
        label = QLabel("Thread not started yet")
        self.central_widget.layout().itemAt(0).widget().layout().addWidget(label)
        self.labels.append(label)

        # 创建并启动一个新的线程
        worker = Worker(label)
        worker.progress.connect(self.update_label)
        worker.finished.connect(lambda: self.on_thread_finished(worker))
        worker.start()
        self.threads.append(worker)  # 保持对线程的引用

    def update_label(self, value):
        sender = self.sender()
        sender.label.setText(f"Progress: {value}")

    def on_thread_finished(self, worker):
        worker.label.setText("Thread finished")

    def show_message(self, message):
        msg_box = QMessageBox(self)
        msg_box.setText(message)
        msg_box.exec()

    def show_about_dialog(self):
        about_msg_box = QMessageBox(self)
        about_msg_box.setWindowTitle("About")
        about_msg_box.setText("This is an about dialog")
        about_msg_box.exec()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

代码解释

  1. 修改 Worker

    
    class Worker(QThread):
        progress = Signal(int)
        finished = Signal()
    
        def __init__(self, label):
Reisen1969 commented 3 weeks ago

好的,我们可以编写一个示例,当按钮被按下时,创建一个新线程,每2秒向一个 QTextBrowser 输出该线程的编号。下面是完整的代码示例:

import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QWidget, 
                               QPushButton, QTextBrowser, QToolBar, QStatusBar, 
                               QAction)
from PySide6.QtCore import QThread, Signal
import time

class Worker(QThread):
    update_text = Signal(str)

    def __init__(self, thread_id):
        super(Worker, self).__init__()
        self.thread_id = thread_id

    def run(self):
        while True:
            time.sleep(2)
            self.update_text.emit(f"Thread {self.thread_id} is running\n")

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Thread Example with QTextBrowser")
        self.setGeometry(100, 100, 600, 400)

        # 设置主窗口的中心部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        # 创建主布局(QVBoxLayout)
        main_layout = QVBoxLayout(self.central_widget)

        # 创建工具栏
        self.toolbar = QToolBar("Main Toolbar", self)
        self.addToolBar(self.toolbar)

        # 创建状态栏
        self.status_bar = QStatusBar(self)
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage("Ready")

        # 创建操作并添加到工具栏
        action_exit = QAction("Exit", self)
        action_exit.setStatusTip("Exit application")
        action_exit.triggered.connect(self.close)
        self.toolbar.addAction(action_exit)

        # 创建按钮
        self.start_thread_button = QPushButton("Start New Thread")
        self.start_thread_button.clicked.connect(self.create_and_start_thread)
        main_layout.addWidget(self.start_thread_button)

        # 创建 QTextBrowser
        self.text_browser = QTextBrowser(self)
        main_layout.addWidget(self.text_browser)

        # 线程列表
        self.threads = []
        self.thread_count = 0

    def create_and_start_thread(self):
        self.thread_count += 1
        worker = Worker(self.thread_count)
        worker.update_text.connect(self.append_text)
        worker.start()
        self.threads.append(worker)  # 保持对线程的引用

    def append_text(self, text):
        self.text_browser.append(text)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

代码解释

  1. 定义 Worker

    class Worker(QThread):
        update_text = Signal(str)
    
        def __init__(self, thread_id):
            super(Worker, self).__init__()
            self.thread_id = thread_id
    
        def run(self):
            while True:
                time.sleep(2)
                self.update_text.emit(f"Thread {self.thread_id} is running\n")

    这个类继承自 QThread,每2秒发出信号,将线程编号输出到 QTextBrowser

  2. MainWindow 中设置布局和部件

    class MainWindow(QMainWindow):
        def __init__(self):
            super(MainWindow, self).__init__()
            self.setWindowTitle("Thread Example with QTextBrowser")
            self.setGeometry(100, 100, 600, 400)
    
            self.central_widget = QWidget()
            self.setCentralWidget(self.central_widget)
    
            main_layout = QVBoxLayout(self.central_widget)
    
            self.toolbar = QToolBar("Main Toolbar", self)
            self.addToolBar(self.toolbar)
    
            self.status_bar = QStatusBar(self)
            self.setStatusBar(self.status_bar)
            self.status_bar.showMessage("Ready")
    
            action_exit = QAction("Exit", self)
            action_exit.setStatusTip("Exit application")
            action_exit.triggered.connect(self.close)
            self.toolbar.addAction(action_exit)
    
            self.start_thread_button = QPushButton("Start New Thread")
            self.start_thread_button.clicked.connect(self.create_and_start_thread)
            main_layout.addWidget(self.start_thread_button)
    
            self.text_browser = QTextBrowser(self)
            main_layout.addWidget(self.text_browser)
    
            self.threads = []
            self.thread_count = 0
    
        def create_and_start_thread(self):
            self.thread_count += 1
            worker = Worker(self.thread_count)
            worker.update_text.connect(self.append_text)
            worker.start()
            self.threads.append(worker)
    
        def append_text(self, text):
            self.text_browser.append(text)
  3. create_and_start_thread 方法中创建并启动新线程

    def create_and_start_thread(self):
        self.thread_count += 1
        worker = Worker(self.thread_count)
        worker.update_text.connect(self.append_text)
        worker.start()
        self.threads.append(worker)

    每当按钮被按下时,增加线程计数,创建一个新的 Worker 实例,连接 update_text 信号到 append_text 槽,并启动线程。

  4. append_text 方法中将文本添加到 QTextBrowser

    def append_text(self, text):
        self.text_browser.append(text)

    这个方法将从线程接收到的文本添加到 QTextBrowser 中。

这样,每次按下按钮时,都会创建一个新的线程,并且每个线程会每2秒向 QTextBrowser 输出它的编号。

Reisen1969 commented 3 weeks ago

好的,我们可以在状态栏中显示当前时间和当前线程数,并且在线程输出内容时添加当前时间。下面是完整的示例代码:

import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QWidget, 
                               QPushButton, QTextBrowser, QToolBar, QStatusBar, 
                               QAction)
from PySide6.QtCore import QThread, Signal, QTimer
from PySide6.QtGui import QIcon
import time
from datetime import datetime

class Worker(QThread):
    update_text = Signal(str)

    def __init__(self, thread_id):
        super(Worker, self).__init__()
        self.thread_id = thread_id

    def run(self):
        while True:
            time.sleep(2)
            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            self.update_text.emit(f"{current_time} - Thread {self.thread_id} is running\n")

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Thread Example with QTextBrowser")
        self.setGeometry(100, 100, 600, 400)

        # 设置主窗口的中心部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        # 创建主布局(QVBoxLayout)
        main_layout = QVBoxLayout(self.central_widget)

        # 创建工具栏
        self.toolbar = QToolBar("Main Toolbar", self)
        self.addToolBar(self.toolbar)

        # 创建状态栏
        self.status_bar = QStatusBar(self)
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage("Ready")

        # 状态栏右下角显示当前时间
        self.time_label = QLabel()
        self.status_bar.addPermanentWidget(self.time_label)

        # 状态栏左下角显示线程数
        self.thread_count_label = QLabel()
        self.status_bar.addPermanentWidget(self.thread_count_label)

        # 创建操作并添加到工具栏
        action_exit = QAction(QIcon(), "Exit", self)
        action_exit.setStatusTip("Exit application")
        action_exit.triggered.connect(self.close)
        self.toolbar.addAction(action_exit)

        # 创建按钮
        self.start_thread_button = QPushButton("Start New Thread")
        self.start_thread_button.clicked.connect(self.create_and_start_thread)
        main_layout.addWidget(self.start_thread_button)

        # 创建 QTextBrowser
        self.text_browser = QTextBrowser(self)
        main_layout.addWidget(self.text_browser)

        # 线程列表
        self.threads = []
        self.thread_count = 0

        # 设置定时器以更新时间
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_time)
        self.timer.start(1000)

        self.update_time()
        self.update_thread_count()

    def create_and_start_thread(self):
        self.thread_count += 1
        worker = Worker(self.thread_count)
        worker.update_text.connect(self.append_text)
        worker.start()
        self.threads.append(worker)
        self.update_thread_count()

    def append_text(self, text):
        self.text_browser.append(text)

    def update_time(self):
        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        self.time_label.setText(current_time)

    def update_thread_count(self):
        self.thread_count_label.setText(f"Threads: {self.thread_count}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

代码解释

  1. Worker 类中添加当前时间

    def run(self):
        while True:
            time.sleep(2)
            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            self.update_text.emit(f"{current_time} - Thread {self.thread_id} is running\n")

    线程每2秒发出信号,带有当前时间和线程编号。

  2. MainWindow 中设置状态栏

    self.status_bar = QStatusBar(self)
    self.setStatusBar(self.status_bar)
    self.status_bar.showMessage("Ready")
    
    # 状态栏右下角显示当前时间
    self.time_label = QLabel()
    self.status_bar.addPermanentWidget(self.time_label)
    
    # 状态栏左下角显示线程数
    self.thread_count_label = QLabel()
    self.status_bar.addPermanentWidget(self.thread_count_label)

    添加两个 QLabel 到状态栏,一个用于显示当前时间,一个用于显示线程数。

  3. 设置定时器以更新时间

    self.timer = QTimer(self)
    self.timer.timeout.connect(self.update_time)
    self.timer.start(1000)
    
    self.update_time()
    self.update_thread_count()

    使用 QTimer 每秒调用 update_time 方法更新当前时间。

  4. create_and_start_thread 方法中更新线程数

    def create_and_start_thread(self):
        self.thread_count += 1
        worker = Worker(self.thread_count)
        worker.update_text.connect(self.append_text)
        worker.start()
        self.threads.append(worker)
        self.update_thread_count()

    每次创建新线程时,增加线程计数,并更新状态栏中的线程数。

  5. 更新当前时间和线程数的方法

    def update_time(self):
        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        self.time_label.setText(current_time)
    
    def update_thread_count(self):
        self.thread_count_label.setText(f"Threads: {self.thread_count}")

    定义用于更新时间和线程数的槽函数。

这样,每当按下按钮时,会创建一个新线程,每2秒向 QTextBrowser 输出该线程的编号和当前时间,同时状态栏会显示当前时间和线程数。

Reisen1969 commented 3 weeks ago

当然可以!我们可以将不同的部件定义在不同的文件中,并在主文件中将它们组合起来。以下是一个示例项目结构:

project/
│
├── main.py
├── widgets/
│   ├── toolbar_widget.py
│   ├── text_browser_widget.py
│   ├── thread_button_widget.py
│   └── status_bar_widget.py

main.py

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from widgets.toolbar_widget import ToolbarWidget
from widgets.text_browser_widget import TextBrowserWidget
from widgets.thread_button_widget import ThreadButtonWidget
from widgets.status_bar_widget import StatusBarWidget

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Thread Example with Multiple Widgets")
        self.setGeometry(100, 100, 600, 400)

        # 设置主窗口的中心部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        # 创建主布局(QVBoxLayout)
        main_layout = QVBoxLayout(self.central_widget)

        # 添加工具栏
        self.toolbar_widget = ToolbarWidget(self)
        self.addToolBar(self.toolbar_widget)

        # 添加文本浏览器
        self.text_browser_widget = TextBrowserWidget()
        main_layout.addWidget(self.text_browser_widget)

        # 添加线程按钮
        self.thread_button_widget = ThreadButtonWidget(self.text_browser_widget)
        main_layout.addWidget(self.thread_button_widget)

        # 添加状态栏
        self.status_bar_widget = StatusBarWidget(self)
        self.setStatusBar(self.status_bar_widget)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

widgets/toolbar_widget.py

from PySide6.QtWidgets import QToolBar, QAction
from PySide6.QtGui import QIcon

class ToolbarWidget(QToolBar):
    def __init__(self, parent=None):
        super(ToolbarWidget, self).__init__("Main Toolbar", parent)

        action_exit = QAction(QIcon(), "Exit", self)
        action_exit.setStatusTip("Exit application")
        action_exit.triggered.connect(parent.close)

        self.addAction(action_exit)

widgets/text_browser_widget.py

from PySide6.QtWidgets import QTextBrowser

class TextBrowserWidget(QTextBrowser):
    def __init__(self, parent=None):
        super(TextBrowserWidget, self).__init__(parent)

widgets/thread_button_widget.py

from PySide6.QtWidgets import QPushButton, QVBoxLayout, QWidget
from PySide6.QtCore import QThread, Signal
from datetime import datetime
import time

class Worker(QThread):
    update_text = Signal(str)

    def __init__(self, thread_id):
        super(Worker, self).__init__()
        self.thread_id = thread_id

    def run(self):
        while True:
            time.sleep(2)
            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            self.update_text.emit(f"{current_time} - Thread {self.thread_id} is running\n")

class ThreadButtonWidget(QWidget):
    def __init__(self, text_browser, parent=None):
        super(ThreadButtonWidget, self).__init__(parent)
        self.text_browser = text_browser

        layout = QVBoxLayout(self)

        self.start_thread_button = QPushButton("Start New Thread")
        self.start_thread_button.clicked.connect(self.create_and_start_thread)
        layout.addWidget(self.start_thread_button)

        self.threads = []
        self.thread_count = 0

    def create_and_start_thread(self):
        self.thread_count += 1
        worker = Worker(self.thread_count)
        worker.update_text.connect(self.text_browser.append)
        worker.start()
        self.threads.append(worker)

widgets/status_bar_widget.py

from PySide6.QtWidgets import QStatusBar, QLabel
from PySide6.QtCore import QTimer
from datetime import datetime

class StatusBarWidget(QStatusBar):
    def __init__(self, parent=None):
        super(StatusBarWidget, self).__init__(parent)

        # 状态栏右下角显示当前时间
        self.time_label = QLabel()
        self.addPermanentWidget(self.time_label)

        # 状态栏左下角显示线程数
        self.thread_count_label = QLabel()
        self.addPermanentWidget(self.thread_count_label)

        # 设置定时器以更新时间
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_time)
        self.timer.start(1000)

        self.update_time()
        self.update_thread_count(0)

    def update_time(self):
        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        self.time_label.setText(current_time)

    def update_thread_count(self, count):
        self.thread_count_label.setText(f"Threads: {count}")

代码解释

  1. main.py

    • 导入并创建主窗口 MainWindow,将各个小部件组合在一起。
  2. widgets/toolbar_widget.py

    • 创建一个 QToolBar,并添加一个退出操作。
  3. widgets/text_browser_widget.py

    • 创建一个简单的 QTextBrowser 部件。
  4. widgets/thread_button_widget.py

    • 创建一个按钮部件,当按钮被按下时,创建并启动一个新线程。
    • 使用 Worker 类来定义线程,该线程每2秒发送一次信号,包含当前时间和线程编号。
  5. widgets/status_bar_widget.py

    • 创建一个状态栏部件,显示当前时间和线程数。
    • 使用 QTimer 每秒更新一次当前时间。

这样,每个小部件都可以独立运行,并且在主文件中将它们组合在一起,形成一个完整的应用程序。

Reisen1969 commented 3 weeks ago

好的,我们可以为每个文件添加一个 __main__ 方法,使其能够独立运行。以下是更新后的代码示例:

main.py

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from widgets.toolbar_widget import ToolbarWidget
from widgets.text_browser_widget import TextBrowserWidget
from widgets.thread_button_widget import ThreadButtonWidget
from widgets.status_bar_widget import StatusBarWidget

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Thread Example with Multiple Widgets")
        self.setGeometry(100, 100, 600, 400)

        # 设置主窗口的中心部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        # 创建主布局(QVBoxLayout)
        main_layout = QVBoxLayout(self.central_widget)

        # 添加工具栏
        self.toolbar_widget = ToolbarWidget(self)
        self.addToolBar(self.toolbar_widget)

        # 添加文本浏览器
        self.text_browser_widget = TextBrowserWidget()
        main_layout.addWidget(self.text_browser_widget)

        # 添加线程按钮
        self.thread_button_widget = ThreadButtonWidget(self.text_browser_widget)
        main_layout.addWidget(self.thread_button_widget)

        # 添加状态栏
        self.status_bar_widget = StatusBarWidget(self)
        self.setStatusBar(self.status_bar_widget)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

widgets/toolbar_widget.py

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QToolBar, QAction
from PySide6.QtGui import QIcon

class ToolbarWidget(QToolBar):
    def __init__(self, parent=None):
        super(ToolbarWidget, self).__init__("Main Toolbar", parent)

        action_exit = QAction(QIcon(), "Exit", self)
        action_exit.setStatusTip("Exit application")
        action_exit.triggered.connect(parent.close)

        self.addAction(action_exit)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    main_window = QMainWindow()
    toolbar = ToolbarWidget(main_window)
    main_window.addToolBar(toolbar)
    main_window.setGeometry(100, 100, 600, 400)
    main_window.show()
    sys.exit(app.exec())

widgets/text_browser_widget.py

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QTextBrowser

class TextBrowserWidget(QTextBrowser):
    def __init__(self, parent=None):
        super(TextBrowserWidget, self).__init__(parent)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    main_window = QMainWindow()
    text_browser = TextBrowserWidget(main_window)
    main_window.setCentralWidget(text_browser)
    main_window.setGeometry(100, 100, 600, 400)
    main_window.show()
    sys.exit(app.exec())

widgets/thread_button_widget.py

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QTextBrowser
from PySide6.QtCore import QThread, Signal
from datetime import datetime
import time

class Worker(QThread):
    update_text = Signal(str)

    def __init__(self, thread_id):
        super(Worker, self).__init__()
        self.thread_id = thread_id

    def run(self):
        while True:
            time.sleep(2)
            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            self.update_text.emit(f"{current_time} - Thread {self.thread_id} is running\n")

class ThreadButtonWidget(QWidget):
    def __init__(self, text_browser, parent=None):
        super(ThreadButtonWidget, self).__init__(parent)
        self.text_browser = text_browser

        layout = QVBoxLayout(self)

        self.start_thread_button = QPushButton("Start New Thread")
        self.start_thread_button.clicked.connect(self.create_and_start_thread)
        layout.addWidget(self.start_thread_button)

        self.threads = []
        self.thread_count = 0

    def create_and_start_thread(self):
        self.thread_count += 1
        worker = Worker(self.thread_count)
        worker.update_text.connect(self.text_browser.append)
        worker.start()
        self.threads.append(worker)

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

    text_browser = QTextBrowser(main_window)
    main_window.setCentralWidget(text_browser)

    thread_button_widget = ThreadButtonWidget(text_browser)
    main_window.setCentralWidget(thread_button_widget)

    main_window.setGeometry(100, 100, 600, 400)
    main_window.show()
    sys.exit(app.exec())

widgets/status_bar_widget.py

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QStatusBar, QLabel
from PySide6.QtCore import QTimer
from datetime import datetime

class StatusBarWidget(QStatusBar):
    def __init__(self, parent=None):
        super(StatusBarWidget, self).__init__(parent)

        # 状态栏右下角显示当前时间
        self.time_label = QLabel()
        self.addPermanentWidget(self.time_label)

        # 状态栏左下角显示线程数
        self.thread_count_label = QLabel()
        self.addPermanentWidget(self.thread_count_label)

        # 设置定时器以更新时间
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_time)
        self.timer.start(1000)

        self.update_time()
        self.update_thread_count(0)

    def update_time(self):
        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        self.time_label.setText(current_time)

    def update_thread_count(self, count):
        self.thread_count_label.setText(f"Threads: {count}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    main_window = QMainWindow()
    status_bar = StatusBarWidget(main_window)
    main_window.setStatusBar(status_bar)
    main_window.setGeometry(100, 100, 600, 400)
    main_window.show()
    sys.exit(app.exec())

代码解释

  1. main.py

    • 主文件负责将所有部件组合在一起,并启动应用程序。
  2. widgets/toolbar_widget.py

    • ToolbarWidget 定义了一个带有退出操作的工具栏,并在 __main__ 块中创建一个 QMainWindow 将其显示出来。
  3. widgets/text_browser_widget.py

    • TextBrowserWidget 定义了一个 QTextBrowser,并在 __main__ 块中创建一个 QMainWindow 将其显示出来。
  4. widgets/thread_button_widget.py

    • ThreadButtonWidget 定义了一个按钮部件,当按钮被按下时,创建并启动一个新线程。
    • Worker 类定义了一个线程,该线程每2秒发送一次信号,包含当前时间和线程编号。
    • __main__ 块中,创建一个 QMainWindow,包含一个 QTextBrowserThreadButtonWidget
  5. widgets/status_bar_widget.py

    • StatusBarWidget 定义了一个状态栏部件,显示当前时间和线程数。
    • 使用 QTimer 每秒更新一次当前时间。
    • __main__ 块中,创建一个 QMainWindow,并设置 StatusBarWidget 作为其状态栏。

这样,每个文件都可以独立运行,同时在主文件中可以将这些部件组合在一起。

Reisen1969 commented 3 weeks ago

好的,以下是一个示例项目,包含多个可以独立运行的 py 文件,每个文件定义了一个 widget,并使用不同的布局和部件。在主文件中,使用各种容器来容纳这些 widget

项目结构

project/
│
├── main.py
├── widgets/
│   ├── group_box_widget.py
│   ├── tab_widget.py
│   ├── scroll_area_widget.py
│   ├── splitter_widget.py
│   └── basic_widget.py

main.py

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QTabWidget, QScrollArea, QSplitter
from widgets.group_box_widget import GroupBoxWidget
from widgets.tab_widget import TabWidget
from widgets.scroll_area_widget import ScrollAreaWidget
from widgets.splitter_widget import SplitterWidget
from widgets.basic_widget import BasicWidget

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Multiple Widgets Example")
        self.setGeometry(100, 100, 800, 600)

        # 创建主布局(QVBoxLayout)
        main_layout = QVBoxLayout()

        # 添加 GroupBox
        group_box_widget = GroupBoxWidget()
        main_layout.addWidget(group_box_widget)

        # 添加 TabWidget
        tab_widget = TabWidget()
        main_layout.addWidget(tab_widget)

        # 添加 ScrollArea
        scroll_area_widget = ScrollAreaWidget()
        main_layout.addWidget(scroll_area_widget)

        # 添加 Splitter
        splitter_widget = SplitterWidget()
        main_layout.addWidget(splitter_widget)

        # 添加 BasicWidget
        basic_widget = BasicWidget()
        main_layout.addWidget(basic_widget)

        # 设置中心部件
        central_widget = QWidget()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

widgets/group_box_widget.py

import sys
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QGroupBox, QPushButton

class GroupBoxWidget(QWidget):
    def __init__(self, parent=None):
        super(GroupBoxWidget, self).__init__(parent)
        layout = QVBoxLayout(self)

        group_box = QGroupBox("Group Box")
        group_layout = QVBoxLayout()

        group_layout.addWidget(QPushButton("Button 1"))
        group_layout.addWidget(QPushButton("Button 2"))

        group_box.setLayout(group_layout)
        layout.addWidget(group_box)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = GroupBoxWidget()
    window.setGeometry(100, 100, 300, 200)
    window.show()
    sys.exit(app.exec())

widgets/tab_widget.py

import sys
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QTabWidget, QLabel

class TabWidget(QWidget):
    def __init__(self, parent=None):
        super(TabWidget, self).__init__(parent)
        layout = QVBoxLayout(self)

        tab_widget = QTabWidget()
        tab_widget.addTab(QLabel("Content of Tab 1"), "Tab 1")
        tab_widget.addTab(QLabel("Content of Tab 2"), "Tab 2")

        layout.addWidget(tab_widget)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = TabWidget()
    window.setGeometry(100, 100, 300, 200)
    window.show()
    sys.exit(app.exec())

widgets/scroll_area_widget.py

import sys
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QScrollArea, QLabel

class ScrollAreaWidget(QWidget):
    def __init__(self, parent=None):
        super(ScrollAreaWidget, self).__init__(parent)
        layout = QVBoxLayout(self)

        scroll_area = QScrollArea()
        content = QWidget()
        content_layout = QVBoxLayout(content)

        for i in range(1, 21):
            content_layout.addWidget(QLabel(f"Item {i}"))

        scroll_area.setWidget(content)
        scroll_area.setWidgetResizable(True)

        layout.addWidget(scroll_area)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = ScrollAreaWidget()
    window.setGeometry(100, 100, 300, 200)
    window.show()
    sys.exit(app.exec())

widgets/splitter_widget.py

import sys
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QSplitter, QTextEdit, QListView

class SplitterWidget(QWidget):
    def __init__(self, parent=None):
        super(SplitterWidget, self).__init__(parent)
        layout = QVBoxLayout(self)

        splitter = QSplitter()

        text_edit = QTextEdit()
        list_view = QListView()

        splitter.addWidget(text_edit)
        splitter.addWidget(list_view)

        layout.addWidget(splitter)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = SplitterWidget()
    window.setGeometry(100, 100, 300, 200)
    window.show()
    sys.exit(app.exec())

widgets/basic_widget.py

import sys
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel

class BasicWidget(QWidget):
    def __init__(self, parent=None):
        super(BasicWidget, self).__init__(parent)
        layout = QVBoxLayout(self)

        layout.addWidget(QLabel("Basic Widget"))
        layout.addWidget(QPushButton("Click Me"))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = BasicWidget()
    window.setGeometry(100, 100, 300, 200)
    window.show()
    sys.exit(app.exec())

代码解释

  1. main.py

    • 主文件负责将所有部件组合在一起,并启动应用程序。
    • 使用 QVBoxLayout 作为主布局,依次添加各个自定义 widget
    • 每个 widget 包括:GroupBoxWidgetTabWidgetScrollAreaWidgetSplitterWidgetBasicWidget
  2. widgets/group_box_widget.py

    • GroupBoxWidget 使用 QGroupBox 包含两个按钮。
    • 通过 QVBoxLayout 布局管理器将按钮添加到 QGroupBox 中。
  3. widgets/tab_widget.py

    • TabWidget 使用 QTabWidget 包含两个标签页,每个标签页显示一个 QLabel
  4. widgets/scroll_area_widget.py

    • ScrollAreaWidget 使用 QScrollArea 包含一个包含多个标签的部件,通过滚动区域进行显示。
  5. widgets/splitter_widget.py

    • SplitterWidget 使用 QSplitter 包含一个 QTextEdit 和一个 QListView,可以调整大小。
  6. widgets/basic_widget.py

    • BasicWidget 使用 QVBoxLayout 包含一个 QLabel 和一个 QPushButton,作为基本的示例。

每个 widget 文件都可以独立运行,同时在主文件中可以将这些部件组合在一起。

Reisen1969 commented 3 weeks ago

在 PyQt 或 PySide 中,部件(widgets)之间的通信通常使用信号和槽(signals and slots)机制。这是一种基于事件的通信机制,允许一个对象发出信号,而另一个对象接收该信号并执行相应的槽函数。

以下是一些常见的通信方式:

1. 信号和槽(Signals and Slots)

通过连接信号和槽,一个部件可以通知另一个部件发生了某个事件。例如,一个按钮被点击时,可以让另一个部件更新其显示内容。

2. 全局信号/槽对象

可以使用全局的信号/槽对象来在不同模块之间进行通信。例如,使用 PyQtPySide 提供的 pyqtSignalSignal 创建全局信号对象。

3. 共享数据模型

部件可以共享一个数据模型,当数据模型发生变化时,部件可以更新其显示。例如,使用 QStandardItemModel 或自定义模型。

示例

以下是一个使用信号和槽进行通信的示例,展示了如何在不同的 widget 之间进行通信。

文件结构

project/
│
├── main.py
├── widgets/
│   ├── sender_widget.py
│   └── receiver_widget.py

main.py

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from widgets.sender_widget import SenderWidget
from widgets.receiver_widget import ReceiverWidget

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Widget Communication Example")
        self.setGeometry(100, 100, 800, 600)

        # 创建主布局
        main_layout = QVBoxLayout()

        # 创建 SenderWidget 和 ReceiverWidget
        self.sender_widget = SenderWidget()
        self.receiver_widget = ReceiverWidget()

        # 连接信号和槽
        self.sender_widget.custom_signal.connect(self.receiver_widget.update_label)

        # 添加到主布局
        main_layout.addWidget(self.sender_widget)
        main_layout.addWidget(self.receiver_widget)

        # 设置中心部件
        central_widget = QWidget()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

widgets/sender_widget.py

import sys
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
from PySide6.QtCore import Signal

class SenderWidget(QWidget):
    custom_signal = Signal(str)

    def __init__(self, parent=None):
        super(SenderWidget, self).__init__(parent)
        layout = QVBoxLayout(self)

        self.button = QPushButton("Send Message")
        self.button.clicked.connect(self.send_message)
        layout.addWidget(self.button)

    def send_message(self):
        self.custom_signal.emit("Hello from SenderWidget")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = SenderWidget()
    window.setGeometry(100, 100, 300, 200)
    window.show()
    sys.exit(app.exec())

widgets/receiver_widget.py

import sys
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel

class ReceiverWidget(QWidget):
    def __init__(self, parent=None):
        super(ReceiverWidget, self).__init__(parent)
        layout = QVBoxLayout(self)

        self.label = QLabel("Waiting for message...")
        layout.addWidget(self.label)

    def update_label(self, message):
        self.label.setText(message)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = ReceiverWidget()
    window.setGeometry(100, 100, 300, 200)
    window.show()
    sys.exit(app.exec())

解释

  1. main.py

    • 创建 SenderWidgetReceiverWidget 实例。
    • 使用 connect 方法连接 SenderWidget 的自定义信号 custom_signalReceiverWidget 的槽函数 update_label
  2. sender_widget.py

    • 定义 SenderWidget 类,包含一个按钮。
    • 定义一个自定义信号 custom_signal,在按钮点击时发出信号,并传递消息。
  3. receiver_widget.py

    • 定义 ReceiverWidget 类,包含一个标签。
    • 定义槽函数 update_label,在接收到信号时更新标签内容。

这种方式可以轻松实现部件之间的通信,适用于大多数场景。通过信号和槽机制,部件之间的耦合度较低,代码更加模块化和易于维护。

Reisen1969 commented 3 weeks ago

当然可以!在上面的示例中,我们已经演示了如何通过自定义信号和槽机制在两个部件之间传递字符串。具体来说,SenderWidget 在按钮点击时发出带有字符串参数的自定义信号,而 ReceiverWidget 的槽函数接收该字符串并更新标签内容。

下面是示例中的相关代码片段:

widgets/sender_widget.py

from PySide6.QtCore import Signal

class SenderWidget(QWidget):
    # 定义自定义信号,带有一个字符串参数
    custom_signal = Signal(str)

    def __init__(self, parent=None):
        super(SenderWidget, self).__init__(parent)
        layout = QVBoxLayout(self)

        self.button = QPushButton("Send Message")
        self.button.clicked.connect(self.send_message)
        layout.addWidget(self.button)

    def send_message(self):
        # 发射自定义信号,并传递字符串参数
        self.custom_signal.emit("Hello from SenderWidget")

widgets/receiver_widget.py

class ReceiverWidget(QWidget):
    def __init__(self, parent=None):
        super(ReceiverWidget, self).__init__(parent)
        layout = QVBoxLayout(self)

        self.label = QLabel("Waiting for message...")
        layout.addWidget(self.label)

    # 槽函数,接收字符串参数,并更新标签内容
    def update_label(self, message):
        self.label.setText(message)

在这个示例中,当 SenderWidget 的按钮被点击时,它发射了一个自定义信号 custom_signal,并传递了字符串参数 "Hello from SenderWidget"。这个信号被 ReceiverWidget 的槽函数 update_label 接收,然后将接收到的字符串用作标签的文本内容。

Reisen1969 commented 3 weeks ago

好的,以下是一个简单的示例,分为主文件 main.py 和一个 widget 文件 custom_widget.py。主文件中有一个按钮,点击按钮将弹出自定义的 widget 窗口。

main.py

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton
from custom_widget import CustomWidget

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Custom Widget Example")
        self.setGeometry(100, 100, 400, 300)

        # 创建按钮
        self.button = QPushButton("Show Custom Widget", self)
        self.button.setGeometry(150, 100, 150, 50)
        self.button.clicked.connect(self.show_custom_widget)

    def show_custom_widget(self):
        # 创建自定义窗口
        self.custom_widget = CustomWidget()
        self.custom_widget.show()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

custom_widget.py

from PySide6.QtWidgets import QDialog, QLabel, QVBoxLayout

class CustomWidget(QDialog):
    def __init__(self, parent=None):
        super(CustomWidget, self).__init__(parent)
        self.setWindowTitle("Custom Widget")
        self.setGeometry(200, 200, 300, 200)

        # 创建标签
        self.label = QLabel("This is a custom widget!", self)

        # 创建布局
        layout = QVBoxLayout()
        layout.addWidget(self.label)
        self.setLayout(layout)

在这个示例中,主文件 main.py 创建了一个主窗口,并包含一个按钮。当点击按钮时,主窗口将弹出一个自定义的 widget 窗口。custom_widget.py 文件定义了自定义的窗口,其中包含一个简单的标签。

Reisen1969 commented 3 weeks ago

好的,你可以使用 PySide 或 PyQt 中的布局管理器来实现这个布局。以下是一个简单的示例代码,其中包含一个水平布局和一个垂直布局,分别放置了按钮和文本浏览器。

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QPushButton, QTextBrowser, QWidget

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Horizontal and Vertical Layouts Example")
        self.setGeometry(100, 100, 600, 400)

        # 创建主布局
        main_layout = QVBoxLayout()

        # 创建水平布局
        horizontal_layout = QHBoxLayout()

        # 添加按钮到水平布局
        button1 = QPushButton("Button 1")
        button2 = QPushButton("Button 2")
        button3 = QPushButton("Button 3")
        horizontal_layout.addWidget(button1)
        horizontal_layout.addWidget(button2)
        horizontal_layout.addWidget(button3)

        # 创建垂直布局
        vertical_layout = QVBoxLayout()

        # 添加文本浏览器到垂直布局
        text_browser = QTextBrowser()
        vertical_layout.addWidget(text_browser)

        # 将水平布局和垂直布局添加到主布局
        main_layout.addLayout(horizontal_layout)
        main_layout.addLayout(vertical_layout)

        # 创建中心部件
        central_widget = QWidget()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

在这个示例中,我们创建了一个 QMainWindow,其中包含一个垂直布局(main_layout)。在垂直布局中,我们首先创建了一个水平布局(horizontal_layout),并向其添加了三个按钮。然后,我们创建了一个垂直布局(vertical_layout),并向其添加了一个文本浏览器。最后,我们将水平布局和垂直布局添加到主布局中,并将主布局设置为中心部件。这样,按钮将水平排列在顶部,文本浏览器将垂直排列在底部。

Reisen1969 commented 3 weeks ago

当然可以,以下是用C++实现的代码,将一个存储在 uint64_t 变量中的无符号数根据其位数转换为有符号数:

#include <iostream>
#include <cstdint>

int64_t convert_to_signed(uint64_t unsigned_number, int bit_length) {
    // 如果 bit_length 小于 1 或大于 64,返回错误(根据实际需求可以进行修改)
    if (bit_length < 1 || bit_length > 64) {
        throw std::invalid_argument("Bit length must be between 1 and 64.");
    }

    // 计算符号位的位置
    uint64_t sign_bit = 1ULL << (bit_length - 1);

    // 如果符号位为1,进行补码转换
    if (unsigned_number & sign_bit) {
        // 补码转换
        int64_t signed_number = (int64_t)(unsigned_number | (~((1ULL << bit_length) - 1)));
        return signed_number;
    } else {
        // 否则直接转换
        return (int64_t)unsigned_number;
    }
}

int main() {
    uint64_t unsigned_number = 0x3FFFFFFF; // 示例无符号数 (30 bits, positive)
    int bit_length = 30; // 示例位数

    try {
        int64_t signed_number = convert_to_signed(unsigned_number, bit_length);
        std::cout << "Unsigned number: " << unsigned_number << std::endl;
        std::cout << "Signed number: " << signed_number << std::endl;
    } catch (const std::invalid_argument& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

代码解释

  1. 参数检查:首先检查 bit_length 是否在合理范围内(1到64)。如果不在范围内,抛出异常。
  2. 计算符号位:通过 1ULL << (bit_length - 1) 计算符号位的位置。
  3. 符号位检查和转换
    • 如果符号位为1,说明是负数,需要进行补码转换。
    • 如果符号位为0,说明是正数,直接转换为 int64_t 类型即可。

主要功能

这段代码演示了如何使用 C++ 将不同位数的无符号数转换为有符号数,并处理可能的异常情况。

Reisen1969 commented 3 weeks ago

理解了,要从无符号数中推导出实际的位数,然后将其转换为有符号数。我们可以通过不断右移来计算无符号数的有效位数。以下是修改后的C++代码:

#include <iostream>
#include <cstdint>

int64_t convert_to_signed(uint64_t unsigned_number) {
    // 计算无符号数的实际位数
    int bit_length = 0;
    uint64_t temp_number = unsigned_number;

    while (temp_number > 0) {
        temp_number >>= 1;
        bit_length++;
    }

    // 特殊情况:如果无符号数为0,位数应为1
    if (bit_length == 0) {
        bit_length = 1;
    }

    // 计算符号位的位置
    uint64_t sign_bit = 1ULL << (bit_length - 1);

    // 如果符号位为1,进行补码转换
    if (unsigned_number & sign_bit) {
        // 补码转换
        int64_t signed_number = (int64_t)(unsigned_number | (~((1ULL << bit_length) - 1)));
        return signed_number;
    } else {
        // 否则直接转换
        return (int64_t)unsigned_number;
    }
}

int main() {
    uint64_t unsigned_number = 0xFFFFFFFF; // 示例无符号数

    int64_t signed_number = convert_to_signed(unsigned_number);

    std::cout << "Unsigned number: " << unsigned_number << std::endl;
    std::cout << "Signed number: " << signed_number << std::endl;

    return 0;
}

代码解释

  1. 计算实际位数:通过不断右移 temp_number 并增加 bit_length 来计算无符号数的有效位数。
  2. 特殊情况处理:如果无符号数为0,位数应为1。
  3. 计算符号位:通过 1ULL << (bit_length - 1) 计算符号位的位置。
  4. 符号位检查和转换
    • 如果符号位为1,说明是负数,需要进行补码转换。
    • 如果符号位为0,说明是正数,直接转换为 int64_t 类型即可。

主要功能

这样,程序就能够根据无符号数的实际位数进行适当的转换,无需额外指定位数。

Reisen1969 commented 2 weeks ago

在Linux上,你可以使用ps -efgrep命令来过滤出特定的进程,然后使用kill命令关闭这些进程。以下是一个步骤指南:

  1. 查找进程: 使用ps -ef | grep <关键字>查找特定的进程。例如,如果你想查找包含“myprocess”的进程:

    ps -ef | grep myprocess
  2. 提取进程ID (PID): 使用awk命令从输出中提取PID。例如:

    ps -ef | grep myprocess | grep -v grep | awk '{print $2}'

    其中grep -v grep是为了排除掉包含grep命令本身的进程。

  3. 终止进程: 使用xargskill命令批量终止这些进程:

    ps -ef | grep myprocess | grep -v grep | awk '{print $2}' | xargs kill

    如果进程不能被普通的kill命令终止,可以尝试使用kill -9强制终止:

    ps -ef | grep myprocess | grep -v grep | awk '{print $2}' | xargs kill -9

注意事项

完整示例

假设你要关闭名为“myprocess”的所有进程,完整的命令如下:

ps -ef | grep myprocess | grep -v grep | awk '{print $2}' | xargs kill

如果上述命令无法终止进程,再尝试强制终止:

ps -ef | grep myprocess | grep -v grep | awk '{print $2}' | xargs kill -9