(12)Qt事件系统(one)

news/2024/5/20 22:15:43

目录

Qt Event System

事件处理的方法

系统事件处理函数

基本事件 

窗口显示事件

 窗口关闭事件

 窗口隐藏事件

 窗口移动事件

 窗口大小改变事件

 窗口状态改变事件

鼠标事件

鼠标进入、离开事件

鼠标按下抬起事件

 鼠标双击事件

鼠标移动事件

 鼠标滚轮事件

 示例:无边框窗口移动

 键盘事件

键盘按下抬起事件

 键盘事件实现按钮移动

 焦点事件

 右键菜单

右键菜单信号与槽方式

右键菜单事件方式

 拖拽事件

 输入法事件

定时器

定时器事件方式实现

QTimer信号与槽方式创建定时器

成员函数方式

单发 


Qt Event System

        在Qt中,事件是派生自抽象QEvent类的对象,它表示应用程序内发生的事情或应用程序需要知道的外部活动的结果。事件可以由QObject子类的任何实例接收和处理,但它们与小部件尤其相关(例如自定义的按钮Button类)。Qt程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始Qt的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件,当事件发生时,Qt将创建一个事件对象

事件处理的方法

        传递事件的通常方式是调用虚函数。例如,QPaintEvent通过调用QWidget::paintEvent()来传递。这个虚函数负责进行适当的响应,通常是重新绘制小部件。如果在虚函数的实现中不执行所有必要的工作,则可以调用基类的实现。

系统事件处理函数

基本事件 

 事件处理函数是重写父类或者基类的虚函数,不需要手动调用,会在事件循环中自动调用。

#禁用Qt6以前的函数(Cmake中)
add_compile_definitions(QT_DISABLE_DEPRECATED_BEFORE=0x060000)

窗口显示事件

        除隐藏和显示窗口外,窗口最小化会发送窗口隐藏事件,正常显示会发送窗口显示事件。

#include <QApplication>
#include <QWidget>
#include <QDebug>
#include <QMessageBox>
#include <QMoveEvent>class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640,480);}~Widget(){}// 窗口显示事件void showEvent(QShowEvent* ev) override{QMessageBox::information(this, "提示", "窗口显示");}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
#include "main.moc"

 窗口关闭事件

        当Qt从窗口系统接收到一个顶级小部件的窗口关闭请求时,将用给定的事件调用此事件处理程序。

        默认情况下,接受事件并关闭小部件。您可以重新实现此函数,以更改小部件响应窗口关闭请求的方式。例如,您可以通过在所有事件上调用ignore()来防止窗口关闭。

        主窗口应用程序通常使用该函数的重新实现来检查用户的工作是否已保存,并在关闭前请求权限。

void Widget::closeEvent(QCloseEvent* ev)override
{auto ret = QMessageBox::question(this, "温馨提示", "你有未保存的操作,是否保存并关闭?");if (ret == QMessageBox::StandardButton::Yes){// 接收关闭ev->accept();// 与前一句代码效果相同 (设置为true表示接收该事件)//ev->setAccepted(true);}else{// 不接收关闭ev->ignore();//ev->setAccepted(false);}
}

 

 窗口隐藏事件

void Widget::hideEvent(QHideEvent* ev)override
{qInfo() << "我隐藏啦~";
}

 窗口移动事件

// 窗口或控件移动事件
void moveEvent(QMoveEvent* ev) override
{qInfo() << "移动事件: " << ev->oldPos() << ev->pos();
}

 窗口大小改变事件

// 窗口大小改变事件
void resizeEvent(QResizeEvent* ev) override
{qInfo() << "窗口大小改变: " << ev->oldSize() << ev->size();
}

 窗口状态改变事件

        如果需要检测程序中,某些东西是否发生了改变,可以通过void QWidget::changeEvent(QEvent *event)来检测。

  • 以下是常用事件:

    • QEvent::FontChange

    • QEvent::WindowTitleChange

    • QEvent::IconTextChange

    • QEvent::ModifiedChange

    • QEvent::MouseTrackingChange

    • QEvent::WindowStateChange

  • QEvent::WindowStateChange窗口状态改变为例!

void Widget::changeEvent(QEvent* ev)override
{if (ev->type() == QEvent::WindowStateChange){auto we = static_cast<QWindowStateChangeEvent*>(ev);qInfo() << "oldState" << we->oldState();qInfo() << "curState" << this->windowState();}
}

鼠标事件

鼠标进入、离开事件

#include <QApplication>
#include <QWidget>
#include <QDebug>
#include <QPushButton>
#include <QMouseEvent>class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640,480);btn = new QPushButton("Button", this);//启用鼠标追踪setMouseTracking(true);}~Widget(){}//鼠标进入void enterEvent(QEnterEvent* ev) override{//鼠标进入事件(进入客户区响应)btn->setStyleSheet("background-color:red;");}//鼠标离开void leaveEvent(QEvent* ev) override{//一般是重写按钮类,然后重写鼠标进入和离开事件btn->setStyleSheet("background-color:green;");}
private:QPushButton* btn;
};int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
#include "main1.moc"

 

