当前位置: 首页 > article >正文

【QT5 Widgets示例】Model/View编程初探

文章目录

  • Model/View
    • Model/View编程的优点
    • 常见Model类和View类
    • Model/View应用程序示例
      • 只读的表格
      • 修改文本外观
      • 显示变化的数据
      • 设置表格标头
      • 可编辑视图示例
      • 树结构视图示例
      • 获取视图选中项

Model/View

Model/View编程的优点

Model/View编程介绍:https://doc.qt.io/qt-5/model-view-programming.html

表格(Table)、列表(List)和树(Tree )形控件(widget)是图形用户界面(GUI)中经常使用的组件。这些控件有两种不同的方式来访问其数据,即直接访问和通过Model/View的模式访问。

直接访问
一般的控件自身包含了用于存储数据的容器,可以直接访问数据:

在这里插入图片描述
这样使用起来很直观,但是有以下几个问题:

(1)数据冗余与同步问题:每个控件都维护自己的数据容器,多个控件可能会存储相同的数据,导致冗余。如果数据需要在多个控件间同步,开发者需要手动维护,容易导致数据不一致。
(2)难以复用和扩展:由于数据存储和视图绑定在一起,无法轻松替换数据源或调整数据结构。
不能灵活适配不同的数据来源,例如数据库、文件、远程 API 等。
(3)耦合度高:视图和数据紧密耦合,控件既要管理 UI,又要管理数据,违反了单一职责原则。难以单独测试数据逻辑或 UI 逻辑,不利于单元测试和维护。
(4)性能问题:当数据量较大时,控件自身存储数据可能导致高内存占用。需要加载完整数据集,而不是按需加载,影响性能。

Model/View

  • Model-View将控件的数据与视图分离。控件不维护内部数据容器,它们通过标准化接口访问外部数据。在Model-View中,模型(Model)是数据的抽象表示,负责存储和管理数据。视图(View)是用于显示模型中的数据,并与用户进行交互的组件。视图通常会提供一个setModel()方法,允许开发者将视图与一个特定的模型实例相关联。一旦设置了模型,视图就可以通过该模型访问和显示数据。

相比于直接访问,Model/View有以下优点:
(1)数据和视图分离,低耦合:视图仅负责显示数据,数据逻辑由模型管理,符合MVC(Model-View-Controller)设计思想。修改数据源或替换模型不会影响视图,增强了代码的灵活性和可维护性。
(2)数据共享与同步更方便:多个视图可以共享同一个模型,避免数据冗余。数据变更时,模型可以通知所有关联视图(例如 Qt 的 QAbstractItemModel 提供 dataChanged() 信号),自动更新 UI。

(3)支持大规模数据处理:视图只会访问模型提供的接口,而模型可以实现懒加载(按需加载数据),提高性能。例如,视图可以根据需要请求数据,而不是一次性加载所有数据。

(4)提高代码复用性:相同的模型可以被不同的视图复用,例如列表视图、树状视图、表格视图等都可以使用同一个数据模型。适用于不同的 UI 组件,减少重复代码。

(5)更好的测试和维护性:由于视图和数据分离,可以单独测试数据模型,提高代码质量。UI 变更不会影响数据逻辑,降低了维护成本。

常见Model类和View类

QT5中常见的模型(Model)类包括:

  1. QAbstractItemModel:所有模型类的基类,定义了一些纯虚函数,需要子类来实现以提供自定义的数据存储和访问方式。
  2. QAbstractListModel:列表模型的抽象基类,适用于一维数据。
  3. QAbstractTableModel:表格模型的抽象基类,适用于二维数据。
  4. QStringListModel:用于处理字符串列表数据的数据模型类。
  5. QStandardItemModel:标准的基于数据项的数据模型类,每个数据项都可以是任何数据类型。
  6. QFileSystemModel:计算机上文件系统的数据模型类,提供了对本地文件系统的访问和操作。
  7. QSortFilterProxyModel:与其他数据模型结合,提供排序和过滤功能的数据类型模型类。
  8. QSqlTableModel:用于数据库的一个数据表的数据模型类。
  9. QSqlRelationalTableModel:用于关系类型数据表的数据模型。

QT5中常见的视图(View)类包括:

  1. QListView:用于显示单列的列表数据,适用于一维数据的操作。
  2. QTreeView:用于显示树状结构数据,适用于树状结构数据的操作。
  3. QTableView:用于显示表格状数据,适用于二维表格型数据的操作。
  4. QColumnView:用多个QListView显示树状层次结构,树状结构的一层用一个QListView显示。

