驱动开发学习

article/2025/10/13 2:00:50

驱动

1、驱动开发环境

完成系统移植的三步:u-boot启动引导程序、内核镜像、文件系统,u-boot启动引导程序最好固化到开发板上,内核镜像通过tftp服务从ubuntu下载,文件系统通过nfs服务从ubuntu共享到开发板,开发板启动计数时按任意键进入u-boot命令模式设置bootcmd和bootargs

# setenv serverip 192.168.3.120
# setenv ipaddr 192.168.3.233
# setenv bootcmd tftp 41000000 uImage\;tftp 42000000 exynos4412-fs4412.dtb\;bootm 41000000 – 42000000\;
#setenv bootargs root=/dev/nfs\; nfsroot=192.168.3.120:/source/rootfs rw console=ttySAC2,115200  init=/linuxrc  ip=192.168.3.233
# saveenv

注意:192.168.3.120 对应Ubuntu的ip

192.168.3.233 对应板子的ip

这两个ip应该根据自己的实际情况适当修改

重启开发板进入自启动模式自动加载内核和文件系统

2、驱动开发工具使用source insight软件和vim工具,当然如果不想频繁地从Windows复制到ubuntu可以使用共享文档,这样就可以在Windows上写好代码在ubuntu中直接编译。

设置共享文档步骤:

  1. 打开VMware,选择虚拟机设置选择选项选择共享文件夹,点击总是启用,点击添加就可以设置共享文件夹了

  2. 设置完成后可以在ls /mnt/hgfs查看共享目录是否挂载

  3. 如果没有挂载可以通过vmware-hgfsclient查看共享文件夹

  4. 挂载共享文件夹命令vmhgfs-fuse .host:/my /mnt/hgfs其中my是查看共享文件夹时显示的名字,/mnt/hgfs是挂载路径,挂载路径必须是空的文件夹否则可能失败

