【Qt 学习笔记】详解Qt中的信号和槽

news2024/11/20 11:42:35

  • 博客主页:Duck Bro 博客主页
  • 系列专栏:Qt 专栏
  • 关注博主,后期持续更新系列文章
  • 如果有错误感谢请大家批评指出,及时修改
  • 感谢大家点赞👍收藏⭐评论✍

详解Qt中的信号与槽

文章编号:Qt 学习笔记 / 12

文章目录

  • 详解Qt中的信号与槽
    • 一、信号和槽的基本概念
      • 1. 什么是信号
        • 1.1 信号本质
        • 1.2 信号举例
      • 2. 什么是槽
        • 2.1 槽的本质
        • 2.2 说明
    • 二、信号和槽如何使用
      • 1. connect函数
        • 1.1 函数原型
        • 1.2 参数说明
        • 1.3 代码示例
      • 2. 如何查看内置信号和槽
        • 2.1 查询信号
        • 2.1 查询槽
      • 3. 使用 Qt Creator 生成信号槽代码(图形化快速生成信号槽代码)
        • 3.1 实现步骤
        • 3.2 实现结果
    • 三、自定义信号和槽
      • 1. 基础语法
        • 1.1 自定义信号函数书写规范
        • 1.2 自定义槽函数书写规范
        • 1.3 发送信号
        • 1.4 示例代码
      • 2. 带参数的信号和槽
        • 2.1 示例代码
    • 四、信号与槽的连接方式
      • 1. 一对一
        • 1.1 信号连接槽
        • 1.2 信号连接信号
      • 2. 一对多
      • 3. 多对一
    • 五、信号与槽的优缺点
      • 1. 优点: 松散耦合
      • 2. 缺点: 效率较低


一、信号和槽的基本概念

QT中的信号和槽是用于实现对象之间的通信的机制。每个对象都可以发出一个信号,其他对象可以通过连接到该信号的槽来接收并处理信号。

通过将信号和槽连接起来,可以实现对象之间的交互和通信。一个对象的信号可以连接到其他对象的槽,也可以将多个信号连接到同一个槽上。

Qt中可以使用connect函数,把一个信号和一个槽关联起来,后续只要信号触发,Qt就会自动执行槽函数。

在这里插入图片描述

信号源:由哪个控件发出信号
信号类型:用户进行不同的操作,就可能触发不同的信号
信号处理方式:槽(slot)== 函数

QT中的信号和槽是通过使用QObject类的特性来实现的,需要使用宏来声明信号和槽,并使用信号和槽的宏来进行连接。QT提供了一个QMetaObject系统来管理信号和槽的连接和调用。

使用信号和槽机制可以使代码更加灵活,模块化和可重用。它使得对象之间的交互变得简单而直观,并允许通过连接和断开连接来动态地改变交互方式。

在这里插入图片描述

1. 什么是信号

信号可以被认为是一个事件,当某些条件发生时,对象会发出一个信号。例如,鼠标点击、键盘输入或者是对象的状态改变都可以作为信号。

1.1 信号本质

信号是由于用户对窗⼝或控件进行了某些操作,导致窗⼝或控件产⽣了某个特定事件,这时 Qt 对
应的窗⼝类会发出某个信号,以此对⽤⼾的操作做出反应。

1.2 信号举例

信号的本质就是事件

  • 按钮单击、双击
  • 窗⼝刷新
  • ⿏标移动、⿏标按下、⿏标释放
  • 键盘输⼊

在 Qt 中信号是通过什么形式呈现给使用者的?

  • 我们对哪个窗⼝进⾏操作, 哪个窗⼝就可以捕捉到这些被触发的事件。
  • 对于使⽤者来说触发了⼀个事件我们就可以得到 Qt 框架给我们发出的某个特定信号。
  • 信号的呈现形式就是函数, 也就是说某个事件产⽣了, Qt 框架就会调⽤某个对应的信号函数, 通知使⽤者。

2. 什么是槽

槽是接收信号的函数,当一个信号被发出时,连接到该信号的槽会被调用。槽可以执行任意代码,包括更新界面、处理数据等。

2.1 槽的本质

槽(Slot)就是对信号响应的函数。槽就是⼀个函数,与⼀般的 C++ 函数是⼀样的,可以定义在类的任何位置(public、protected 或 private),可以具有任何参数,可以被重载,也可以被直接调用(但是不能有默认参数)。槽函数与⼀般的函数不同的是:槽函数可以与⼀个信号关联,当信号被发射时,关联的槽函数被自动执行。

2.2 说明