注:一些标准控件类,如QListWidget、QTreeWidget和QTableWidget,它们是上述视图类的子类。

下面是部分的标准控件(item-based)和对应的Model/View 控件(Model-based)的效果示例:

列表List:QListWidget,QListView

在这里插入图片描述
表格:QTableWidget QTableView

树:QTreeWidget QTreeView

在这里插入图片描述

QColumnView:

在这里插入图片描述

特别地,QComboBox既可以作为标准的控件,也可以作为Model/View 控件:

对于那些操作单个值而不是数据集的控件(如QLineEdit、QCheckBox等),没有直接的Model/View对应项来分离数据和视图,因此我们需要一个适配器(Adapters )来将表单连接到数据源。例如,QDataWidgetMapper和QCompleter。

QT官网提供了各个类的使用示例:
https://doc.qt.io/qt-5/examples-itemviews.html

下面将以一些简单的例子展示Model/View用法。

Model/View应用程序示例

下面用七个简单的应用程序,展示模型/视图编程:

只读的表格

代码:[github]
我们从一个使用QTableView显示数据的应用程序开始。

// main.cpp
#include <QApplication>
#include <QTableView>   // 引入的视图类,用于以表格形式显示数据。
#include "mymodel.h"    // 引入自定义的模型类头文件,用于提供数据给视图。int main(int argc, char *argv[]) {QApplication a(argc, argv);// 创建QTableView对象tableViewQTableView tableView;// 创建自定义模型对象myModelMyModel myModel;// 通过调用tableView的setModel方法,将自定义模型myModel设置为tableView的数据源。// 这样,tableView就可以从myModel中获取数据并进行显示了。tableView.setModel(&myModel);// 调用tableView的show方法,显示表格视图。tableView.show();return a.exec();
}

tableView.setModel(&myModel); 将它的指针传递给 tableViewtableView 会调用它所接收到的指针的方法来了解两件事:

  • 应该显示多少行和列。
  • 每个单元格应该打印什么内容。

模型需要一些代码来响应这些请求。假设需要展示一个表格数据,让我们以 QAbstractTableModel作为基类来自定义模型:

// mymodel.h
#include <QAbstractTableModel>// 声明MyModel类,它继承自QAbstractTableModel
class MyModel : public QAbstractTableModel {Q_OBJECT public:explicit MyModel(QObject *parent = nullptr);// 重写rowCount函数,返回模型中的行数int rowCount(const QModelIndex &parent = QModelIndex()) const override;// 重写columnCount函数,返回模型中的列数int columnCount(const QModelIndex &parent = QModelIndex()) const override;// 重写data函数,用于提供模型中特定单元格的数据// index参数指定了单元格的位置(行和列),role参数指定了所需数据的类型(如显示文本、工具提示等)QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
};

模型的源文件:

// mymodel.cpp
#include "mymodel.h"MyModel::MyModel(QObject *parent): QAbstractTableModel(parent)
{
}// 实现rowCount函数,返回模型中的行数
int MyModel::rowCount(const QModelIndex & /*parent*/) const
{// 表格模型没有使用父/子结构,// 所以我们可以忽略parent参数(它通常用于树形或层次结构模型)// 在这个例子中,我们简单地返回2,表示模型有两行return 2;
}// 实现columnCount函数,返回模型中的列数
int MyModel::columnCount(const QModelIndex & /*parent*/) const
{// 与rowCount同样地,忽略parent参数// 简单地返回3,表示模型有三列return 3;
}// 实现data函数,用于提供模型中特定单元格的数据
QVariant MyModel::data(const QModelIndex &index, int role) const
{// 我们只对Qt::DisplayRole感兴趣,这是用于在视图中显示的数据if (role == Qt::DisplayRole) {// 使用QString的arg函数来格式化字符串,// QModelIndex是Qt中用于定位数据模型数据的一个类,// 调用其方法,将行号和列号(都加1以符合人类阅读习惯,从1开始计数)插入到字符串中return QString("Row%1, Column%2").arg(index.row() + 1).arg(index.column() + 1);}// 对于其他类型的role(如编辑、工具提示等),我们返回QVariant的默认值,// 表示没有提供这些数据return QVariant();
}

当视图需要知道单元格的文本内容时,它会调用 MyModel::data() 方法。行和列的信息通过参数 index 指定,并且role被设置为 Qt::DisplayRole