source insight---查看和编写代码工具

  1. 将ubuntu中的linux内核代码复制到Windows中

  2. 在source insight中新建项目

    在第一个对话框中,第一个文本框(行编辑器),输入工程的名字

    在第二个对话框中,第一个本文框中选择刚解压的Linux内核源码目录(顶层linux-3.14),点击ok

    在第三个对话框中,在对话框中选择要查看的目录/文件 需要选择的目录文件: include init kernel arch/arm/kernel arch/arm/include/asm driver/base driver/char driver/i2c driver/spi fs/char_dev.c 点击close关闭

    重新选择project---->open project ​ 选择刚才创建的工程名 ​ ok ​ 如果提示同步,则选择确认进行同步

  3. 开始编写驱动代码

    驱动代码必须包含四部分:

    a.头文件

    #include <linux/init.h>
    #include <linux/module.h>

    b.加载和卸载时的函数定义

    static int __init hello_init(void)
    {return 0;
    }
    ​
    static void __exit hello_exit(void)
    {
    ​
    }

    c.加载和卸载的入口声明

    module_init(hello_init);//当使用insmod 驱动名称.ko 加载驱动时执行函数hello_init()
    module_exit(hello_exit);//当使用remod 驱动名称 卸载驱动时执行函数hello_exit()

    d.协议选择GPL

    MODULE_LICENSE("GPL");

    3、驱动操作

    a.加载驱动使用命令

    insmod 驱动程序路径.ko

    b.加载好驱动后可以通过命令查看驱动

    lsmod

    c.卸载驱动命令(不用加.ko)

    rmmod 驱动程序名

    字符设备驱动创建框架

    1、申请设备号

    int register_chrdev(unsigned int major,const char *name,const struct file_operations *fops)
    参数1:unsigned int major------大于0则是申请对应的主设备号;等于0则是由内核分配主设备号
    参数2:const char *name------是注册时的名字
    参数3:const struct file_operations *fops------是用来关联文件IO接口的结构体
    返回值:当参数1大于0时,正确返回0,失败返回负数当参数1等于0时,正确返回主设备号,失败返回负数

    2、创建设备节点(生成对应的驱动文件)

    1. 创建文件信息结构体

      struct class * class_create(owner,name);
      参数1:owner-----拥有者,一般THIS_MODULE
      参数2:name-----字符串,描述信息
      返回值:struct class *------信息结构体       
    2. 创建字符驱动设备文件(节点),一般默认建在/dev下,但是可以在任意目录下创建

      struct device *device_create(   struct class *class,struct device *parent,dev_t devt,void *drvdata,const char *fmt, ...)
      参数1:struct class *class--------class结构体,创建的设备文件的信息内容,通过 class_create()函数创建
      参数2:struct device *parent--------表示父类对象,一般直接写NULL,结构体地址
      参数3:dev_t devt--------设备号可以有函数MKDEV(ma, mi)获得,ma------主设备号, mi------次设备号,也可以由主设备号左移20位或上一个数字得到这个数字不能大于2^20,例:major<<20|0
      参数4:void *drvdata-------私有数据,一般填NULL
      参数5:const char *fmt, ...--------设备文件名字符串首地址
      返回值:struct device *---------设备节点对象(设备文件描述),成功返回地址,失败返回NULL
    3. 文件IO接口层实现,应用程序调用文件io时,驱动程序也调用对应的文件io接口函数 在结构体 struct file_operations 每一个成员变量都代表绑定一个系统调用(文件io)函数,只要对结构体中的成员赋值,就代表值绑定上一个文件io函数

      struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);int (*iterate) (struct file *, struct dir_context *);unsigned int (*poll) (struct file *, struct poll_table_struct *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);int (*show_fdinfo)(struct seq_file *m, struct file *f);};//函数指针的集合,
    4. 驱动控制硬件,控制外设,其实就是控制地址,通过地址往寄存器写入、读出控制 内核驱动是通过虚拟地址操作 初始化硬件

      地址映射:
      void * ioremap(cookie,size);
      参数1:cookie-----物理地址
      参数2:size-----映射内容大小,字节
      返回值:返回映射成功后的虚拟内存地址
      操作虚拟内存地址中的内容就是操作对应的物理地址空间内容

    字符设备驱动卸载时需要的函数

    在卸载入口中实现,清除 与初始化逆序过程进行卸载

    //1、映射释放(中断释放)
    iounmap(映射的虚拟内存地址);----释放映射地址
    //2、释放设备文件
    void device_destroy(struct class * class,dev_t devt);
    //3、释放设备文件结构体
    void class_destroy(struct class * cls)
    //4、释放设备号
    void unregister_chrdev(unsigned int major,const char * name)

    字符驱动模型举例:

    通过字符设备驱动控制一颗LED灯

    //头文件包含
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <asm/io.h>#include <asm/uaccess.h>
    //使用结构体表示一个驱动对象(面向对象的编程思想)
    struct Led_Dev
    {
    unsigned int *gpx1con;
    unsigned int *gpx1dat;
    struct class * class;
    struct device * dev;
    unsigned int major;
    unsigned int dev_no;
    };
    struct Led_Dev led;
    //4、文件IO功能与设备功能实现绑定
    ssize_t led_read (struct file * flie, char __user * data, size_t size, loff_t * ops)
    {return 0;
    }
    ssize_t led_write (struct file * file, const char __user * data, size_t size, loff_t * ops)
    {//data是应用程序传递的数据的地址,size 传递的大小int num;copy_from_user(&num,data,size);//从应用程序获取数据存到num中if(num==1){*(led.gpx1dat) |= 1;}else{*(led.gpx1dat) &= ~1;}return 0;
    }
    int led_open (struct inode * inode, struct file * file)
    {printk("open ok\n");return 0;
    }
    int led_close (struct inode * inode, struct file * file)
    {printk("close ok\n");return 0;
    }const struct file_operations fops=
    {.open = led_open,.release = led_close,.write = led_write};//驱动加载与卸载函数实现
    static int __init led_init(void)
    {led.major = 250;led.dev_no = led.major<<20|0;
    //1、申请设备号int res = register_chrdev(led.major,"led_dev",&fops);if(res !=0 ){printk("register dev error\n");goto err_1;//return -1;}
    //2、创建设备文件led.class = class_create(THIS_MODULE,"led_cls");if (IS_ERR(led.class)){printk("class create error\n");goto err_2;//unregister_chrdev(major,name);//return -1;}		led.dev = device_create(led.class, NULL, led.dev_no,NULL,"led");if(IS_ERR(led.dev)){printk("device create error\n");goto err_3;//class_destroy(class);//unregister_chrdev(major,name);//return -1;}
    //3、驱动设备控制硬件//硬件寄存器地址映射led.gpx1con = ioremap(0x11000c20, 4);if(led.gpx1con==NULL){printk("gpx1con ioremap error\n");goto err_4;//device_destroy(class, dev_no);//class_destroy(class);//unregister_chrdev(major,name);//return -1;}led.gpx1dat = ioremap(0x11000c24, 4);if(led.gpx1dat==NULL){printk("gpx1dat ioremap error\n");goto err_5;//iounmap(gpx1con);//device_destroy(class, dev_no);//class_destroy(class);//unregister_chrdev(major,name);//return -1;}//硬件初始化*(led.gpx1con) = *(led.gpx1con) & ~0xf | 1;*(led.gpx1dat) |= 1;return 0;//出错处理的代码   
    err_5://1、映射地址释放iounmap(led.gpx1con);err_4:	//2、设备文件释放device_destroy(led.class,led.dev_no);//释放设备文件err_3:class_destroy(led.class);//释放文件信息结构体err_2:	//3、驱动设备号注销unregister_chrdev(led.major,"led_dev");err_1:return -1;
    }static void __exit led_exit(void)
    {//卸载驱动,与初始化逆序//1、映射地址释放iounmap(led.gpx1con);iounmap(led.gpx1dat);//2、设备文件释放device_destroy(led.class, led.dev_no);//释放设备文件class_destroy(led.class);//释放文件信息结构体//3、驱动设备号注销unregister_chrdev(led.major,"led_dev");}//驱动加载与卸载
    module_init(led_init);
    module_exit(led_exit);//协议包含GPL
    MODULE_LICENSE("GPL");

    驱动中实现中断

    中断驱动---检测外部中断 获取外设的数据内容,通过中断信号进行获取 在驱动中设置外设为中断模式:当外设产生设定的特定信号(就是中断) 在驱动中实现中断处理操作(函数)

    本文以按键中断为例

    需要使用按键设备,需要先在设备树中说明使用的按键是一个中断设备,我使用的板子是Samsung的Exynos系列,按键使用的是KEY3

     

开发板管脚

 

查看数据手册得到GPX1_2使用的中断是XEINT_10

 

 

在设备树中:arch/arm/boot/dts/exynos4x12-pinctrl.dtsi

	gpx1: gpx1 {gpio-controller;#gpio-cells = <2>;interrupt-controller;interrupt-parent = <&gic>;interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,<0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;#interrupt-cells = <2>;};在设备树中添加自己的硬件设备信息---添加key3节点-----描述当前设备的的信息内容(中断号)arch/arm/boot/dts/exynos4412-fs4412.dts:实现硬件描述(中断号)key3_node {compatible = "key3";interrupt-parent = <&gpx1>;interrupts = <2 4>;//26};

在驱动中申请中断,实现中断处理

a、获取到中断号获取设备树节点,返回值就是从设备树中找到的节点struct device_node *of_find_node_by_path(const char *path);从节点中获取到中断号,返回值就是中断号unsigned int irq_of_parse_and_map(struct device_node *dev,int index);b、申请中断int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)参数1:unsigned int irq:申请中断的中断号参数2:irqreturn_t (*)(int, void *)    ----   irq_handler_tirq_handler_t handler:函数指针,进行注册中断,当产生中断时调用对应的函数进行处理参数3:unsigned long flags:中断处理的触发方式#define IRQF_TRIGGER_NONE	0x00000000#define IRQF_TRIGGER_RISING	0x00000001#define IRQF_TRIGGER_FALLING	0x00000002#define IRQF_TRIGGER_HIGH	0x00000004#define IRQF_TRIGGER_LOW	0x00000008参数4:const char *name:字符串首地址,中断的描述信息/proc/inruppter参数5:void *dev:传递给参数2的函数进行自动调用的(作为参数2这个函数的参数)返回值:成功返回0,失败返回非0
	释放中断:void free_irq(unsigned int irq,void * dev_id)参数1:unsigned int irq中断号参数2:void * dev_id:与申请中断第五个参数保持一致

中断分上下两部分:

上部分处理时间短、不费时间的中断处理

下部分处理一些费时间的中断

下部分就是将耗时操作延后处理,一般在上半部分的处理函数中调用

1、softirq:软中断,处理级别比较高,在内核机制中,需要修改内核源码功能 ​ 2、tasklet:实际上就是内部调用了softirq ​ 3、workqueue:工作队列

驱动中申请中断举例:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/wait.h>#include <asm/io.h>
#include <asm/uaccess.h>char c;struct key_desc{unsigned int major;struct class * cls;dev_t devno;struct device * dev;}key;irqreturn_t key_irq_handler(int i, void * j)
{c='q';printk("irqno : %d;input char %c\n",i,c);return IRQ_HANDLED;
}//3、文件IO接口功能关联
ssize_t key_read (struct file * file, char __user * data, size_t size, loff_t * ops)
{printk("key read\n");int n = copy_to_user(data, &c, 1);c='w';return 0;
}
int key_open (struct inode * inode , struct file * file)
{printk("key open\n");return 0;
}int key_release (struct inode * inode , struct file *file)
{printk("key close\n");return 0;
}const struct file_operations fops = {.read = key_read,.release = key_release,.open = key_open
};static int __init keydev_init(void)
{key.major = 252;//1、申请设备号int res = register_chrdev( key.major,"key", &fops);if(res<0){goto err1;	}//2、设备文件key.cls = class_create(THIS_MODULE, "cls");if(IS_ERR(key.cls)){goto err2;	}key.devno = key.major << 20 | 0;key.dev = device_create(key.cls, NULL,key.devno ,NULL,"key_dev");if(IS_ERR(key.dev)){goto err3;	}//4、硬件初始化//a.获取中断号struct device_node * node = of_find_node_by_path("/key3");//查找设备树中节点为key_3if(IS_ERR(node)){goto err4;	}int irqno = irq_of_parse_and_map(node, 0);//申请中断号if(irqno<0){goto err5;}//b.申请中断res = request_irq(irqno, key_irq_handler,IRQF_TRIGGER_FALLING,"key_in", NULL);if(res<0){goto err6;}return 0;
err6:irqno = -1;err5:node=NULL;err4:device_destroy(key.cls, key.devno);err3:class_destroy(key.cls);err2:unregister_chrdev(key.major, "key");err1:return -1;
}static void __exit key_exit(void)
{device_destroy(key.cls, key.devno);class_destroy(key.cls);unregister_chrdev(key.major, "key");
}module_init(keydev_init);
module_exit(key_exit);MODULE_LICENSE("GPL");

测试程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>int main()
{char buf;int num;int fd = open("/dev/key_dev",O_RDONLY);while(1){num=0;num = read(fd,&buf,1);printf("%d%c\n",num,buf);}return 0;
}

Makefile

#编译驱动代码
KERNEL_PATH = /home/ubuntu/code/kernel/linux-3.14
#APP=beep_test#测试程序的名字不包括后缀
MODULES_PATH = $(shell pwd)
obj-m += key_int.o #把.c编译为.o文件注意.o前的名字与驱动文件名字一致#编译为驱动程序.ko 要借助已经编译过的内核
all:make modules -C $(KERNEL_PATH) M=$(MODULES_PATH)
#	arm-none-linux-gnueabi-gcc $(APP).c -o $(APP)install:
#	cp *.ko $(APP) /home/ubuntu/rootfscp *.ko /home/ubuntu/rootfs

驱动中的阻塞IO与非阻塞IO实现

驱动中实现阻塞: 要创建等待队列头: wait_queue_head_t head; init_waitqueue_head(&head);

	1、在需要等待的位置(没有数据),就阻塞等待wait_event_interruptible(wq,condition)-----根据参数是否进行阻塞等待,完成阻塞等待参数1:wq:等待队列头,把当前进程加入到哪个等待队列中参数2:condition:是否执行阻塞等待的条件condition:真---不进行阻塞condition:假---进行阻塞2、合适位置进行阻塞唤醒wake_up_interruptible(&head);

非阻塞:在进行读写操作时,如果没有数据,就立即返回,如果有数据读取数据然后立即返回 应用程序:设置为非阻塞打开 int fd = open("/dev/key3",O_RDONLY | O_NONBLOCK);

	驱动文件中在阻塞前面添加:if((file->f_flags & O_NONBLOCK != 0) && (condition == 0))return -1;

驱动总线模型: 驱动框架: 0、声明实现入口函数(module_init、module_exit) 1、申请设备号(register_chrdev) 2、创建设备节点(class_create、device_create) 3、硬件初始化 ioremap地址映射 中断申请 4、实现文件IO接口

总线模型: 总线bus 驱动driver 设备device

总线bus: struct bus_type:总线对象,描述一条总线,管理device、driver,进行匹配

struct bus_type
{const char		*name;:总线名字int (*match)(struct device *dev, struct device_driver *drv);总线调用匹配设备和驱动,返回值就表示匹配成功与否
};

注册总线: ​ int bus_register(struct bus_type * bus); ​ 参数: ​ struct bus_type * bus:总线对象 ​ 注销总线: ​ void bus_unregister(struct bus_type * bus);

驱动driver: struct device_driver :驱动对象,描述一个驱动,对驱动进行说明

struct device_driver { ​ const char *name;:驱动的名字 ​ struct bus_type bus;总线对象,表示要把驱动注册到哪条总线 ​ int (probe) (struct device dev);如果匹配成功,则调用该驱动的probe函数,创建驱动(申请设备号。。。) ​ int (remove) (struct device *dev);当设备对象和驱动对象移除总线时会调用 ​ } ​ 注册驱动到总线: ​ int driver_register(struct device_driver * drv); ​ 从总线上注销: ​ void driver_unregister(struct device_driver * drv);

设备device: struct device { struct kobject kobj;//所有对象的父类 const char *init_name;设备名 struct bus_type *bus;总线对象,表示要把设备注册到哪条总线 void platform_data;自定义数据,指向任意类型,可以存储设备信息 void (release)(struct device *dev);设备对象从总线移除时会调用 }; 注册设备到总线: int device_register(struct device * dev); 从总线上注销: void device_unregister(struct device * dev);


http://chatgpt.dhexx.cn/article/801NYKk5.shtml

相关文章

Linux设备驱动开发详解

Linux设备驱动开发详解 Linux设备驱动开发详解Linux设备驱动开发Linux设备驱动开发详解1. linux设备驱动概述及开发环境搭建2. 驱动设计的硬件基础3. linux内核及内核编程4. linux内核模块5. linux文件系统与设备文件6. 字符设备驱动7. linux设备驱动中的并发控制8. linux设备驱…

QT部件基类——QWidget与QDialog

一、默认基类 QT提供的默认部件基类包括QMainWindow、QWidget、和QDialog这三种&#xff0c;这三个部件基类也是用的最多的。 QWidget类是所有部件对象的基类&#xff0c;被称为基础窗口部件&#xff0c;继承关系详看QWidget类关系图。QWidget提供自我绘制和处理用户输入等基本…

qt 中 qwidget 嵌入 qml

以上是效果图&#xff0c; 整个窗口是 qml 窗口 核心代码 #ifndef WIDGETANCHOR_H #define WIDGETANCHOR_H#include <QObject> #include <QQuickItem> #include <QWidget> #include <QQmlApplicationEngine> #include <QQmlContext>class WgtAn…

QWidget尺寸限定

1、控件只能在最小和最大之间进行调整&#xff0c;不能超过范围。 ①直接宽高同时设置 window.setMinimumSize(200, 200)&#xff1b; window.setMaximumSize(500, 500) app QApplication(sys.argv)window QWidget()window.setWindowTitle("最小最大窗口")windo…

QWidget居中显示

须知 以下函数只要继承QWidget都可以使用.例如 QDialog, QPushButton( -v- 一个居中的”引爆按钮”)关于坐标问题: qt窗口坐标原点是在”左上角”的. 如图, (x2, y2)是我窗口的分辨率的一半无论目前我的窗口在什么位置,我只要把窗口原点设置为(x1, y1)就行了.所以目前我要获得…

QWidget事件消息

1、用户操作界面时会产生特定的事件消息。 API&#xff1a;显示和关闭事件、移动事件、调整大小、鼠标事件、键盘事件、焦点事件、拖拽事件、绘制事件、改变事件、右键菜单、输入法。 应用场景&#xff1a;当一个控件被触发了一个特定的行为时&#xff0c;就会调用特定的方法…

QWidget的isHidden和isVisible

文章目录 问题的出现QWidget的show()函数QWidget的isVisible和isHidden源码追溯 QWidget的isHidden和isVisible 问题的出现 最近在写代码的时候&#xff0c;出现了一个问题&#xff0c;我新建了两个类&#xff0c;分别是Chapter2和ConsecutiveCurtain // ConsecutiveCurta…

QWidget鼠标操作

1、设置鼠标形状&#xff1a;鼠标放置在不同控件上&#xff08;有不同功能时&#xff09;鼠标的形状是不一样的。 鼠标跟踪&#xff1a;鼠标在某一个控件上移动&#xff0c;则会向对应的控件对象发送一个消息&#xff0c;去调用具体的某一个方法&#xff0c;可以在方法里面监听…

QWidget旋转方法

参考链接&#xff1a;https://stackoverflow.com/questions/43388464/rotate-whole-qwidget-by-angle 说明&#xff1a; 本文实现方法本质是使用QGraphics三件套&#xff0c;即View、Scene、Item&#xff0c;将QWidget控件作为Item显示&#xff0c;从而实现角度控制的目的&…

QWidget设置背景图及圆角

在Qt开发过程中&#xff0c;QWidget是经常作为主窗体的父窗口&#xff0c;有时我们需要对主窗口设置背景&#xff0c;设置圆角以达到美观的效果&#xff0c;通常的有以下三种方法&#xff1a;qss, QPalette设置以及paintEvent绘制。下面介绍这三种方法。 背景设置介绍 方法一&a…

QWidget之adjustSize

from PyQt5.Qt import * import sys# 创建一个应用程序对象 app QApplication(sys.argv)window QWidget()label QLabel(window) label.setText(学无止境) label.move(100, 100) label.setStyleSheet(background-color:gray)def changeCao():tmp label.text()学无止境label.…

初识QWidget

初识QWidget 在Qt中QWidget是一个非常关键和重要的类&#xff0c;推荐初学Qt的同学们第一个学习此类 在Qt的帮助手册中我们搜索QWidget&#xff0c;可以看到下图的描述 通过帮助手册我们了解到如果想使用QWidget这个类&#xff0c;需要包含QWidget这个头文件&#xff0c;Qt特…

QWidget继承

查看QWidget的继承于哪个类 方法一 随便写一个类继承自己QWidget 按住Ctrl鼠标单击QWidget即可 方法二 print(QWidget.__base__)方法三 print(QWidget.mro()) 链条式的继承展示 enjoy

QWidget(长文)

一、描述 1、QWidget 是用户界面的原子&#xff1a;它从窗口系统接收鼠标、键盘和其他事件&#xff0c;并在屏幕上绘制自己的表示。每个小部件都是矩形的&#xff0c;它们按Z顺序排序。小部件由其父部件和它前面的小部件剪裁。 2、未嵌入父窗口小部件的 QWidget 称为窗口。通…

QWidget的使用

一、QWidget介绍 QWidget是用户操作的原子接口&#xff0c;它从窗口系统中接收鼠标&#xff0c;键盘以及其他事件&#xff0c;并绘制图形界面。QT提供的默认窗口基类只有QMainWindow、QWidget、和QDialog这三种&#xff0c;QMainWindow是带有菜单栏和工具栏的主窗口类&#xf…

QWidget

QWidget QWidget是容器组件&#xff0c;继承自QObject类和QPaintDevice类。能够绘制自己和处理用户输入&#xff0c;是QT中所有窗口组件类的父类&#xff0c;是所有窗口组件的抽象&#xff0c;每个窗口组件都是一个QWidget&#xff0c;QWidget类对象常用作父组件或顶级组件使用…

QT学习总结之QWidget详解

1、说明 QWidget类是所有用户界面对象的基类。 QWidget是用户界面的原子类。它接收鼠标、键盘和来自系统的其他事件&#xff0c;并在屏幕上将它们绘制出来。每个Widget都是矩形的&#xff0c;并按照Z-order&#xff08;Z轴&#xff09;进行排序。一个Widget夹在它的Parent和它…

VS2019安装与使用教程

VS2019安装与使用教程 可能有很多小伙伴们&#xff0c;知道VS2019这个软件&#xff0c;但是不知道怎么安装与使用&#xff0c;下面我将具体介绍VS2019的安装方法与创建我们自己的C项目以及如何运行自己编写的代码&#xff01; Visual Studio 2019(VS2019)简介 Microsoft Visual…

vs2017初学c++环境配置及使用教程

作为一个计算机小白, 初学c的时候使用了vs2017, 配置环境如下 如图所示, 可以实现c的基本操作. 在vs2017的版本中, 取消了win32这个选项, 所以直接选择新建空项目. 在解决方案资源管理器中, 于源文件处新建.cpp文件, 即可执行操作. 如果出现闪现的情况, 则右键点击解决方案资…

【转载】VS2019使用技巧

大家好&#xff0c;今天分享几个我知道的实用 VS 技巧&#xff0c;而这些技巧我发现很多人都不知道。因为我经常在工作中遇到&#xff1a;我在同事电脑上解决问题&#xff0c;或在会议上演示代码示例时&#xff0c;使用了一些 VS “骚”操作&#xff0c;他们会好奇地问&#xf…