(1)信号和槽机制底层是通过函数间的相互调⽤实现的。每个信号都可以⽤函数来表⽰,称为信号函数;每个槽也可以用函数表示,称为槽函数。

例如: “按钮被按下” 这个信号可以⽤ clicked() 函数表示,“窗⼝关闭” 这个槽可以用 close() 函数表示,假如使⽤信号和槽机制-实现:“点击按钮会关闭窗口” 的功能,其实就是 clicked() 函数调⽤ close() 函数的效果。

(2)信号函数和槽函数通常位于某个类中,和普通的成员函数相⽐,它们的特别之处在于:

  • 信号函数⽤ signals 关键字修饰,槽函数⽤ public slots、protected slots 或者 private slots 修饰。signalsslots 是 Qt 在 C++ 的基础上扩展的关键字,专⻔⽤来指明信号函数和槽函数;

  • 信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)。

在这里插入图片描述


二、信号和槽如何使用

1. connect函数

1.1 函数原型
connect (const QObject * sender,  //sender:信号的发送者
 		 const char * signal ,    //signal:发送的信号(信号函数)
 		 const QObject * receiver , //receiver:信号的接收者
 		 const char * method , 		//method:接收信号的槽函数
 		 Qt::ConnectionType type = Qt::AutoConnection )  //目前阶段不用考虑,用的不多
		 //⽤于指定关联⽅式,默认的关联⽅式为 Qt::AutoConnection,通常不需要⼿动设定。
1.2 参数说明

• sender:信号的发送者;
• signal:发送的信号(信号函数);
• receiver:信号的接收者;
• method:接收信号的槽函数;
• type:用于指定关联方式,默认的关联⽅式为 Qt::AutoConnection,通常不需要手动设定。

1.3 代码示例

代码功能:在窗⼝中设置⼀个按钮,当点击 “按钮” 时关闭 “窗⼝”

使用connect进行信号和槽连接,实现点击按钮 关闭窗口的功能
在这里插入图片描述
示例代码:

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button -> setText("按钮");    //设置按钮文本
    button->resize(100,100);     //设置按钮大小   
    button->move(200,200);       //设置按钮位置
    
    //连接信号和槽 实现点击按钮  关闭窗口的功能
    connect(button,&QPushButton::clicked,this,&Widget::close);  
}

Widget::~Widget()
{
    delete ui;
}


2. 如何查看内置信号和槽

在上述代码中使用到的信号和槽可以在“Qt帮助文档(Assistant)”中查看,下面介绍如何查看Qt内置的信号和槽,下面以查询“QPushButton(按钮)”的信号为例。

2.1 查询信号
  1. 打开Assistant,在索引栏中输入QPushButton
    在这里插入图片描述

  2. 然后可以在 “Contents” 中寻找关键字 signals,如果没有请看下一步
    在这里插入图片描述

  3. 如果没有找到, 继续去⽗类中查找.,点击父类QAbstractButton
    在这里插入图片描述

  4. 在⽗类 QAbstractButton 中继续查找关键字signals
    在这里插入图片描述

  5. 找到信号clicked()
    在这里插入图片描述

2.1 查询槽

槽函数的寻找⽅式和信号⼀样,只不过它的关键字是 slot

3. 使用 Qt Creator 生成信号槽代码(图形化快速生成信号槽代码)

在Qt Creator中可以快速帮助我们⽣成信号槽相关的代码

3.1 实现步骤

代码示例如下:
代码功能:在窗⼝中设置⼀个按钮,当点击 “按钮” 时关闭 “窗⼝”

  1. 新建一个Qt项目,如下图为新建完成之后所包含的所有⽂件;
    在这里插入图片描述

  2. 打开widget.ui文件,进入UI界面
    在这里插入图片描述

  3. 在界面中放置一个按钮,并且修改按钮的大小及名称
    在这里插入图片描述

  4. 右键按钮,选择转到槽,生成槽函数
    在这里插入图片描述

  5. 选择信号clicked(),点击OK
    在这里插入图片描述

  6. 自动生成槽函数原型框架

  • 在 “widget.h” 头⽂件中⾃动添加槽函数的声明
    在这里插入图片描述

说明:
⾃动⽣成槽函数的名称有⼀定的规则。槽函数的命名规则为:on_XXX_SSS,其中:
1. 以 " on " 开头,中间使用下划线连接起来;
2. " XXX " 表示的是对象名(控件的 objectName 属性)。
3. " SSS "表示的是对应的信号。

如:" on_pushButton_clicked() " ,pushButton 代表的是对象名,clicked是对应的信号。