在这个例子中,需要显示的数据是指定的。在实际应用中,MyModel 一般会有一个名为 MyData 的成员,它作为所有读写操作的目标。

修改文本外观

代码:[github]
除了控制视图显示的文本外,模型还可以控制文本的外观。只需要添加更多的role条件,就可以以下结果:
在这里插入图片描述

每个格式化属性都将通过单独调用data()方法从模型中请求。role参数用于让模型知道正在请求哪个属性:

// mymodel.cpp
QVariant MyModel::data(const QModelIndex &index, int role) const {int row = index.row();    // 从QModelIndex对象中获取当前行的索引int col = index.column(); // 从QModelIndex对象中获取当前列的索引switch (role) {case Qt::DisplayRole: // 处理显示角色的数据// 为特定单元格设置自定义显示文本if (row == 0 && col == 1) return QString("<--left");if (row == 1 && col == 1) return QString("right-->");// 为其他单元格返回默认的行列信息文本return QString("Row%1, Column%2").arg(row + 1).arg(col + 1);case Qt::FontRole: // 处理字体角色的数据// 仅为单元格(0,0)设置粗体字体if (row == 0 && col == 0) {QFont boldFont;boldFont.setBold(true);return boldFont;}break;case Qt::BackgroundRole: // 处理背景角色的数据// 仅为单元格(1,2)设置红色背景if (row == 1 && col == 2)return QBrush(Qt::red);break;case Qt::TextAlignmentRole: // 处理文本对齐角色的数据// 仅为单元格(1,1)设置文本对齐方式(右对齐和垂直居中)if (row == 1 && col == 1)return int(Qt::AlignRight | Qt::AlignVCenter);break;case Qt::CheckStateRole: // 处理复选框状态角色的数据// 在单元格(1,0)中添加一个已选中的复选框if (row == 1 && col == 0)return Qt::Checked;break;}return QVariant();
}

显示变化的数据

代码:[github]
以上的例子展示了模型的被动性质。模型不知道它何时会被使用,也不知道需要哪些数据。它只是在视图每次请求数据时提供数据。那么,当模型的数据需要改变时会发生什么呢?视图如何意识到数据已经改变并需要重新读取呢?

简而言之,当模型的数据发生变化时,它必须通知视图。这通常是通过发出一个信号来完成的,该信号携带了关于哪些数据已经改变的信息。视图接收到这个信号后,就会知道它需要重新从模型中读取数据来更新其显示。

在一个表格的第一行第一列展示当前时间:

// mymodel.cpp
QVariant MyModel::data(const QModelIndex &index, int role) const
{int row = index.row();int col = index.column();if (role == Qt::DisplayRole && row == 0 && col == 0)return QTime::currentTime().toString();return QVariant();
}

下面使用定时器和信号槽机制来实现表格中时间的更新,每隔1000ms,更新一次。首先在头文件中声明定时器和槽函数。

// mymodel.h
class MyModel : public QAbstractTableModel
{Q_OBJECT
public:explicit MyModel(QObject *parent = nullptr);int rowCount(const QModelIndex &parent = QModelIndex()) const override;int columnCount(const QModelIndex &parent = QModelIndex()) const override;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;private slots: // 每隔1000ms,模型发出一个信号,告诉视图哪些单元格的数据已经改变。void timerHit();private:   QTimer *timer; // 计时器
};

在构造函数中,我们将定时器的间隔设置为1秒,并连接其超时信号到槽函数timerHit()

// mymodel.cpp
MyModel::MyModel(QObject *parent): QAbstractTableModel(parent), timer(new QTimer(this))
{// 超时信号,时间间隔设置为1000ms(1s)timer->setInterval(1000);connect(timer, &QTimer::timeout , this, &MyModel::timerHit);timer->start();
}

槽函数接收到超时信号后,进一步地发出dataChanged()信号来请求视图再次读取左上角单元格对应的数据:

// mymodel.cpp
void MyModel::timerHit()
{QModelIndex topLeft = createIndex(0,0);//发出信号,使视图重新读取topLeft中已识别的数据emit dataChanged(topLeft, topLeft, {Qt::DisplayRole});
}

我们通过值得注意的是,我们并没有明确地将dataChanged()信号与视图相连接。当我们调用setModel()方法时,这一连接是自动完成的。

设置表格标头

代码:[github]

表格的标头可以通过视图方法隐藏:

// main.cpp
#include <QApplication>
#include <QTableView>
#include "mymodel.h"
#include <QHeaderView>int main(int argc, char *argv[])
{QApplication a(argc, argv);QTableView tableView;MyModel myModel;tableView.setModel(&myModel);tableView.horizontalHeader()->hide(); // 隐藏水平标头tableView.verticalHeader()->hide();   // 隐藏垂直标头tableView.show();return a.exec();
}

另一方面,标头内容可以通过重写headerData()方法实现修改:

//mymodel.h
#include <QAbstractTableModel>class MyModel : public QAbstractTableModel
{Q_OBJECT
public:explicit MyModel(QObject *parent = nullptr);int rowCount(const QModelIndex &parent = QModelIndex()) const override;int columnCount(const QModelIndex &parent = QModelIndex()) const override;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;QVariant headerData(int section, Qt::Orientation orientation, int role) const override; // 重写headerData()方法
};
//mymodel.cpp
QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role) const
{if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {switch (section) {case 0:return QString("first");case 1:return QString("second");case 2:return QString("third");}}else if (role == Qt::DisplayRole && orientation == Qt::Vertical) {switch (section) {case 0:return QString("first");case 1:return QString("second");}}return QVariant();
}

在这里插入图片描述

可编辑视图示例

代码:[github]
在这个例子中,我们将构建一个应用程序,该程序将获取用户输入到表格单元格中的值,并自动填充到窗口标题。

为了方便地访问窗口标题,我们将QTableView放置在QMainWindow中。

// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), tableView(new QTableView(this))
{setCentralWidget(tableView);auto *myModel = new MyModel(this);tableView->setModel(myModel);// 将模型所作的更改显示到窗口标题connect(myModel, &MyModel::editCompleted,this, &QWidget::setWindowTitle);
}

模型决定了是否提供编辑功能。我们只需要修改模型,重写虚函数setData()flags()即可启用编辑功能。

// mymodel.h
#include <QAbstractTableModel>
#include <QString>const int COLS= 3;
const int ROWS= 2;class MyModel : public QAbstractTableModel
{Q_OBJECT
public:MyModel(QObject *parent = nullptr);int rowCount(const QModelIndex &parent = QModelIndex()) const override;int columnCount(const QModelIndex &parent = QModelIndex()) const override;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;Qt::ItemFlags flags(const QModelIndex &index) const override;
private:QString m_gridData[ROWS][COLS];  // 保存输入QTableView的文本
signals:void editCompleted(const QString &); // 编辑完成的信号
};

我们使用二维数组QString m_gridData来存储数据。这使得m_gridData成为MyModel的核心。MyModel的其余部分则像一个包装器,将m_gridData适配到QAbstractItemModel接口