鼠标按下抬起事件

 //鼠标按下
void mousePressEvent(QMouseEvent* ev)override
{//判断那个键按下if(ev->button() == Qt::MouseButton::LeftButton){//鼠标左键按下 按钮右移btn->move(btn->x() + 10,btn->y());//鼠标点击的坐标(局部的坐标)btn->setText(QString("%1,%2").arg(ev->pos().x()).arg(ev->pos().y()));}
}
//鼠标抬起
void mouseReleaseEvent(QMouseEvent* ev) override
{//判断什么键释放了if(ev->button() == Qt::MouseButton::LeftButton){btn->move(btn->pos()+QPoint(0,10));}
}

 鼠标双击事件

//鼠标双击
void mouseDoubleClickEvent(QMouseEvent* ev) override
{qDebug() << __FUNCTION__;
}

 

鼠标移动事件

使用鼠标移动事件的时候需要启用鼠标追踪

//启用鼠标追踪
setMouseTracking(true);
//鼠标移动
void mouseMoveEvent(QMouseEvent* ev) override
{//鼠标移动事件不会自动追踪鼠标,只有当有鼠标按键按下状态才会进行鼠标的追踪qInfo() << __FUNCTION__;//当启用鼠标追踪之后无需按下也可追踪鼠标//获取鼠标按键(不能通过ev->button()获取)左右键均按下获取不到Qt::MouseButton::LeftButtonif(ev->buttons() == Qt::MouseButton::LeftButton)   //buttons == button state{qInfo() << "鼠标左键按下并移动" << ev->buttons();}//这样,左右键均按下的情况也可以获取到if(ev->buttons() & Qt::MouseButton::LeftButton){qInfo() << ev->buttons();}
}

        没有启用鼠标追踪的情况下,鼠标不按下不能进行鼠标追踪

         启用鼠标追踪后,不按下也能追踪鼠标的位置

 鼠标滚轮事件

        返回轮子旋转的相对量,单位为八分之一度。正值表示转轮向前旋转,远离用户;负值表示转轮向后向用户旋转。angleDelta().y()提供自上一个事件以来旋转普通垂直鼠标滚轮的角度。如果鼠标有水平滚轮,angleDelta().x()提供水平鼠标滚轮旋转的角度,否则就是0。有些鼠标允许用户倾斜滚轮来进行水平滚动,有些触摸板支持水平滚动手势;它也会出现在angleDelta().x()中。

        大多数鼠标类型的工作步长为15度,在这种情况下,delta值是120的倍数;即120单位* 1/8 = 15度。

        然而,有些鼠标的滚轮分辨率更高,发送的delta值小于120单位(小于15度)。为了支持这种可能性,可以累计添加来自事件的增量值,直到达到120的值,然后滚动小部件,或者可以部分滚动小部件以响应每个轮事件。但是为了提供更原生的感觉,您应该更喜欢在有pixelDelta()的平台上使用它。