在这里插入图片描述

  • 在 “widget.cpp” 文件中⾃动⽣成槽函数定义.
    在这里插入图片描述
  1. 在槽函数函数定义中添加要实现的功能. 实现关闭窗口的效果.
    在这里插入图片描述
3.2 实现结果

点击按钮窗口关闭。
在这里插入图片描述


三、自定义信号和槽

1. 基础语法

在Qt中,自定义信号和槽可以通过使用signals和slots关键字来定义,在日常项目中用到较少。

在 Qt 中,允许⾃定义信号的发送⽅以及接收⽅,即可以⾃定义信号函数和槽函数。但是对于⾃定义的信号函数和槽函数有⼀定的书写规范

需要注意的是,为了使用自定义信号和槽,类必须包含Q_OBJECT宏,并且需要使用Qt的元对象编译系统(MOC)。这意味着类定义必须位于一个.h文件中,并且在CMake或QMake构建系统中添加适当的语句来使用MOC编译器。

1.1 自定义信号函数书写规范
  • ⾃定义信号函数必须写到 “signals” 下;
  • 返回值为 void,只需要声明,不需要实现;
  • 可以有参数,也可以发⽣重载;
1.2 自定义槽函数书写规范
  • 早期的 Qt 版本要求槽函数必须写到 “public slots” 下,但是现在⾼级版本的 Qt 允许写到类的"public" 作⽤域中或者全局下;
  • 返回值为 void,需要声明,也需要实现;
  • 可以有参数,可以发⽣重载;
1.3 发送信号
  • 使⽤ “emit” 关键字发送信号 。“emit” 是⼀个空的宏。“emit” 其实是可选的,没有什么含义,只是为了提醒开发⼈员。
1.4 示例代码
  1. 在 widget.h 中声明⾃定义的信号和槽
    在这里插入图片描述
    2.在 widget.cpp 中实现槽函数,并且关联信号和槽
    在这里插入图片描述
    示例代码:
//widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

signals:
    void MySignal();//信号声明

public:
    void MySlots();//槽函数声明

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(this,&Widget::MySignal,this,&Widget::MySlots);
    //emit MySignal();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::MySlots()
{
    this->setWindowTitle("修改窗口1");
}


void Widget::on_pushButton_clicked()
{
    emit MySignal();
}

2. 带参数的信号和槽

Qt 的信号和槽也可以带有参数, 同时也可以⽀持重载.
信号函数的参数列表要和对应连接的槽函数参数列表⼀致.

此时信号触发, 调⽤到槽函数的时候, 信号函数中的实参就能够被传递到槽函数的形参当中,就起到让信号给槽传参的效果

注意:为了使用自定义信号和槽,类必须包含Q_OBJECT宏,并且需要使用Qt的元对象编译系统(MOC)。这意味着类定义必须位于一个.h文件中,并且在CMake或QMake构建系统中添加适当的语句来使用MOC编译器。

2.1 示例代码

自定义信号和槽,参数必须一致,主要要求类型一致;个数如果不一致也可以,要求信号参数个数比槽参数个数多要多

注意:一个槽函数, 有可能会绑定多个信号,如果我们严格要求参数个数一致,就意味着信号绑定到槽的要求就变高了换而言之,当下这样的规则,就允许信号和槽之间的绑定更灵活了更多的信号可以绑定到这个槽函数上了

在这里插入图片描述
在这里插入图片描述

//widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();


signals:
        void MySignals(const QString& text1,const QString& text2);        //此处text可以省略


public:
        void handleMySignal(const QString& text);   //此处text可以省略

private slots:
        void on_pushButton_clicked();          //UI自动生成的槽函数

        void on_pushButton_2_clicked();        //UI自动生成的槽函数

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

//widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(this,&Widget::MySignals,this,&Widget::handleMySignal);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleMySignal(const QString& text)
{
    qDebug()<<text;
    this->setWindowTitle(text);
}


void Widget::on_pushButton_clicked()
{
    emit MySignals("按钮1修改参数","");
}

void Widget::on_pushButton_2_clicked()
{
    emit MySignals("按钮2修改参数","");
}


四、信号与槽的连接方式

1. 一对一

分为两种形式:⼀个信号连接⼀个槽 和 ⼀个信号连接⼀个信号。

1.1 信号连接槽

在这里插入图片描述

  • 在 “widget.h” 中声明信号和槽
    在这里插入图片描述
  • 在 “widget.cpp” 中实现槽函数,信号发射函数以及连接信号和槽
    在这里插入图片描述
1.2 信号连接信号

在这里插入图片描述

  • 在 “widget.cpp” ⽂件中添加如下代码:
    在这里插入图片描述
    在这里插入图片描述

2. 一对多