// mymodel.cpp
bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{if (role == Qt::EditRole) {if (!checkIndex(index))return false;//将值从编辑器保存到成员m_gridDatam_gridData[index.row()][index.column()] = value.toString();// 将各个单元格的值凭拼接成字符串,通过editCompleted信号发送给标题QString result;for (int row = 0; row < ROWS; row++) {for (int col= 0; col < COLS; col++)result += m_gridData[row][col] + ' ';}emit editCompleted(result);return true;}return false;
}

每次用户编辑一个单元格时,都会调用setData()方法。index参数告诉我们哪个字段被编辑了,而value参数提供了编辑过程的结果。
由于我们的单元格只包含文本,因此role将始终被设置为Qt::EditRole。如果存在一个复选框,并且用户权限被设置为允许选择该复选框,那么当role被设置为Qt::CheckStateRole时,也会进行调用。

// mymodel.cpp
Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
{return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
}

通过flags()方法可以调整单元格的各种属性。

  • Qt::ItemIsSelectable:表示单元格是可以被选中的。这是模型/视图框架中的默认行为之一,通常不需要显式指定,因为QAbstractTableModel::flags(index)默认会包含这个标志
  • Qt::ItemIsEditable:表示单元格是可以被编辑的。
  • Qt::ItemIsEnabled:表示单元格是启用的,即它是可交互的。这同样是模型/视图框架中的默认行为之一,通常不需要显式指定。

树结构视图示例

代码:[github]
使用模型/视图的典型方法是包装特定数据,使其可用于视图类。然而,Qt也为常见的底层数据结构提供了预定义的模型。例如,下面我们将通过预定义的模型QStandardItemModel,用树形结构展示数据:

QStandardItemModel它是一个用于存储层次结构数据的容器,必须被填充QStandardItems

// mainwindow.h
#include <QMainWindow>// Qt 的命名空间开始,这里使用了前向声明来减少头文件的依赖
QT_BEGIN_NAMESPACE
class QTreeView;       // QTreeView 类的前向声明,用于在 MainWindow 中作为成员变量
class QStandardItemModel; // QStandardItemModel 类的前向声明,用于存储树形视图的数据
class QStandardItem;    // QStandardItem 类的前向声明,通常用于构建 QStandardItemModel 的内容
QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);private:// 辅助函数,用于准备一行数据,并返回一个包含三个 QStandardItem* 的列表// 这些 QStandardItem 分别对应于行中的第一、第二和第三个数据项QList<QStandardItem*> prepareRow(const QString &first,const QString &second,const QString &third) const;// 成员变量QTreeView *treeView;          // 指向 QTreeView 的指针,用于显示树形结构的数据QStandardItemModel *standardModel; // 指向 QStandardItemModel 的指针,作为树形视图的数据源
};;
// mainwindow.cpp
#include "mainwindow.h"#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItem>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), treeView(new QTreeView(this)) // 创建 QTreeView 对象,并将其父对象设置为当前 MainWindow, standardModel(new QStandardItemModel(this)) // 创建 QStandardItemModel 对象,并将其父对象设置为当前 MainWindow
{// 将 treeView 设置为 MainWindow 的中心部件setCentralWidget(treeView);// 准备一行数据QList<QStandardItem *> preparedRow = prepareRow("first", "second", "third");// 获取模型的不可见根项(即模型的顶层项,它本身不显示)QStandardItem *item = standardModel->invisibleRootItem();// 向不可见根项添加一行数据,这将作为模型的根元素显示item->appendRow(preparedRow);// 准备第二行数据QList<QStandardItem *> secondRow = prepareRow("111", "222", "333");// 将第二行数据添加到第一行数据的第一个单元格对应的项下,这将创建一个子树preparedRow[0]->appendRow(secondRow);// 为 treeView 设置模型treeView->setModel(standardModel);// 展开 treeView 中的所有项treeView->expandAll();
}// prepareRow 成员函数,用于准备一行数据
QList<QStandardItem *> MainWindow::prepareRow(const QString &first,const QString &second,const QString &third) const
{// 创建一个包含三个 QStandardItem 对象的列表,并将它们分别初始化为 first, second, third 字符串return {new QStandardItem(first),new QStandardItem(second),new QStandardItem(third)};
}

在这里插入图片描述

获取视图选中项

代码:[github]
下面提供的代码段旨在实现以下功能:将用户在树状视图中选定的每一项,连同其在树状结构中的层级信息(Level),共同展示在应用程序的窗口标题中。

在这里插入图片描述

// mainwindow.h
#include <QMainWindow>QT_BEGIN_NAMESPACE
// 前向声明 QTreeView 类, 这对于减少编译时间和避免循环依赖很有用
class QTreeView;
class QStandardItemModel;
class QItemSelection;QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);private slots:// 一个私有槽函数,它将在 QTreeView 的选择发生变化时被调用void selectionChangedSlot(const QItemSelection &newSelection, const QItemSelection &oldSelection);private:QTreeView *treeView;  // 树视图QStandardItemModel *standardModel;   // 为 treeView 提供数据模型
};
#include "mainwindow.h"#include <QTreeView>
#include <QStandardItemModel>
#include <QItemSelectionModel>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), treeView(new QTreeView(this)), standardModel(new QStandardItemModel(this))
{setCentralWidget(treeView);auto *rootNode = standardModel->invisibleRootItem();// 让我们创建几个`QStandardItem`:auto *americaItem = new QStandardItem("America");auto *mexicoItem =  new QStandardItem("Canada");auto *usaItem =     new QStandardItem("USA");auto *bostonItem =  new QStandardItem("Boston");auto *europeItem =  new QStandardItem("Europe");auto *italyItem =   new QStandardItem("Italy");auto *romeItem =    new QStandardItem("Rome");auto *veronaItem =  new QStandardItem("Verona");// 建立层次结构rootNode->    appendRow(americaItem);rootNode->    appendRow(europeItem);americaItem-> appendRow(mexicoItem);americaItem-> appendRow(usaItem);usaItem->     appendRow(bostonItem);europeItem->  appendRow(italyItem);italyItem->   appendRow(romeItem);italyItem->   appendRow(veronaItem);// 将模型设置给 treeViewtreeView->setModel(standardModel);treeView->expandAll();  // 展开树视图中的所有项// 连接选择模型的 selectionChanged 信号到 MainWindow 的 selectionChangedSlot 槽函数QItemSelectionModel *selectionModel = treeView->selectionModel();connect(selectionModel, &QItemSelectionModel::selectionChanged,this, &MainWindow::selectionChangedSlot);
}
// mainwindow.cpp
void MainWindow::selectionChangedSlot(const QItemSelection & /*newSelection*/, const QItemSelection & /*oldSelection*/)
{// 不使用 newSelection 和 oldSelection 参数,因此用注释标记为未使用// 获取当前选中的项的 QModelIndexconst QModelIndex index = treeView->selectionModel()->currentIndex();// 从 QModelIndex 中获取显示数据(通常是文本),并将其转换为 QStringQString selectedText = index.data(Qt::DisplayRole).toString();// 初始化层级级别为 1,因为顶层项(直接挂在根节点下的项)的层级为 1int hierarchyLevel = 1;// 从当前选中项的 QModelIndex 开始,向上遍历其父节点,计算层级级别QModelIndex seekRoot = index;while (seekRoot.parent().isValid()) // 当父节点存在时继续循环{seekRoot = seekRoot.parent(); // 移动到父节点hierarchyLevel++; // 每向上移动一级,层级级别加 1}// 构建一个包含选中项文本和层级级别的字符串QString showString = QString("%1, Level %2").arg(selectedText).arg(hierarchyLevel);// 将构建的字符串设置为 MainWindow 的窗口标题setWindowTitle(showString);
}

