工作中学习自己的Qt知识误区-Version3
1.关与多层继承关系的梳理
基类a,被b继承,如果一个b被三个派生类继承,那么我三个派生类中的一个实现一个write()存到QList<a*>list种,那么我将a动态转换成b,那b的write()函数实现的是谁的
在面向对象编程中,当你有一个基类 a
被类 b
继承,并且 b
又被三个不同的派生类继承时,这形成了一个继承层次结构。当你将一个派生类的实例动态转换成其基类 b
的指针时,你需要了解多态和虚函数的工作原理。
以下是一个简化的例子:
cpp
复制
class a {
public:virtual void write() = 0; // 纯虚函数,a 是一个抽象类
};class b : public a {
public:void write() override {// b 类的 write() 实现可能会被覆盖}
};class derived1 : public b {
public:void write() override {// derived1 类的 write() 实现}
};class derived2 : public b {
public:void write() override {// derived2 类的 write() 实现}
};class derived3 : public b {
public:void write() override {// derived3 类的 write() 实现}
};QList<a*> list;
derived1 d1;
derived2 d2;
derived3 d3;list << &d1 << &d2 << &d3; // 存储派生类的指针到 QList<a*>
现在,假设你有一个 a
类型的指针,指向 derived1
类型的对象,并且你将其动态转换为 b
类型的指针:
cpp
复制
a* ptr = &d1; // ptr 指向 derived1 的实例
b* bPtr = dynamic_cast<b*>(ptr); // 动态转换到 b 类型
当你调用 bPtr->write();
时,会发生以下情况:
- 如果
derived1
的write()
方法没有被覆盖,那么将调用b
类的write()
方法。 - 如果
derived1
的write()
方法被覆盖了(如上面例子所示),那么即使你将指针转换成了b
类型,多态性仍然确保调用的是derived1
的write()
方法。
这是因为虚函数表(vtable)的工作原理。每个具有虚函数的对象都有一个指向其类的虚函数表的指针。当你使用基类的指针调用一个虚函数时,实际调用的函数取决于对象的实际类型,而不是指针的类型。
因此,当你执行 bPtr->write();
时,如果 bPtr
实际上指向一个 derived1
的实例,那么将调用 derived1
的 write()
方法。同理,如果 bPtr
指向 derived2
或 derived3
的实例,那么将调用相应派生类的 write()
方法。
还是继承和多态的学习和应用
- 抽象类定义为基类,
- 该基类所有的方法都得被中间的继承的类给定义或者实现,否则会报错
QTreeView,QTableView,QStandardItemModel,QStandardItem,QModelIndex的学习
好的,以下是详细列出所有方法,包括 setCurrentIndex
,涵盖了 QTreeView
、QTableView
、QStandardItemModel
、QModelIndex
和 QStandardItem
中常用的主要方法。
1. QTreeView
和 QTableView
(视图类):
这两个类有很多相似的功能,继承自 QAbstractItemView
,常用的方法如下:
常用方法:
setModel(QAbstractItemModel \*model)
设置视图的模型。model()
返回当前视图所使用的模型。setRootIndex(const QModelIndex &index)
设置视图的根索引,指定从哪个节点开始显示。setCurrentIndex(const QModelIndex &index)
设置当前选中的索引。rootIndex()
获取当前视图的根索引。expand(const QModelIndex &index)
展开指定的索引项(仅对树形视图有效)。collapse(const QModelIndex &index)
折叠指定的索引项(仅对树形视图有效)。scrollTo(const QModelIndex &index)
滚动视图,使指定的项可见。selectionModel()
返回与视图相关联的选择模型。setSelectionModel(QItemSelectionModel \*selectionModel)
设置视图的选择模型。setColumnWidth(int column, int width)
设置列宽(仅适用于QTableView
)。resizeColumnsToContents()
自动调整所有列的宽度以适应内容。setItemDelegate(QAbstractItemDelegate \*delegate)
设置一个自定义的项委托来控制项的渲染和编辑。viewport()
获取视图的显示区域(通常是滚动区域)。isIndexHidden(const QModelIndex &index)
判断指定的索引项是否被隐藏。setIndexWidget(const QModelIndex &index, QWidget \*widget)
在指定索引位置插入一个自定义的控件。
2. QStandardItemModel
(模型类):
QStandardItemModel
是一个通用的模型类,适用于 QTreeView
和 QTableView
,与 QStandardItem
配合使用。
常用方法:
setItem(int row, int column, QStandardItem \*item)
设置指定位置的项。item(int row, int column)
返回指定位置的项。appendRow(QStandardItem \*item)
向模型中添加一行。appendRow(const QList<QStandardItem \*> &items)
向模型中添加一行多个项。insertRow(int row, const QList<QStandardItem \*> &items)
在指定位置插入一行。removeRow(int row)
移除指定行。removeRows(int row, int count)
移除多行。setHorizontalHeaderLabels(const QStringList &labels)
设置列的标题。setVerticalHeaderLabels(const QStringList &labels)
设置行的标题。setData(const QModelIndex &index, const QVariant &value, int role)
设置指定索引位置的数据。setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
设置指定项的所有角色数据。clear()
清空模型中的所有数据。setRowCount(int rowCount)
设置模型的行数。setColumnCount(int columnCount)
设置模型的列数。
3. QModelIndex
(索引类):
QModelIndex
是对模型中某一项的封装,用于表示模型项的位置和相关信息。
常用方法:
isValid()
判断索引是否有效。row()
返回索引所在的行号。column()
返回索引所在的列号。parent()
返回索引的父项。sibling(int row, int column)
返回指定行列的兄弟项的索引。model()
返回索引关联的模型。data(int role)
获取索引位置的特定数据。setData(const QVariant &value, int role)
设置该项数据(需要模型支持)。internalPointer()
返回一个指向数据内部指针的指针(通常不需要手动调用)。isValid()
判断索引是否有效。
4. QStandardItem
(项类):
QStandardItem
用来表示模型中的一项数据,通常与 QStandardItemModel
一起使用。
常用方法:
setData(const QVariant &value, int role)
设置项的数据,指定角色。data(int role)
获取项的数据。setText(const QString &text)
设置项的文本。text()
获取项的文本。setIcon(const QIcon &icon)
设置项的图标。setCheckState(Qt::CheckState state)
设置项的复选框状态。checkState()
获取项的复选框状态。setEditable(bool editable)
设置项是否可编辑。setSelectable(bool selectable)
设置项是否可选择。setBackground(const QBrush &brush)
设置项的背景颜色。setForeground(const QBrush &brush)
设置项的前景颜色(文本颜色)。setFont(const QFont &font)
设置项的字体。setToolTip(const QString &toolTip)
设置项的提示文本。setData(int role, const QVariant &value)
设置项的指定角色的数据。setFlags(Qt::ItemFlags flags)
设置项的标志。
这些方法涵盖了视图、模型、索引以及单个项的数据操作和界面显示控制。在实际开发中,你可以根据需要使用这些方法来实现数据的展示、交互和修改。
如果有其他具体问题,随时向我提问!
当出现链接错误时
一、这种链接错误是重复定义了,需要把重复的删除一点
通常是Link2005的错误JZLibCutConfig.lib(JZLibCutConfig.dll):-1: error: LNK2005: "public: class PLC_Abstract * __cdecl PLCProgramManageInstance::CreateCallingSentanceFor(enum PLCProgramManageInstance::PLCType,int)" (?CreateCallingSentanceFor@PLCProgramManageInstance@@QEAAPEAVPLC_Abstract@@W4PLCType@1@H@Z) already defined in plcprogrammanageinstance.obj
-
这种连接错误,通常是在.h文件定义了,但是没有实现导致的
-
或者是目标函数的层级不对,就是比如你的子窗口不能调用父窗口的一些内容,这个就是层级不对造成的
二、层级不对
1、给类添加Q_DECL_EXPORT关键字,就表示可以不同层级的也可以进行导入,比如class a {} 变为class Q_DECL_EXPORT a{}
2、让a在同级目录下面
Link2019exp_dlgplcprogramquickedit.obj:-1: error: LNK2019: unresolved external symbol "public: void __cdecl Exp_DlgPLCProgramQuickEdit::slot_QuitBtn(void)" (?slot_QuitBtn@Exp_DlgPLCProgramQuickEdit@@QEAAXXZ) referenced in function "public: virtual void __cdecl Exp_DlgPLCProgramQuickEdit::initWidget(void)" (?initWidget@Exp_DlgPLCProgramQuickEdit@@UEAAXXZ)
三、对应的类没有加作用域限定符,从而导致链接错误
如何批量删除Qt中的文件夹以及文件夹下的代码
1、找到对应目录下的CutConfig.pro文件
比如要删除PLC_Program文件夹,先把对应PLC_Program对应的文件全部删除掉
SOURCES +=\versionAdapter/V1/configstep_V1.cpp \cutHead/panelconfigcuthead_ecatfocusaxiscontrol.cpp \commandiooutmenu.cpp \config/configgas.cpp \config/configcustomcondition.cpp \panelconfigcustomcondition.cpp \customconditionwarning.cpp \panelconfigplc.cpp \PLC_Program/plc_function.cpp \PLC_Program/plc_functionblock.cpp \PLC_Program/plc_program.cpp \PLC_Program/plc_abstract.cpp \PLC_Program/plcprogrammanageinstance.cpp \PLC_Program/plc_variable.cpp \PLC_Program/plc_logic.cpp \PLC_Program/UI/plcoptionsview.cpp \PLC_Program/UI/plcprogramview.cpp \PLC_Program/UI/progsentanceitem.cpp \PLC_Program/UI/plcparameditview.cpp \PLC_Program/plc_atomicfunction.cpp \PLC_Program/plc_standardprocess.cppHEADERS += \versionAdapter/V1/configstep_V1.h \versionAdapter/V1/common_V1.h \cutHead/panelconfigcuthead_ecatfocusaxiscontrol.h \commandiooutmenu.h \config/configgas.h \config/configcustomcondition.h \panelconfigcustomcondition.h \customconditionwarning.h \panelconfigplc.h \PLC_Program/plc_function.h \PLC_Program/plc_functionblock.h \PLC_Program/plc_program.h \#PLC_Program/plc_predefine.h \PLC_Program/plc_abstract.h \PLC_Program/plcprogrammanageinstance.h \PLC_Program/plc_variable.h \PLC_Program/plc_logic.h \PLC_Program/UI/plcoptionsview.h \PLC_Program/UI/plcprogramview.h \PLC_Program/UI/progsentanceitem.h \PLC_Program/UI/plcparameditview.h \PLC_Program/plc_atomicfunction.h \PLC_Program/plc_standardprocess.hFORMS += \$$PWD/Tools/Exp_Password.ui \$$PWD/widgetexpandcardscan.ui
2、在找对对用的文件夹,把该文件夹删除掉就好了
3、然后重新清除,qmake,构建
在使用QTreeView,QStandardItemModel,QTableView的时候,显示的第一行有一个数字1的解决方法
在 QTreeView
,QStandardItem
,QStandardItemModel
中使用了 m_PLCProgramsView
,并且为它设置了 m_PLC_tree_model
作为数据模型:
cpp复制编辑m_PLCProgramsView = new QTreeView(this);
m_PLC_tree_model = new QStandardItemModel(m_PLCProgramsView);
m_PLCProgramsView->setModel(m_PLC_tree_model);
在默认情况下,QStandardItemModel
会为每一列创建一个表头项(也就是显示数字 1
),这就是你看到的数字 1
。你可以通过隐藏 QTreeView
的表头来解决这个问题。
你可以通过调用 setHeaderHidden(true)
来隐藏表头,避免显示数字 1
:
复制编辑
m_PLCProgramsView->header()->hide(); // 隐藏表头
或者:
复制编辑
m_PLCProgramsView->setHeaderHidden(true); // 隐藏表头
这两种方法都可以有效地隐藏表头,使得你在 QTreeView
中不会看到默认的数字 1
。
关于static和extern的使用和误区
对于static的使用错误方式
//a.hstatic bool flag; // 如果我这样定义全局静态变量,该变量只能在a.h和a.cpp中进行访问
extern bool flagB; // 这样定义全局静态变量,该变量可以在任何的文件中进行访问,而且仅此一份数据可供修改和使用class A {void show();
};
// a.cppbool flag = true;A::show() {flag = false;
}
// b.h
#include "a.h"class B{void showB();
}
// b.cppB::showB() {cout << "flag = " << flag;
}
static:
作用:用于声明一个在当前文件内有效的变量或函数。即限定变量或函数的作用域为当前文件,其他文件无法访问。
链接类型:static
声明的是内部链接,即该变量或函数的链接只限于当前文件,其他文件无法访问或修改。
访问权限:该变量或函数只能在声明它的文件内部访问,外部文件无法直接引用。
extern
关键字
作用:用于声明一个全局变量或函数,表明该变量或函数在其他地方定义。
链接类型:extern
声明的是外部链接,即该变量或函数可以在多个源文件中共享和访问。
访问权限:其他文件可以通过 extern
声明来引用该变量或函数。
往以有的枚举中添加枚举项的时候,一定要添加到最后位置
enum EnumFuncMark{FuncMark_No = 0x0,FuncMark_Ins, // 指令(调高器 控制器 通用)FuncMark_InsResult, // 指令反馈/// 控制器FuncMark_EncoderDetection, // 编码器检测反馈结果FuncMark_TrajErrMeasure, // 轨迹误差测定反馈结果FuncMark_AutoFeedParam, // 自动送料参数(拉料)FuncMark_GasDACheck = 50, //气体DA校正FuncMark_SyncSystemTime, //同步系统时间FuncMark_GrooveLinearCenterStruct, //坡口线性寻中FuncMark_OtherInfoConf = 110, //机床相关参数/// 总线FuncMark_EcatScanResult = 200, //总线扫描结果// PLC读写FuncMark_PLCReadWriteFile, // 在配置工具中流程的保存后生成的plc读写文
};
就比如这样已经完成枚举的类,
我要再次添加枚举的时候,就只能往枚举的最后位置添加,否则就会导致枚举的位置错位
我将其添加到中间位置的时候,那中间及其以下的都会被错位掉。
union共用体的使用和知识误区
共用体示例:存储不同类型的数据
假设你需要设计一个数据结构,可以存储不同类型的数字,比如整数、浮点数和字符。为了节省内存,你希望它们共用同一块内存空间。
#include <iostream>
using namespace std;// 定义一个共用体
union Data {int i; // 整数float f; // 浮点数char c; // 字符
};int main() {Data data; // 创建一个共用体变量// 存储一个整数data.i = 42;cout << "Integer: " << data.i << endl;// 存储一个浮点数,这时整数的数据会被覆盖data.f = 3.14;cout << "Float: " << data.f << endl;// 存储一个字符,这时浮点数的数据会被覆盖data.c = 'A';cout << "Character: " << data.c << endl;// 注意到这里输出的整数和浮点数已经被覆盖cout << "Integer after storing char: " << data.i << endl;cout << "Float after storing char: " << data.f << endl;return 0;
}
输出:
Integer: 42
Float: 3.14
Character: A
Integer after storing char: 0
Float after storing char: 0
解释:
- 在这个例子中,我们定义了一个名为
Data
的共用体,它有三个成员:int i
、float f
和char c
。这三个成员共享同一块内存。 - 当你存储一个整数(如 42)时,
data.i
会保存该值,其他成员f
和c
并不存储有效数据。 - 当你之后存储一个浮点数(如 3.14),
data.f
会保存该浮点数,原先存储在data.i
的整数值被覆盖。 - 当你再次存储一个字符(如
'A'
),data.c
会保存该字符,原先存储的浮点数(data.f
)也会被覆盖。
作用和优点:
- 节省内存: 因为所有的成员共享同一块内存,所以在同一时刻,只有一个数据有效。假设
int
占用 4 字节,float
占用 4 字节,char
占用 1 字节,那么共用体的大小是其最大成员的大小,这里是 4 字节。所以即使你需要存储多种类型的数据,实际占用的内存大小是最小化的。 - 适用于同一时刻只需要一个数据成员: 如果你知道在任何时候,只需要存储一种类型的数据,比如在处理传感器数据时,它们可能是温度(浮点数)、状态(整数)或者警报(字符),你可以使用共用体来节省内存。
- 灵活性: 共用体让你能够使用不同的数据类型而不需要额外的内存开销。例如,在嵌入式系统中,内存非常有限,这种设计可以节省资源。
总结:
共用体的作用就是让多个不同类型的成员共用同一块内存,从而节省内存空间。当你需要在不同的时间使用不同的数据类型时,共用体是一个非常有用的工具。但需要注意的是,在同一时刻,只能存储一个成员的数据,其他成员会被覆盖,因此需要小心使用。
dropEvent和paintEvent事件的学习
首先肯定时先拖动,通过拖动创建拖动到目标索引
当目标索引被拖到固定位置后,目标索引就会改变,我需要将其置空,然后重新赋值
现在有这么一个情况我的PanelConfigPLC由一下QTreeView组成//UI 视图QTreeView * m_PLCProgramsView;PLCOptionsView * m_PLCSubOptionsView;PLCProgramView * m_programTreeView;
其中PLCOptionsView组成结构如下class PLCOptionsView : public QTreeView
{Q_OBJECT
public:PLCOptionsView(QWidget * parent = nullptr);void setModel(QAbstractItemModel *model) override;void startDrag(Qt::DropActions supportedActions) override;private:QStandardItemModel * m_model;
};
PLCProgramView组成结构如下
// 自定义的 QMimeData 类
class MyMimeData : public QMimeData {
public:using QMimeData::QMimeData;void setOriginalIndex(const QModelIndex &index) {originalIndex_ = index;}QModelIndex getOriginalIndex() const {return originalIndex_;}private:QModelIndex originalIndex_;
};class PLCProgramView : public QTreeView
{Q_OBJECT
public:PLCProgramView(QWidget * parent = nullptr);public: //数据刷新与初始化void Init();void SetPLCOptionTreePointer( QStandardItemModel * );public: //数据读入 读出 接口void dataToTableView(PLC_Abstract* cmdProcess);void tableViewToData(PLC_Abstract* &cmdProcess);protected: //交互事件函数 拖拽操作、右键菜单(删除、前移、后移等)void mousePressEvent(QMouseEvent *event) override;void mouseDoubleClickEvent(QMouseEvent *event) override;//拖拽相关事件void dragEnterEvent(QDragEnterEvent *event) override;void dropEvent(QDropEvent *event) override;void startDrag(Qt::DropActions supportedActions) override;void dragMoveEvent(QDragMoveEvent *event) override;void paintEvent(QPaintEvent *event) override;QRect getRowVisualRect(const QModelIndex &index);QModelIndex m_hoveredIndex; // 当前拖拽的Indexbool m_isMyselfDrop = true; // 是否为本身的拖拽private: //PLC程序编辑交互函数void insertRow(PLC_Abstract* toAdd, QModelIndex index, bool flagBellow = false);void insertChild(PLC_Abstract* toAdd, QModelIndex index);void removeRow();static ProgSentanceItem* createTreeItemOnSent(PLC_Abstract* sent);static void DisplayProcessToItem(PLC_Abstract* process, QStandardItem* item);//待确认功能事件
protected:bool viewportEvent(QEvent *event) override;/// @note 拖入鼠标为禁止状态,要实现该函数void contextMenuEvent(QContextMenuEvent *event) override;public:private:QMenu *contextMenu;public:signals:void signal_SysnSelfDefProcessForDragItem(int insertRow, PLC_Abstract* idx);public slots:void slot_CustomContextMenu(const QPoint &pos);void slot_TableViewAdd();void slot_TableViewDel();void slot_TableViewClear();void slot_TableViewUp(); //行向上移动void slot_TableViewDown(); //行向下移动void slot_TableCommandClicked(QModelIndex index);void slot_ClickedActionGroup(QAction *action);void slot_ClickedIoActionGroup(QAction *action);private:protected:QString getIoDesc(uint Port,bool isIoIn); //CmdParam.inPort outPortvoid moveRow(int currentRow, int toRow);
private:QStandardItemModel * m_model;//StepObjectMenu * m_stepMenu;//ProcessId m_curProcessid;//StepIoObjectMenu * m_ioMenu;PLC_Function_SelfDef* currentEditProcess;private://QStandardItemModel * m_PLCOptionmodel;
};/// @brief 自绘指示器
class ControlViewStyle : public QProxyStyle
{
public:ControlViewStyle(QStyle *style = 0);void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = 0) const override;
};现在我的PLCOptionsView对应的模型m_PLC_avi_module的指针回传给PLCProgramView,让其显示m_PLCProgramsView某一item的信息,但是我让其拖动是在PLCProgramView进行和接受的,无法把m_PLCProgramsView中对应item信息给同步,我该怎么做
你的问题是:
当 PLCOptionsView
的 item
拖动到 PLCProgramView
时,如果是在最外层,可以直接告诉 PanelConfigPLC
插入的行号。但如果拖动到 PLCProgramView
的嵌套位置,该如何准确通知 PanelConfigPLC
进行正确的插入?
思路解析
-
最外层插入: 你已经有插入行号的逻辑,直接用
insertRow(row, item)
即可。 -
嵌套插入:
需要知道:
- 拖入的目标
item
是谁?(parentItem) - 拖入的目标
item
里的具体位置?(行号 row) - 让
PanelConfigPLC
处理插入逻辑。
- 拖入的目标
处理嵌套:
1、知道要插入到目标的parent Item是谁?
2、知道当前嵌套层要插入的位置;
3、让 PanelConfigPLC 处理插入逻辑
最外层的parent的row,以及插入位置的row,还有从parent到目标parent的完整路径,就是行号,各个层级的行号
插入的时候:首先定位到目标行,然后解析路径,一直到最后的,然后插入到指定位置
类的成员函数设置成static类型的好处
优点 | 说明 |
---|---|
无需创建对象 | 可直接通过类名调用,提高可用性 |
独立性高 | 不依赖对象状态,避免对象污染 |
适合工具类 | 适用于封装工具函数、数学运算、日志记录等 |
性能优化 | 没有 this 指针,调用效率更高 |
适用于单例模式 | 方便管理全局唯一实例 |
可作回调函数 | 可用于多线程、C 回调等场景 |
在实际开发中,如果一个成员函数不需要访问实例成员变量,也不会修改对象状态,就可以考虑声明为 static
,以提高代码的可维护性和效率。
1、而且有了static关键字后,这就表示在代码中只有一份
2、我在lambda表达式中,调用该类的其他成员函数,实际是同过this->func();的方式去调用的,但是static func()就不能用this指针去调用,因为静态类不依托于指针,通过类名::func()就可以进行调用
lambda表达式 调用成员函数的局限和认知,以及复习
在 C++ 中,lambda 表达式捕获外部作用域的变量时,默认情况下是不包含 this
指针的。因此,如果在一个类的非静态成员函数中使用 lambda 表达式,并且尝试在 lambda 表达式中直接调用类的其他成员函数或访问类的成员变量,你会遇到问题,因为 lambda 表达式没有默认捕获 this
。
以下是一个示例,说明为什么直接在 lambda 表达式中使用类的成员可能会遇到问题:
class MyClass {
public:void doSomething() {// 错误:不能直接在 lambda 中使用 this->memberFunction()auto lambda = []() {memberFunction(); // 这会引发编译错误};lambda();}void memberFunction() {// ...}
};
在上面的代码中,lambda
表达式尝试调用 memberFunction
,但是由于没有捕获 this
,这是不允许的。
要解决这个问题,你有几种选择:
- 显式捕获
this
指针:
void doSomething() {auto lambda = [this]() {memberFunction(); // 现在这是允许的,因为捕获了 this};lambda();
}
- 使用静态成员函数:
class MyClass {
public:void doSomething() {auto lambda = []() {staticMemberFunction(); // 静态成员函数可以直接调用};lambda();}static void staticMemberFunction() {// ...}
};
在静态成员函数中使用 lambda 表达式时,不需要捕获 this
,因为静态成员函数不依赖于任何特定的对象实例。
如何添加关于ui文件何以如何使用 Qt designer 设计师模式
Qt-> Qt Designer From[只是新建ui文件的]
Qt-> Qt 设计器界面类 [把所有的ui文件,以及对应的cpp文件和h文件都会创建好]
如何高效的拷贝数组呢
我想将bool chuckFlag[3] 赋值给 bool mChuckFlag[3] 改怎么做呢?
#include <cstring>std::memcpy(mChuckFlag, chuckFlag, sizeof(chuckFlag));