//鼠标滚轮
void wheelEvent(QWheelEvent* ev) override
{qInfo() << ev->angleDelta().x()/8     //横向滚动 120单位/8 =  15度<< ev->angleDelta().y()/8;    //纵向滚动 120单位/8 =  15度
}

 示例:无边框窗口移动

        在做窗口应用程序时,为了使窗口更简洁,有时候会将窗口setWindowFlag();属性设置为“Qt::WindowType::FramelessWindowHint”,这时,窗口将不可拖动,但我们可以通过以下代码尝试解决无边框窗体的拖动问题。

#include <QApplication>
#include <QWidget>
#include <QMouseEvent>
#include <QPushButton>class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640,480);//设置窗口无边框setWindowFlag(Qt::WindowType::FramelessWindowHint);auto btn = new QPushButton("关闭窗口", this);connect(btn,&QPushButton::clicked,this,&Widget::close);}~Widget(){}void mouseMoveEvent(QMouseEvent* ev) override{if(ev->buttons() & Qt::MouseButton::LeftButton){QPoint pos = ev->pos();//鼠标位置的全局坐标 - 鼠标按下的位置(局部坐标)//move(mapToGlobal(pos) - pressPos);move(ev->globalPosition().toPoint() - pressPos);}}void mousePressEvent(QMouseEvent* ev) override{pressPos = ev->pos();}
private:QPoint pressPos;        //保存鼠标按下的坐标
};int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
#include "main.moc"

 键盘事件

键盘按下抬起事件

// 基本使用    判断是什么按键按下(字符不区分大小写)不是字符消息
void keyPressEvent(QKeyEvent* ev) override
{//如果ev没有使用,不想让编译器提示未使用的变量Q_UNUSED(ev)//当窗口中有上下左右键时,按钮中有控件(焦点在控件上按上下左右是没有反应的)qInfo() << "keyPressEvent:" << Qt::Key(ev->key());//返回此键生成的Unicode文本。不同平台按下Shift、Control、Alt和Meta等修饰键时的返回值不同,可能返回空字符串。qInfo() << "text:" << ev->text();//获取按下了什么键盘修饰符(shift、control、alt、meta(windows键)、keypad(小键盘,即数字键))qInfo() << ev->modifiers();
}

//键盘按下事件
void keyPressEvent(QKeyEvent* ev) override
{switch(ev->key()){case Qt::Key::Key_Up:qInfo() << "up";break;default:break;}//快捷键 Ctrl + Aif(ev->modifiers() & Qt::KeyboardModifier::ControlModifier &&ev->key() == Qt::Key::Key_A){qInfo() << "Ctrl + A";}//直接有判断是否有某个快捷键if(ev->matches(QKeySequence::Copy)){qInfo() << "matches" << "Ctrl + C";}
}

// 键盘抬起事件
void keyReleaseEvent(QKeyEvent* ev) override
{switch(ev->key()){case Qt::Key::Key_Up:qInfo() << "up release";break;default:break;}
}

//获取快捷键(显示)
for(int i = 0;i < 71; i++)
{qInfo() << QKeySequence::StandardKey(i)<< QKeySequence(QKeySequence::StandardKey(i)).toString();
}//设置窗口关闭时销毁对象(而不是隐藏)
setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);

 键盘事件实现按钮移动

class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){setWindowFlag(Qt::WindowType::FramelessWindowHint);resize(640, 480);button = new QPushButton("玩蛇", this);// 设置按钮不可获取焦点(这样才能触发上下左右按键)button->setFocusPolicy(Qt::FocusPolicy::NoFocus);}void keyPressEvent(QKeyEvent* ev)override{switch (ev->key()){       case Qt::Key::Key_Up:button->move(button->pos() + QPoint(0, -1));break;case Qt::Key::Key_Down:button->move(button->pos() + QPoint(0, 1));break;case Qt::Key::Key_Left:button->move(button->pos() + QPoint(-1, 0));break;case Qt::Key::Key_Right:button->move(button->pos() + QPoint(1, 0));break;}}
private:QPushButton* button = nullptr;
};

 焦点事件

        这个事件处理程序可以在子类中重新实现,以接收小部件的键盘焦点事件(焦点接收)。

        小部件通常必须将focuspolicy()设置为Qt::NoFocus以外的东西,以便接收焦点事件。(注意,程序员可以在任何小部件上调用setFocus(),即使是那些通常不接受焦点的小部件。)

        默认实现更新小部件(不指定focusPolicy()的窗口除外)。