参考:
https://doc.qt.io/qt-5/modelview.html#2-a-simple-model-view-application

相关文章:

【QT5 Widgets示例】Model/View编程初探

文章目录 Model/ViewModel/View编程的优点常见Model类和View类Model/View应用程序示例只读的表格修改文本外观显示变化的数据设置表格标头可编辑视图示例树结构视图示例获取视图选中项 Model/View Model/View编程的优点 Model/View编程介绍&#xff1a;https://doc.qt.io/qt-…...

一键优化右键菜单,高效又清爽!

打工人们你们好&#xff01;这里是摸鱼 特供版~ 电脑右键菜单杂乱无章&#xff0c;常用功能被淹没&#xff1f;图标显示异常、打印出错让人手足无措&#xff1f;别担心&#xff0c;Easy Context Menu来帮你&#xff01;这是一款右键菜单管理工具&#xff0c;能快速清理不必要的…...

排查数据库的正在执行的占用的慢sql语句,可以查看生产的执行sql语句时间

1. 生产报错异常&#xff1a; org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: master - Interrupted during connection acquisition2.在数据库执行查询生…...

Java泛型程序设计使用方法

Java泛型程序设计是Java语言中一项强大的特性&#xff0c;它允许你编写更加通用和类型安全的代码。以下是Java泛型程序设计的使用方法和技巧&#xff1a; 1. 基本概念 泛型类&#xff1a;可以定义一个类&#xff0c;其中的某些类型是参数化的。 public class Box<T> {pr…...

成绩排序(结构体排序)

成绩排序 #include<stdio.h> #include<stdlib.h> struct student{char name[50];int score;int order; }; int flag; int cmp(const void *a,const void *b){struct student *s1(struct student*)a;struct student *s2(struct student*)b;if(s1->scores2->…...

【redis】lua脚本

在分布式系统与高并发架构的战场中&#xff0c;开发者们始终在与两个永恒的命题博弈&#xff1a;数据一致性与系统性能。当我们试图用Redis构建高速缓存、实现分布式锁或设计秒杀系统时&#xff0c;往往会陷入这样的困境——如何在保证原子性的同时&#xff0c;避免网络往返带来…...

Oracle中的INHERIT PRIVILEGES权限

Oracle中的INHERIT PRIVILEGES权限 存储过程和用户函数的AUTHID属性调用者权限vs定义者权限一个简单的示例INHERIT PRIVILEGES权限的含义INHERIT PRIVILEGES权限的安全隐患注意到Oracle 19c数据库中有如下权限信息: SQL> select grantor,grantee,table_name,privilege fro…...

Kafka相关的面试题

以下是150道Kafka相关的面试题及简洁回答&#xff1a; Kafka基础概念 1. 什么是Kafka&#xff1f; Kafka是一个分布式、可扩展、容错的发布-订阅消息系统&#xff0c;最初由LinkedIn开发&#xff0c;现为Apache项目。它适用于高吞吐量的场景&#xff0c;如大数据处理和实时数据…...

OpenHarmony-XTS测试