在这里插入图片描述

  • 在 “widget.h” 头⽂件中声明⼀个信号和三个槽
    在这里插入图片描述
  • 在 “widget.cpp” ⽂件中实现槽函数以及连接信号和槽;
    在这里插入图片描述

3. 多对一

在这里插入图片描述

  • 在 “widget.h” 头⽂件中声明两个信号以及⼀个槽
    在这里插入图片描述

  • 在 “widget.cpp” ⽂件中实现槽函数以及连接信号和槽
    在这里插入图片描述


五、信号与槽的优缺点

1. 优点: 松散耦合

信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了自己,Qt的信号槽机制保证了信号与槽函数的调⽤。⽀持信号槽机制的类或者⽗类必须继承QObject类。

2. 缺点: 效率较低

与回调函数相⽐,信号和槽稍微慢⼀些,因为它们提供了更⾼的灵活性,尽管在实际应⽤程序中差别不⼤。通过信号调⽤的槽函数⽐直接调⽤的速度慢约10倍(这是定位信号的接收对象所需的开销;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调⽤速度对性能要求不是⾮常⾼的场景是可以忽略的,是可以满⾜绝⼤部分场景。


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1572786.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构】ArrayList详解

目录 前言 1. 线性表 2. 顺序表 3. ArrayList的介绍和使用 3.1 语法格式 3.2 添加元素 3.3 删除元素 3.4 截取部分arrayList 3.5 其他方法 4. ArrayList的遍历 5.ArrayList的扩容机制 6. ArrayList的优缺点 结语 前言 在集合框架中&#xff0c;ArrayList就是一个…

代码随想录第19天

654. 最大二叉树 已解答 中等 相关标签 相关企业 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀…

Mac 配置 Aria2

文章目录 1. Aria2 安装1.1 安装 brew1.2 安装 Aria2 2. 配置 Aria22.1 创建配置文件 aria2.conf 和空对话文件 aria2.session2.2 编辑配置文件 aria2.conf 3. 开机启动设置3.1 创建用户启动文件3.2 管理自启动项 4. 配置 BT tracker 自动更新4.1 XIU2/TrackersListCollection …

通义灵码-ai编码

https://developer.aliyun.com/topic/lingma/activities/202403?taskCode14508&recordIdb1ef3ba27250a5818b1b6ffe418af658#/?utm_contentm_fission_1 「通义灵码 体验 AI 编码&#xff0c;开 AI 盲盒」

sourcetree提交代码出现闪退报错(已解决)

当我在sourcetree提交代码时&#xff0c;点击提交按钮出现闪退关闭&#xff0c;并弹出下面的报错框&#xff0c;报错的图片如下&#xff1a; 那么经过了解&#xff0c;出现这样的报错原因是&#xff0c;git的提交时无法定位提交的人是谁&#xff0c;导致无法提交 那么解决的方…

Allavsoft for Mac v3.27.0.8852注册激活版 优秀的视频下载工具

Allavsoft for Mac是一款功能强大的多媒体下载和转换工具&#xff0c;支持从各种在线视频网站和流媒体服务下载视频、音频和图片。它具备批量下载和转换功能&#xff0c;可将文件转换为多种格式&#xff0c;以适应不同设备的播放需求。此外&#xff0c;Allavsoft还提供视频编辑…

ARM v8 Cortex R52内核 02 程序模型 Programmers Model

ARM v8 Cortex R52内核 02 程序模型 Programmers Model 2.1 关于程序模型 Cortex-R52处理器实现了Armv8-R架构。这包括&#xff1a; 所有的异常级别&#xff0c;EL0-EL2。 每个异常级别下的AArch32执行状态。 T32和A32指令集&#xff0c;其中包括&#xff1a; 浮点运算。 …

电子商务平台中大数据的应用|主流电商平台大数据采集API接口

(一)电商平台物流管理中大数据的应用 电商平台订单详情订单列表物流信息API接口应用 电子商务企业对射频识别设备、条形码扫描设备、全球定位系统及销售网站、交通、库存等管理软件数据进行实时或近实时的分析研究,提高物流速度和准确性。部分电商平台已建立高效的物流配送网…

Ruoyi-vue-pro Vue + nginx 二级目录部署到云服务器

http://www.your-server.com/ 这是一级目录&#xff0c;由于项目多&#xff0c;一般会通过二级域名http://oa.your-server.com/或二级目录http://www.your-server.com/oa来发布&#xff0c;本篇记录一下二级目录发布。先看效果 1、router/index.js配置base export default new …

Open CASCADE学习|在给定的TopoDS_Shape中查找与特定顶点 V 对应的TopoDS_Edge编号

enum TopAbs_ShapeEnum{TopAbs_COMPOUND,TopAbs_COMPSOLID,TopAbs_SOLID,TopAbs_SHELL,TopAbs_FACE,TopAbs_WIRE,TopAbs_EDGE,TopAbs_VERTEX,TopAbs_SHAPE}; 这段代码定义了一个名为 TopAbs_ShapeEnum 的枚举类型&#xff0c;它包含了表示不同几何形状类型的常量。这些常量通常…

5G网络架构及技术(二):OFDM一

ToDo: 等把这些讲义看完后得单开一个文章整理思维导图   该部分由于内容比较重要&#xff0c;OFDM是5G物理层的基础&#xff0c;但学习时直接跳到5G OFDM去看它的那些参数设置感觉没什么意义&#xff0c;还得从发展的角度进行学习&#xff0c;先从最先用到OFDM的WiFi协议开始…

最新版两款不同版SEO超级外链工具PHP源码

可根据个人感觉喜好自行任意选择不同版本使用&#xff08;版V1或版V2&#xff09; 请将zip文件全部解压缩即可访问&#xff01; 源码全部开源&#xff0c;支持上传二级目录访问 #已更新增加大量高质量外链&#xff08;若需要增加修改其他外链请打开txt文件&#xff09; #修…

隐私计算实训营学习八:隐语SCQL的开发实践

文章目录 一、SCQL使用集成最佳实践1.1 SCQL使用流程1.2 SCQL部署1.3 SCQL使用示例 二、SCQL工作原理三、使用SecretNote上手体验SCQL 一、SCQL使用集成最佳实践 1.1 SCQL使用流程 SCQL使用&#xff1a; SCQL 开放 API 供⽤户使⽤/集成。可以使⽤SCDBClient上⼿体验(类似与My…

基于R、Python的Copula变量相关性分析及AI大模型应用

在工程、水文和金融等各学科的研究中&#xff0c;总是会遇到很多变量&#xff0c;研究这些相互纠缠的变量间的相关关系是各学科的研究的重点。虽然皮尔逊相关、秩相关等相关系数提供了变量间相关关系的粗略结果&#xff0c;但这些系数都存在着无法克服的困难。例如&#xff0c;…

Docker实战教程 第1章 Linux快速入门

2-1 Linux介绍 为什么要学Linux 三个不得不学习 课程需要&#xff1a;Docker开发最好在Linux环境下。 开发需要&#xff1a;作为一个后端程序员&#xff0c;是必须要掌握Linux的&#xff0c;这是找工作的基础门槛。 运维需要&#xff1a;在服务器端&#xff0c;主流的大型服…

735.小行星碰撞

题目&#xff1a;给定一个整数数组 asteroids&#xff0c;表示在同一行的小行星。 对于数组中的每一个元素&#xff0c;其绝对值表示小行星的大小&#xff0c;正负表示小行星的移动方向&#xff08;正表示向右移动&#xff0c;负表示向左移动&#xff09;。每一颗小行星以相同…

设置你的第一个React应用

目录 一、React入门 1.1 你好React 1.2 创建React 1.3 应用结构 二、总结 2.1 定义组件 2.2 组件源码 三、组件详解 注意事项 3.1 组件三部曲 3.2 组件通信 —— props 3.3 对象数组迭代 —— map() 3.4 事件处理 3.5 钩子函数 —— useState() 初次学习最终效果…

递归实现指数型枚举(acwing)

题目描述&#xff1a; 从 1∼n 这 n 个整数中随机选取任意多个&#xff0c;输出所有可能的选择方案。 输入格式&#xff1a; 输入一个整数 n。 输出格式&#xff1a; 每行输出一种方案。 同一行内的数必须升序排列&#xff0c;相邻两个数用恰好 1 个空格隔开。 对于没有…

3d代理模型怎么转换成标准模型---模大狮模型网

在当今的虚拟世界中&#xff0c;3D建模技术被广泛运用于游戏开发、电影制作、工业设计等领域。在3D建模过程中&#xff0c;有时会遇到需要将代理模型转换成标准模型的情况。模大狮将从理论和实践两方面&#xff0c;介绍如何将3D代理模型转换成标准模型&#xff0c;以帮助读者更…

2_5.Linux存储的基本管理

实验环境&#xff1a; 系统里添加两块硬盘 ##1.设备识别## 设备接入系统后都是以文件的形式存在 设备文件名称&#xff1a; SATA/SAS/USB /dev/sda,/dev/sdb ##s SATA, dDISK a第几块 IDE /dev/hd0,/dev/hd1 ##h hard VIRTIO-BLOCK /de…