// 按tab键可以切换焦点
#include <QApplication>
#include <QWidget>
#include <QPushButton>class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640,480);for(int i = 0; i < 3; i++){btns[i] = new QPushButton("Button",this);btns[i]->move(i*100,i*100);}//为窗口设置焦点之后才可以触发焦点事件this->setFocus();}~Widget(){}//焦点进入事件void focusInEvent(QFocusEvent* ev)override{qInfo() << "focus In";btns[1]->setStyleSheet("border:3px solid red;");}//焦点退出事件void focusOutEvent(QFocusEvent* ev)override{qInfo() << "focus Out";btns[1]->setStyleSheet("border:3px solid green;");}
private:QPushButton* btns[3];
};int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
#include "main.moc"

 右键菜单

右键菜单信号与槽方式

// 信号方式
#include <QApplication>
#include <QWidget>
#include <QContextMenuEvent>class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640,480);// 设置上下文菜单选项this->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);connect(this, &Widget::customContextMenuRequested, this, &Widget::slot_contextMenu);}~Widget(){}
private slots:void slot_contextMenu(const QPoint& pos){qInfo() << "右键菜单弹出" << pos;}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
#include "main.moc"

右键菜单事件方式

// contextMenuEvent方式void  Widget::contextMenuEvent(QContextMenuEvent* ev){// 直接在此处弹出菜单即可qInfo() << "报告长官,请求弹出上下文菜单,也就是右键菜单!"<< "请弹出在\n"<< "全局坐标:"<<ev->globalPos()<< "局部坐标:"<<ev->pos() << "位置";     }

 拖拽事件

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QDragEnterEvent>
#include <QMimeData>class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640,480);//设置窗口接受拖拽setAcceptDrops(true);}~Widget(){}//拖拽事件//使用时需要设置窗口接受拖拽void dragEnterEvent(QDragEnterEvent* ev)override{const QMimeData* pData = ev->mimeData();quint64 pos = pData->text().lastIndexOf(".");QString str = pData->text().right(pData->text().size() - pos);qInfo() << str;if(str == ".doc")//拖拽进入控件时调用ev->acceptProposedAction();     //接受拖拽的建议操作}void dropEvent(QDropEvent* ev)override{//拖拽放下时调用const QMimeData* pData = ev->mimeData();qInfo() << pData->text();                   //获取文件路径}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
#include "main.moc"

 输入法事件

// 输入法事件
void inputMethodEvent(QInputMethodEvent* ev)
{qInfo() << ev->commitString() << ev->preeditString();
}

定时器

定时器事件方式实现

// 定时器事件实现定时器
class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){//启动一个定时器,并返回定时器ID(可设置定时精度)_timerID = startTimer(1000);} void timerEvent(QTimerEvent* ev)override{if (ev->timerId() == _timerID){qInfo()<<_timerID<<"timeout";}}
private:int _timerID;
};

QTimer信号与槽方式创建定时器

// 定时器的实现方式2(QTimer)
#include <QTimer>class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){//创建定时器m_timer = new QTimer(this);connect(m_timer, &QTimer::timeout, this, [](){qInfo() << "hello world";});// 定时器开始(一秒执行一次)m_timer->start(1000);} 
private:QTimer* m_timer;
};

 成员函数方式

// 使用成员函数callOnTimeout代替connect
#include <QTimer>class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){//创建定时器m_timer = new QTimer(this);m_timer->callOnTimeout(this, [](){qInfo() << "hello world";});//m_timer->callOnTimeout([](){qInfo() << "Hello World!";});// 定时器开始(一秒执行一次)m_timer->start(1000);} 
private:QTimer* m_timer;
};