OpenHarmony-XTS测试 OpenHarmony-XTS测试环境搭建测试准备开始运行PS OpenHarmony-XTS测试 针对OpenHarmony版本进行XTS测试使用记录。 windows环境。 以acts套件为例。 环境搭建 获取测试套件&#xff0c;两种方法 1&#xff09;官网下载&#xff1a;https://www.openharm…...

【物联网-WIFI】

物联网-WIFI ■ ESP32-C3-模块简介■ ESP32-C3-■ ESP32-C3-■ WIFI-模组■ WIFI-■ WIFI- ■ ESP32-C3-模块简介 ■ ESP32-C3- ■ ESP32-C3- ■ WIFI-模组 ■ WIFI- ■ WIFI-...

作业9 (2023-05-05 数组的定义和初始化)

第1题/共11题【单选题】 关于一维数组初始化,下面哪个定义是错误的?( ) A.int arr[10] = {1,2,3,4,5,6}; B.int arr[] = {1,2,3,4,5,6}; C.int arr[] = (1,2,3,4,5,6); D.int arr[10] = {0}; A:正确,10个int的一段连续空间,前6个位置被初始化为1,2,3,4,5,6,其他…...

C语言中的流程控制语句

一.流程控制语句的分类&#xff1a; 1.顺序结构 概念&#xff1a;从上往下依次执行&#xff0c;也是程序默认的执行顺序 2.分支结构 概念&#xff1a;程序在执行的过程中出现了岔路&#xff08;我们只能选择一条支线进行执行&#xff09; &#xff08;1&#xff09;.if语句…...

linux常用基本指令汇总

文章目录 01. ls指令02. pwd指令03. cd指令04. touch指令05. mkdir指令06. rmdir指令07. rm指令08. man指令09. cp指令10. mv指令11. cat指令11. more指令12. less指令13. head指令14. tail指令15. time指令16. cal指令17. find指令18. grep指令19. zip/unzip指令20.tar指令21.…...

Python 与 JavaScript 交互及 Web 逆向分析全解析

一、引言 在当今数字化时代,软件开发的复杂性和多样性不断增加,不同编程语言之间的交互与协作变得愈发重要。Python 凭借其简洁易读的语法、丰富的库和强大的数据处理能力,在数据科学、自动化脚本编写等领域占据着重要地位。而 JavaScript 作为前端开发的核心语言,以其在网…...

Docker Desktop 安装与使用详解

目录 1. 前言2. Docker Desktop 安装2.1 下载及安装2.2 登录 Docker 账号2.3 进入 Docker Desktop 主界面 3. Docker 版本查看与环境检查3.1 查看 Docker Desktop 支持的 Docker 和 Kubernetes 版本3.2 检查 Docker 版本 4. Docker Hub 和常用镜像管理方式4.1 使用 Docker Hub4…...

鬼泣:移动系统3

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 run/Sprint混合空间输入&#xff1a;(leanAngle, maxWalkSpeed) 按布尔混合和选择的区别&#xff1a;自动生成过渡动画dead blending&#xff1a;惯性化节点疑问run/Sprint混合空间为什么速度为0时也设置奔跑动画为…...

解决 HTTP 请求中的编码问题:从乱码到正确传输

文章目录 解决 HTTP 请求中的编码问题&#xff1a;从乱码到正确传输1. **问题背景**2. **乱码问题的原因**2.1 **客户端编码问题**2.2 **请求头缺失**2.3 **服务器编码问题** 3. **解决方案**3.1 **明确指定请求体编码**3.2 **确保请求头正确**3.3 **动态获取响应编码** 4. **调…...

golang从入门到做牛马:第二十篇-Go语言接口:行为的“契约”

Go语言接口:行为的“契约” 在Go语言中,接口(interface)是一种非常强大的类型,用于定义行为的集合。接口通过描述类型必须实现的方法,规定了类型的行为契约。Go语言的接口设计简单而功能强大,是实现多态和解耦的重要工具。接下来,让我们一起深入了解Go语言中的接口。 …...

HTML5 Web SQL

HTML5 Web SQL 引言 随着互联网技术的飞速发展,HTML5 作为新一代的网页技术,已经逐渐成为网页开发的主流。在 HTML5 中,Web SQL 是一种轻量级的数据库存储技术,它允许开发者直接在网页中存储数据。本文将详细介绍 HTML5 Web SQL 的概念、特点、应用场景以及使用方法。 一…...

【品铂科技工业生产应用案例解析】