单发 

// QTimer静态成员函数(单次发射)
QTimer::singleShot(1000, [](){qInfo() << "Hello World";
});


http://www.mrgr.cn/p/13541841

相关文章

【多模态】21、BARON | 通过引入大量 regions 来提升模型开放词汇目标检测能力(CVPR2021)

文章目录 一、背景二、方法2.1 主要过程2.2 Forming Bag of Regions2.3 Representing Bag of Regions2.4 Aligning bag of regions 三、效果 论文&#xff1a;Aligning Bag of Regions for Open-Vocabulary Object Detection 代码&#xff1a;https://github.com/wusize/ovdet…

怎么通过通过 p 名称空间配置 bean以及怎么去引用/注入其它 bean 对象--ref和怎么去引用/注入内部 bean 对象-内部 bean 对象

&#x1f600;前言 本章是spring基于XML 配置bean系类中第2篇讲解怎么通过通过 p 名称空间配置 bean以及怎么去引用/注入其它 bean 对象–ref和怎么去引用/注入内部 bean 对象 &#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0…

了解Unity编辑器之组件篇Miscellaneous(九)

一、Aim Constraint&#xff1a;是一种动画约束&#xff0c;用于使一个对象朝向另一个对象或一个指定的矢量方向 Activate按钮&#xff1a;用于激活或停用Aim Constraint。当Aim Constraint处于激活状态时&#xff0c;其约束效果将应用于目标对象。 Zero按钮&#xff1a;用于将…

工业平板电脑优化汽车工厂的生产流程

汽车行业一直是自动化机器人系统的早期应用领域之一。通过使用具有高负载能力和远程作用的大型机械臂&#xff0c;汽车装配工厂可以实现点焊、安装挡风玻璃、安装车轮等工作&#xff0c;而较小的机械手则用于焊接和安装子组件。使用机器人系统不仅提高了生产效率&#xff0c;还…

福特汽车在全球电动汽车市场的主导地位正在不断扩大

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 2023年7月27日&#xff0c;美国最大的汽车巨头之一福特汽车(F)公布了其2023年第二季度财报。 2023年7月6日&#xff0c;福特汽车宣布&#xff0c;第二季度美国市场的汽车销量已经较2023年第一季度增长了11.7%&#xff0c;令…

前后端分离开发流程

1、介绍 在前后端分离开发中&#xff0c;前端负责用户界面和交互逻辑的实现&#xff0c;后端则处理业务逻辑和数据持久化。这种开发模式的优势在于前后端可以独立进行开发&#xff0c;提高了开发效率&#xff0c;并且使得前后端可以采用不同的技术栈来实现各自的功能。 2、开…

学习笔记|大模型优质Prompt开发与应用课(二)|第二节:超高产文本生成机,传媒营销人必备神器

文章目录 01 文字写作技能的革新&#xff0c;各行各业新机遇四大类常见文字工作新闻记者的一天新闻记者的一天–写策划prompt 新闻记者的一天–排采访prompt生成结果prompt生成结果 大模型加持&#xff0c;文字写作我们如何提效营销创作营销创作-使用预置法为不同平台生成文案p…

以智慧监测模式守护燃气安全 ,汉威科技“传感芯”凸显智慧力

城市燃气工程作为城市基建的重要组成部分&#xff0c;与城市居民生活、工业生产紧密相关。提升城市燃气服务质量和安全水平&#xff0c;也一直是政府和民众关注的大事。然而&#xff0c;近年来居民住宅、餐饮等工商业场所燃气事故频发&#xff0c;时刻敲响的警钟也折射出我国在…

Mybatis插件

文章目录 1. 如何自定义插件1.1 创建接口Interceptor的实现类1.2 配置拦截器1.3 运行程序 2. 插件原理2.1 解析过程2.2 创建代理对象2.2.1 Executor2.2.2 StatementHandler2.2. 3ParameterHandler2.2.4 ResultSetHandler 2.3 执行流程 1. 如何自定义插件 1.1 创建接口Intercep…

el-table 表格头部合并

<el-table v-loading"listLoading" :key"tableKey" :data"list" stripe border fit highlight-current-rowstyle"width: 100%;" size"mini"><el-table-column label"第一行" align"center">…

Vue3 导出word

&#x1f642;博主&#xff1a;锅盖哒 &#x1f642;文章核心&#xff1a;导出word 目录 1.首先&#xff0c;你需要安装docxtemplater库。可以使用npm或yarn来安装&#xff1a; 2.在Vue组件中&#xff0c;你可以使用docxtemplater来生成Word文档并提供一个导出按钮供用户下载…

2023软件设计师中级备考经验分享(文中有资料链接分享)

先摊结论吧&#xff0c;软考中级设计师备考只是备考半个月&#xff08;期间还摆烂了几天&#xff09;&#xff0c;然而成绩如下&#xff1a; 我自己都没想到会这么好的成绩。。。 上午题&#xff1a;推荐把软考通APP里的历年真题刷3-4遍&#xff0c;直接刷真题&#xff0c;然后…

白盒测试和黑盒测试的区别是什么?

前言 曾言道“黑猫&#xff0c;白猫&#xff0c;只要能抓住老鼠就是好猫”。我们的测试亦是如此&#xff0c;不管是黑盒测试还是白盒测试&#xff0c;只要能测试出来bug&#xff0c;可以找出问题所在&#xff0c;保障软件质量就是好的测试方法。 对于刚入门的软件测试小白来说…

PHP数组转对象和对象转数组

PHP数组转对象和对象转数组 <?php function array_to_object($arr){$obj new stdClass();foreach ($arr as $key > $val) {if (is_array($val) || is_object($val)) {$obj->$key array_to_object($val);} else {$obj->$key $val;}}return $obj; } function o…

Verilog语法学习——LV2_异步复位的串联T触发器

LV2_异步复位的串联T触发器 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 题目描述&#xff1a; 用verilog实现两个串联的异步复位的T触发器的逻辑&#x…

【Python数据分析】Python常用内置函数(二)

&#x1f389;欢迎来到Python专栏~Python常用内置函数&#xff08;二&#xff09; ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;Python学习专栏 文章作者技术和水平有限&#xff0c;如果文…

【机器学习】西瓜书学习心得及课后习题参考答案—第3章线性模型

过了一遍第三章&#xff0c;大致理解了内容&#xff0c;认识了线性回归模型&#xff0c;对数几率回归模型&#xff0c;线性判别分析方法&#xff0c;以及多分类学习&#xff0c;其中有很多数学推理过程以参考他人现有思想为主&#xff0c;没有亲手去推。 术语学习 线性模型 l…

为Android构建现代应用——主体结构

创建Screents和ViewModels 在前面的章节中&#xff0c;我们已经分析了OrderNow项目的理论概念和我们将赋予的组织。 在本章中&#xff0c;我们将开始实现初始结构和模板&#xff0c;这将联接每一个应用程序的部分。 首先将添加以下带有各自视图模型的主屏幕&#xff1a; •…

ElementUI 实现动态表单数据校验(已解决)

文章目录 &#x1f34b;前言&#xff1a;&#x1f34d;正文1、探讨需求2、查阅相关文档&#xff08;[element官网](https://element.eleme.cn/#/zh-CN/component/form)&#xff09;官方动态增减表单项示例3、需求完美解决4、注意事项 &#x1f383;专栏分享&#xff1a; &#…

linux NDK交叉编译rtmp 与 ffmpeg+rtmp交叉编译(v7a,v8a) 完成流程

最近在学RTMP,记录一下完成的编译流程 我是mac 电脑,但是mac上编译一直通过不了,后来才换到服务器上编译, 其实mac也能编译,只是最开始踩到坑里面了… 这里记录一下linux编译完整流程 环境: NDK: android-ndk-r17cFfmpeg: ffmpeg4.2.2 (高版本也可以编译)system: mac 1. …