品铂科技&#xff08;Pinpoint&#xff09;在工业领域的高精度定位解决方案已广泛应用于电力、钢铁、仓储、化工、地铁等场景&#xff0c;以下为典型应用案例及技术方案&#xff1a; 一、‌电力行业&#xff1a;上海闵行电厂人员定位‌ 白鹤滩水力发电站 ‌项目需求‌&#x…...

pjsip dtmf发送和接收(pjsua)

DTMF(双音多频,Dual-Tone Multi-Frequency)是一种用于电话系统的信号技术,通过组合两个不同频率的音频信号来表示数字和符号。以下是DTMF的主要使用背景和应用场景: 电话拨号 DTMF最常见的用途是电话拨号。当用户按下电话键盘上的数字或符号时,电话会生成两个特定频率的音…...

【HarmonyOS Next】鸿蒙应用常规面试题和答辩思路参考

【HarmonyOS Next】鸿蒙应用常规面试题和答辩思路参考 一、充分了解岗位JD要求 根据招聘发布的岗位JD&#xff0c;进行自我匹配分析。了解基本要求和加分项&#xff0c;以及项目节奏和英文要求等。 技术不匹配的点&#xff0c;是否会影响应聘岗位加分项自己是否掌握&#xf…...

《计算机图形学》第二课笔记-----二维变换的推导

前言&#xff1a;为什么这么突兀的把这一节内容放在了第二课&#xff0c;第一是因为我急于求成&#xff0c;第二是因为这一章节太重要了&#xff0c;这几乎是二维三维变换的最核心的东西&#xff0c;理解了这一章节内容&#xff0c;后面的就会像打通了任督二脉一样&#xff0c;…...

无需微调的对齐方法URIAL

无需微调的对齐方法URIAL 研究背景与目的:LLMs的对齐调优通常采用监督微调(SFT)和强化学习从人类反馈(RLHF),但LIMA研究表明少量示例的SFT也能实现较好对齐,暗示对齐调优可能存在“表面性质”。本研究旨在探究对齐调优对基础LLMs的具体影响,并提出不依赖SFT或RLHF的对齐…...

机器学习(七)

一&#xff0c;监督学习和无监督学习聚类的数据集比较&#xff1a; 监督学习&#xff1a; 数据集包括输入的数据和与之对应的标签 无监督学习&#xff1a; 数据集仅含有输入的数据&#xff0c;要求算法自己通过所给的数据集来确定决策边界 二&#xff0c;聚类(Clustering): 聚…...

利用labelimg实现yolov8数据集的制作

我们在使用yolov8进行物体检测识别的时候&#xff0c;由于其内置的n,s,m等模型只包含90多种物体&#xff08;很多其他物品并未包含在其中&#xff09;&#xff0c;导致我们无法直接使用其模型进行视频或者图片的检测识别。这个时候&#xff0c;我们就需要自己制作数据集进行训练…...

【0x80070666】-已安装另一个版本...(Tableau 安装失败)

第一种是之前安装过tableau相关软件&#xff0c;但是没卸载干净。 方法1&#xff1a;卸载旧版本 打开 控制面板 → 程序和功能&#xff08;或 添加/删除程序&#xff09;。查找 Tableau Desktop&#xff0c;如果已安装旧版本&#xff0c;卸载它。重新启动电脑后再尝试安装。 …...

在rv1106上部署vue3

创建vue3项目 $ npm create vuelatest Need to install the following packages: create-vue3.15.1 Ok to proceed? (y) y > npx > create-vue┌ Vue.js - The Progressive JavaScript Framework │ ◇ 请输入项目名称&#xff1a; │ ip_cam │ ◇ 请选择要包含的…...

海量数据查询加速:Presto、Trino、Apache Arrow

1. 引言 在大数据分析场景下,查询速度往往是影响业务决策效率的关键因素。随着数据量的增长,传统的行存储数据库难以满足低延迟的查询需求,因此,基于列式存储、向量化计算等技术的查询引擎应运而生。本篇文章将深入探讨 Presto、Trino、Apache Arrow 三种主流的查询优化工…...

Word填写窗口功能详解:如何让文档填写更高效?

在日常办公中&#xff0c;我们经常需要让他人填写一些固定格式的文档&#xff0c;比如合同、申请表、调查问卷等。如果直接使用普通文本编辑&#xff0c;填写时可能会破坏排版&#xff0c;甚至修改了不该改动的内容。这时候&#xff0c;Word的填写窗口&#xff08;即“内容控件…...