Qt扫盲-Model/View入门
Model/View 编程入门
- 一、概述
- 二、介绍
- 1. 标准部件
- 2. Model/View 控件
- 3. Model/View控件概述
- 4. 在表格单和 model 之间使用适配器 Adapters
- 三、 简单的 model / view 应用程序示例
- 1. 一个只读表
- 2. 使用role扩展只读示例
- 3. 表格单元中的时钟
- 4. 为列和行设置标题
- 5. 最小编辑示例
- 四、中间的话题
- 1. TreeView
- 2. 使用选择器
- 3. 预定义模型
- 4. Delegates
一、概述
每个UI开发人员都应该了解ModelView编程,
表格格控件、列表格控件和树控件是gui中经常使用的组件。这些控件有两种不同的方式访问它们的数据。
- 1.传统方法
传统的方法就是让控件本身去储存数据,在控件内部有数据容器,这种方法非常直观,但是,在许多重要的应用程序中,它会导致数据同步问题。 - 2.Model/View 方法
Model/View 也叫 模型/ 视图 编程,其中控件不维护内部数据容器。它们通过标准化接口访问外部数据,从而避免了数据重复。乍一看,这似乎很复杂,但一旦仔细研究,就会发现它不仅易于掌握,而且Model/View编程的许多好处也变得更加清晰。
在这个过程中,我们将学习Qt提供的一些基本技术,比如:
- 标准部件和Model/View部件之间的区别
- 表格单和 model 之间的适配器
- 开发简单的Model/View应用程序
- 预定义的 model
- 中间主题,如:
树 view
选择
代理 - model 试验调试
我们还会了解使用Model/View编程是否可以更容易地编写新应用程序,或者传统的控件是否也可以正常工作。
二、介绍
Model/View是一种用于在处理数据集的控件中分离数据和 view 的技术。标准的控件不是为从 view 中分离数据而设计的,这就是Qt有两种不同类型的控件的原因。虽然这两种类型的控件看起来是一样的,但是它们与数据的交互方式不同。
标准控件使用的数据是控件的一部分。数据和界面是融在一起的
view 类对外部数据( 模型 )进行操作,就是通过接口去分类界面和数据,model 存储数据,而view 则是 视图 类,显示UI。
1. 标准部件
让我们仔细看看一个标准的表格控件。表格控件是用户可以更改的数据元素的2D数组。通过读写表格控件提供的数据元素,可以将表格控件集成到程序流中。这种方法在许多应用程序中非常直观和有用,但是使用标准表格控件显示和编辑数据库表格可能会有问题。
什么问题呢?
那就是我们必须协调数据的两个副本:一个在控件外部;一个在控件内。开发人员负责同步这两个版本。手动同步容易出风险。
除此之外,表格示和数据的紧密耦合使得编写单元测试变得更加困难。不方便测试呐!
2. Model/View 控件
Model/View逐步提供了一个使用更通用架构的解决方案。 Model/View消除了标准控件可能出现的数据一致性问题。
Model/View还使使用相同数据的多个 view 变得更容易,因为一个 model 可以传递给多个 view 。
最重要的区别是Model/View控件不将数据存储在表格格单元格后面。事实上,它们直接根据您的数据进行操作。由于 view 类不知道数据的结构,因此需要提供一个包装器,使数据符合QAbstractItemModel接口。 view 使用该接口读取和写入数据。
实现QAbstractItemModel的类的任何实例都被称为 model 。一旦 view 接收到一个 model 的指针,它将读取并显示其内容,并成为其编辑器。
3. Model/View控件概述
下面是Model/View控件及其相应的标准控件的概述。
控件外观 | 标准部件(基于Item的便利类) | Model/View view 类(用于外部数据) |
---|---|---|
![]() | QListWidget | QListView |
![]() | QTableWidget | QTableView |
![]() | QTreeWidget | QTreeView |
![]() | 很少用 | QColumnView将树显示为列表格的层次结构 |
![]() | QComboBox既可以作为 view 类,也可以作为传统的控件 |
4. 在表格单和 model 之间使用适配器 Adapters
在表格单和 model 之间使用适配器可以派上用场。
我们可以直接从表格本身编辑存储在表格中的数据,但是在文本字段中编辑数据要舒服得多。对于操作一个值(QLineEdit, QCheckBox…)而不是数据集的控件,没有直接的Model/View对应物来分离数据和 view ,因此我们需要一个适配器来将表格单连接到数据源。
QDataWidgetMapper是一个很好的解决方案,因为它将表格单控件映射到表格行,并使为数据库表格构建表格单变得非常容易。这个就相当于我们把修改的数据用QDataWidgetMapper 通过 model/view的方式给同步到表格里面去了。
适配器的另一个例子是QCompleter。Qt有QCompleter用于在Qt控件中提供自动补全功能,如QComboBox和QLineEdit,如下所示。QCompleter使用一个 model 作为它的数据源。
就像下面的简单代码就可以做到这个效果
QStringList wordList;
wordList << "alpha" << "omega" << "omicron" << "zeta"<<"Emanuel"<<"Emerson"<<"Emilio"<<"Emma"<<"Emmily";
QLineEdit *lineEdit = new QLineEdit(this);QCompleter *completer = new QCompleter(wordList, this);
completer->setCaseSensitivity(Qt::CaseInsensitive);
lineEdit->setCompleter(completer);
三、 简单的 model / view 应用程序示例
如果你想开发一个 model / view 应用程序,应该从哪里开始?Qt官方建议从一个简单的示例开始,并逐步扩展它。这使得理解架构变得容易得多。对许多开发人员来说,在调用IDE之前尝试详细理解 model/view 架构是困难的。但是从简单 model/view 应用程序实践来看要更容易。
下面是7个非常简单和独立的应用程序,展示了 model/view 编程的不同方面
1. 一个只读表
我们从一个使用QTableView显示数据的应用程序开始。稍后我们将添加编辑功能。
// main.cpp
#include < QApplication >
#include < QTableView >
#include“mymodel.h”int main(Int argc, char *argv[])
{QApplication a(argc, argv);QTableView tableView;MyModel MyModel;tableView.setModel (&myModel);tableView.show ();return a.exec ();
}
我们有常用的main()函数:
我们创建一个MyModel的实例并使用tableView.setModel(&myModel);
传递它的指针给tableView。
tableView会调用它接收到的指针的方法来找出两件事:
- 应该显示多少行和多少列。
- 每个单元格应该打印什么内容。
model 需要一些代码来响应此请求。我们有一个表数据集,所以让我们从QAbstractTableModel开始,因为它比更通用的qabstractemmodel更容易使用。
// mymodel.h
#include <QAbstractTableModel>class MyModel : public QAbstractTableModel
{Q_OBJECTpublic: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;
};
qabstractablemodel需要实现三个抽象方法。
// mymodel.cpp#include "mymodel.h"MyModel::MyModel(QObject *parent): QAbstractTableModel(parent){}int MyModel::rowCount(const QModelIndex & /*parent*/) const{return 2;}int MyModel::columnCount(const QModelIndex & /*parent*/) const{return 3;}QVariant MyModel::data(const QModelIndex &index, int role) const{if (role == Qt::DisplayRole)return QString("Row%1, Column%2").arg(index.row() + 1).arg(index.column() +1);return QVariant();}
行数和列数由 MyModel::rowCount() 和 MyModel::columnCount() 提供。当 view 需要知道单元格的文本是什么时,它会调用方法MyModel::data()。行和列信息通过index参数指定,role 设置为Qt::DisplayRole。下一节将介绍其他role。
在我们的例子中,应该显示的数据已经生成。在实际的应用程序中,MyModel会有一个名为MyData的成员,它充当所有读写操作的目标。
这个小示例演示了 model 的被动性。** model 不知道何时会使用它或需要哪些数据。它只是在每次 view 请求时提供数据。**
当 model 的数据需要更改时会发生什么? view 如何意识到数据已经更改,需要再次读取? model 必须发出一个信号,表明哪些范围的单元格发生了变化。这将在 3 节中进行演示。
2. 使用role扩展只读示例
除了控制 view 显示的文本, model 还控制文本的外观。对 model 稍作修改后,得到如下结果:
实际上,除了data()方法之外,我们不需要修改任何内容,以设置字体、背景颜色、对齐方式和复选框。下面是产生上面显示结果的data()方法。不同的是,这次我们使用形参int role根据其值返回不同的信息。
// mymodel.cppQVariant MyModel::data(const QModelIndex &index, int role) const{int row = index.row();int col = index.column();// generate a log message when this method gets calledqDebug() << QString("row %1, col%2, role %3").arg(row).arg(col).arg(role);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:if (row == 0 && col == 0) { //change font only for cell(0,0)QFont boldFont;boldFont.setBold(true);return boldFont;}break;case Qt::BackgroundRole:if (row == 1 && col == 2) //change background only for cell(1,2)return QBrush(Qt::red);break;case Qt::TextAlignmentRole:if (row == 1 && col == 1) //change text alignment only for cell(1,1)return Qt::AlignRight + Qt::AlignVCenter;break;case Qt::CheckStateRole:if (row == 1 && col == 0) //add a checkbox to cell(1,0)return Qt::Checked;break;}return QVariant();}
每个格式化属性都是通过单独调用data()方法从模型中请求的。role参数用来让模型知道请求的是哪个属性:
enum Qt::ItemDataRole | Meaning | Type |
---|---|---|
Qt::DisplayRole | 文本 | QString |
Qt::FontRole | 字体 | QFont |
BackgroundRole | 单元格的背景刷 | QBrush |
Qt::TextAlignmentRole | 文字对齐 | enum Qt::AlignmentFlag |
Qt::CheckStateRole | 支持CheckBox with QVariant(), sets checkboxes with Qt::Checked or Qt::Unchecked | enum Qt::ItemDataRole |
Qt::DecorationRole | 以图标形式呈现的装饰数据 | QColor, QIcon or QPixmap |
Qt::ForegroundRole |用于使用默认Delegates渲染的项目的前景画笔(通常为文本颜色 |QBrush
请参阅Qt命名空间文档,了解有关Qt::ItemDataRole枚举功能的更多信息。
现在我们需要确定使用分离的模型如何影响应用程序的性能,因此让我们跟踪视图调用data()方法的频率。为了跟踪视图调用模型的频率,我们在data()方法中添加了调试语句,它会记录到错误输出流中。
在上面的小示例中,data()将被调用42次。每次将光标悬停在字段上时,data()将再次被调用——每个单元格调用7次。
分别执行的是 就是上面的 7个 Role 对应的 case 下面的语句。
这就是为什么在调用 data() 并缓存昂贵的查找操作时,确保数据可用是很重要的。
3. 表格单元中的时钟
我们仍然有一个只读表,但这一次内容每秒都在变化,因为我们显示的是当前时间。
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();}
让时钟滴答作响的东西少了一些。我们需要每秒都告诉视图时间发生了变化,需要重新读取。我们用计时器来做到这一点。在构造函数中,我们将其间隔设置为1秒,并连接其超时信号。
MyModel::MyModel(QObject *parent): QAbstractTableModel(parent), timer(new QTimer(this)){timer->setInterval(1000);connect(timer, &QTimer::timeout , this, &MyModel::timerHit);timer->start();}
下面是对应的槽:
void MyModel::timerHit(){//we identify the top left cellQModelIndex topLeft = createIndex(0,0);//emit a signal to make the view reread identified dataemit dataChanged(topLeft, topLeft, {Qt::DisplayRole});}
我们 调用dataChanged()函数,让视图再次读取左上角单元格中的数据。注意,我们没有显式地将dataChanged()信号连接到视图。这在我们调用setModel()时自动发生。
4. 为列和行设置标题
标题可以通过视图方法隐藏
tableView->verticalHeader()->hide();
然而,头部内容是通过模型设置的,因此我们重新实现了 headerData() 方法:
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");}}return QVariant();}
注意,headerData()方法也有一个 role,与MyModel::data()中role一样的含义。
5. 最小编辑示例
在本例中,我们将构建一个应用程序,通过重复在表格单元格中输入的值,自动用内容填充窗口标题。为了能够轻松地访问窗口标题,我们将 QTableView 放在 QMainWindow 中。
该模型决定是否提供编辑功能。我们只需要修改模型,以便启用可用的编辑功能。这是通过重新实现下列虚拟方法来实现的:setData()和flags()。
// mymodel.h#include <QAbstractTableModel>#include <QString>const int COLS= 3;const int ROWS= 2;class MyModel : public QAbstractTableModel{Q_OBJECTpublic: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]; //holds text entered into QTableViewsignals:void editCompleted(const QString &);};
我们使用二维数组QString m_gridData来存储数据。这使得m_gridData成为MyModel的核心。MyModel的其余部分就像一个包装器,并将m_gridData适应于QAbstractItemModel接口。我们还引入了editCompleted()信号,它可以将修改后的文本传输到窗口标题中。
bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role){if (role == Qt::EditRole) {if (!checkIndex(index))return false;//save value from editor to member m_gridDatam_gridData[index.row()][index.column()] = value.toString();//for presentation purposes only: build and emit a joined stringQString 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,因为单元格只包含文本。如果存在一个复选框,并且用户权限被设置为允许选中该复选框,那么调用也会将角色设置为Qt::CheckStateRole。
Qt::ItemFlags MyModel::flags(const QModelIndex &index) const{return Qt::ItemIsEditable | QAbstractTableModel::flags(index);}
可以使用flags()来调整单元格的各种属性。
返回Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled足以显示编辑器可以选择一个单元格。
如果编辑一个单元格修改的数据比该特定单元格中的数据更多,模型必须发出dataChanged()信号,以便读取已经更改的数据。就像 3 中的写法,自己去手动 emit dataChanged()信号
四、中间的话题
1. TreeView
您可以将上面的示例转换为具有树视图的应用程序。只需将QTableView替换为QTreeView,就可以得到一个读/写树。不需要对模型进行任何更改。树不会有任何层次结构,因为模型本身没有任何层次结构。
QListView、QTableView和QTreeView都使用了一个模型抽象,它是一个合并的列表、表和树。这使得在同一个模型中使用多个不同类型的视图类成为可能。
到目前为止,我们的示例模型看起来是这样的:
我们想要呈现一棵真正的树。为了建立模型,我们将数据包装在上面的示例中。这次我们使用QStandardItemModel,它是一个层次化数据的容器,也实现了QAbstractItemModel。为了显示树,QStandardItemModel必须用QStandardItems填充,它能够保存项目的所有标准属性,如文本、字体、复选框或笔刷。
// modelview.cpp#include "mainwindow.h"#include <QTreeView>#include <QStandardItemModel>#include <QStandardItem>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), treeView(new QTreeView(this)), standardModel(new QStandardItemModel(this)){setCentralWidget(treeView);QList<QStandardItem *> preparedRow = prepareRow("first", "second", "third");QStandardItem *item = standardModel->invisibleRootItem();// adding a row to the invisible root item produces a root elementitem->appendRow(preparedRow);QList<QStandardItem *> secondRow = prepareRow("111", "222", "333");// adding a row to an item starts a subtreepreparedRow.first()->appendRow(secondRow);treeView->setModel(standardModel);treeView->expandAll();}QList<QStandardItem *> MainWindow::prepareRow(const QString &first,const QString &second,const QString &third) const{return {new QStandardItem(first),new QStandardItem(second),new QStandardItem(third)};}
我们只需实例化一个QStandardItemModel,并在构造函数中添加一些QStandardItems。然后,我们可以建立一个分层的数据结构,因为一个QStandardItem可以容纳其他QStandardItem。节点在视图中被折叠和展开。
2. 使用选择器
我们希望访问所选项目的内容,以便将其与层次结构一起输出到窗口标题中。
下面来创建几个条目:
#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);QStandardItem *rootNode = standardModel->invisibleRootItem();//defining a couple of itemsQStandardItem *americaItem = new QStandardItem("America");QStandardItem *mexicoItem = new QStandardItem("Canada");QStandardItem *usaItem = new QStandardItem("USA");QStandardItem *bostonItem = new QStandardItem("Boston");QStandardItem *europeItem = new QStandardItem("Europe");QStandardItem *italyItem = new QStandardItem("Italy");QStandardItem *romeItem = new QStandardItem("Rome");QStandardItem *veronaItem = new QStandardItem("Verona");//building up the hierarchyrootNode-> appendRow(americaItem);rootNode-> appendRow(europeItem);americaItem-> appendRow(mexicoItem);americaItem-> appendRow(usaItem);usaItem-> appendRow(bostonItem);europeItem-> appendRow(italyItem);italyItem-> appendRow(romeItem);italyItem-> appendRow(veronaItem);//register the modeltreeView->setModel(standardModel);treeView->expandAll();//selection changes shall trigger a slotQItemSelectionModel *selectionModel = treeView->selectionModel();connect(selectionModel, &QItemSelectionModel::selectionChanged,this, &MainWindow::selectionChangedSlot);}
视图在一个单独的选择模型中管理选择,这个模型可以用selectionModel()方法取得。我们获取选择模型,以便将槽连接到它的selectionChanged()信号。
void MainWindow::selectionChangedSlot(const QItemSelection & /*newSelection*/, const QItemSelection & /*oldSelection*/)
{//get the text of the selected itemconst QModelIndex index = treeView->selectionModel()->currentIndex();QString selectedText = index.data(Qt::DisplayRole).toString();//find out the hierarchy level of the selected itemint hierarchyLevel = 1;QModelIndex seekRoot = index;while (seekRoot.parent() != QModelIndex()) {seekRoot = seekRoot.parent();hierarchyLevel++;}QString showString = QString("%1, Level %2").arg(selectedText).arg(hierarchyLevel);setWindowTitle(showString);
}
我们通过调用treeView->selectionModel()->currentIndex()来获取与选区对应的模型索引,并通过模型索引获取字段的字符串。然后我们只需要计算物品的层次结构。顶级元素没有父元素,parent()方法将返回一个默认构造的QModelIndex()。
这就是为什么我们使用parent()方法迭代到顶层,同时计算迭代过程中执行的步数。
选择模型(如上所示)可以被检索,但它也可以被QAbstractItemView::setSelectionModel设置。这就是为什么可以有3个视图类同步选择,因为只有一个选择模型的实例被使用。要在3个视图之间共享选择模型,请使用selectionModel(),并将结果分配给第二个和第三个视图类与setSelectionModel()。
3. 预定义模型
使用model/view的典型方法是包装特定的数据,使其可以在视图类中使用。然而,Qt也为常见的底层数据结构提供了预定义的模型。如果有一种可用的数据结构适合您的应用程序,那么预定义的模型可能是一个很好的选择。
模型名称 | 含义 |
---|---|
QStringListModel | 存储一个字符串列表 |
QStandardItemModel | 存储任意的分层项 |
QFileSystemModel | 封装的本地的文件系统 |
QSqlQueryModel | 封装一个SQL结果集 |
QSqlTableModel | 封装一个SQL表 |
QSqlRelationalTableModel | 用外键封装一个SQL表 |
QSortFilterProxyModel | 对另一个模型进行排序和/或过滤 |
4. Delegates
到目前为止,在所有的例子中,数据都是以文本或复选框的形式出现在单元格中,并以文本或复选框的形式进行编辑。提供这些表示和编辑服务的组件称为Delegates。我们才刚刚开始使用Delegates,因为视图使用了默认Delegates。但是,假设我们想要有一个不同的编辑器(例如,滑块或下拉列表),或者想要以图形的形式显示数据。让我们看一个名为Star Delegate的例子,其中使用星号表示评级:
这个视图有一个setItemDelegate()方法,用来替换默认的Delegates,并安装一个自定义的Delegates。新的Delegates可以通过创建一个继承自QStyledItemDelegate的类来编写。为了编写一个显示星号并且没有输入功能的Delegates,我们只需要覆盖两个方法。
class StarDelegate : public QStyledItemDelegate{Q_OBJECTpublic:StarDelegate(QWidget *parent = 0);void paint(QPainter *painter, const QStyleOptionViewItem &option,const QModelIndex &index) const;QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const;};
Paint()根据底层数据的内容绘制星号。可以通过调用index.data()来查找数据。
Delegates的sizeHint()方法用于获取每个星号的尺寸,以便单元格提供足够的高度和宽度来容纳这些星号。
如果你想在视图类的网格中使用自定义图形表示来显示数据,编写自定义Delegates是正确的选择。如果你想离开网格,你不会使用自定义Delegates,而是使用自定义视图类。
Qt文档中对Delegates的其他引用:
Spin BoxDelegates示例
QAbstractItemDelegate类的引用
QSqlRelationalDelegate类的引用
QStyledItemDelegate类引用
QItemDelegate类引用
相关文章:

Qt扫盲-Model/View入门
Model/View 编程入门 一、概述二、介绍1. 标准部件2. Model/View 控件3. Model/View控件概述4. 在表格单和 model 之间使用适配器 Adapters 三、 简单的 model / view 应用程序示例1. 一个只读表2. 使用role扩展只读示例3. 表格单元中的时钟4. 为列和行设置标题5. 最小编辑示例…...

关于win11 debian wsl 子系统安装启动docker一直starting,无法启动
首先我先说明,我的步骤都是按照官网步骤来的 通过官网的操作步骤 通过测试命令 sudo docker run hello-world得到下面的命令,我们通过启动命令 sudo service docker start 执行结果如下图 也就是说无法启动,一直显示在启动中 遇到这种情况…...

Nginx反向代理配置+负载均衡集群部署
文章目录 负载均衡反向代理基础环境部署:什么是代理实验环境图流量过程 环境部署准备两台Web服务器安装Nginx准备页面内容添加主机名 代理服务器配置 修改windos hosts文件测试:终端浏览器 负载均衡反向代理基础环境部署: 什么是代理 正向代…...

设计模式行为型——迭代器模式
什么是迭代器模式 迭代器模式(Iterator Pattern)属于行为型模式,其提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示,即不需要知道集合对象的底层表示。编程环境中非常常用的设计模式。 迭代…...

K8s持久化存储(nfs网络存储)
数据卷 emptydir,是本地存储,pod重启,数据就不存在了,需要对数据持久化存储 1.nfs,网络存储 ,pod重启,数据还存在的...

常规VUE项目优化实践,跟着做就对了!
总结: 主要优化方式: imagemin优化打包大小(96M->50M),但是以打包速度为代价,通过在构建过程中压缩图片来实现,可根据需求开启。字体压缩:目前项目内引用为思源字体,…...

PLL 的 verilog 实现
锁相环(PLL)是一种常用的频率、相位追踪算法,在信号解调、交流并网等领域有着广泛的应用。本文对全数字锁相环的原理进行介绍,随后给出 verilog 实现及仿真。 PLL 锁相原理 锁相环结构如下图所示,主要由鉴相器、环路滤…...

【Hystrix技术指南】(1)基本使用和配置说明
这世间许多事物皆因相信而存在,所以人们亲手捏出了泥菩萨,却选择坚定的去信仰它。 分布式系统的规模和复杂度不断增加,随着而来的是对分布式系统可用性的要求越来越高。在各种高可用设计模式中,【熔断、隔离、降级、限流】是经常被…...
Oracle EBS OM客制化调用API创建销售订单非常慢(FND_FLEX_HASH死锁)
业务场景 由于Oracle EBS标准功的公司间关联交易操作涉及业务节点环节多,需要多个业务部门参考操作完成,浪费人力和花费时间。随着国内集团公司通过业务整合优化,大幅度减少间中很多环节的人为操作,如国内公司间贸易通过类似于客制化出货单申请方式,跨国公司间贸易通过类似…...

【leetcode】394. 字符串解码
题目链接:力扣 给定一个经过编码的字符串,返回它解码后的字符串。 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 你可以认为输入字符串总是有效的;输入字符串中没…...

系统架构设计高级技能 · 系统质量属性与架构评估(二)【系统架构设计师】
系列文章目录 系统架构设计高级技能 软件架构概念、架构风格、ABSD、架构复用、DSSA(一)【系统架构设计师】 系统架构设计高级技能 系统质量属性与架构评估(二)【系统架构设计师】 系统架构设计高级技能 软件可靠性分析与设计…...

魅族Pandaer手机壳
Pandaer的设计真是非常好看啊!像是手机壳的花样就特别多,还分出来很多系列,我比较喜欢它的亮面设计,入手了一款iPhone的,花色叫做“失控街头”,壳内部也是亮的,看起来特别浮夸,潮里潮…...

F5洞察2023年网络威胁,助力网络安全防护
2023已经过半,关于网络安全防护的相关讨论话题热度始终居高不下。对于网络安全领域的从业者来说,应当对相关的前瞻分析有所了解。前段时间,我阅读了F5 安全运营中心工程师对威胁网络安全的预测,深受启发,故此选取了几则…...

从零构建深度学习推理框架-4 框架中的算子注册机制
今天要讲的这一注册机制用到了设计模式中的工厂模式和单例模式,所以这节课也是对两大设计模式的一个合理应用和实践。KuiperInfer的注册表是一个map数据结构,维护了一组键值对,key是对应的OpType,用来查找对应的value,…...
使用vscode+ssh免密远程Linux
使用vscodessh免密远程Linux 使用 SSH 密钥对:使用 SSH Agent:ssh-agent的使用场景 使用 SSH 密钥对: 确保你的本地机器上已经生成了 SSH 密钥对。如果没有,请使用以下命令生成密钥对: ssh-keygen -t rsa这将在 ~/.ssh…...

rust-异步学习
rust获取future中的结果 两种主要的方法使用 async: async fn 和 async 块 async 体以及其他 future 类型是惰性的:除非它们运行起来,否则它们什么都不做。 运行 Future 最常见的方法是 .await 它。 当 .await 在 Future 上调用时,它会尝试把…...

【Azure】office365邮箱测试的邮箱账号因频繁连接邮箱服务器而被限制连接 引起邮箱显示异常
azure微软office365邮箱会对频繁连接自身邮箱服务器的IP地址进行,连接邮箱服务器IP限制,也就是黑名单,释放时间不确定,但至少一天及以上。 解决办法,换一个IP,或者新注册一个office365邮箱再重试。 以下是…...
重新登录成功和登录失败处理器
<template><div class="login"><el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form"><h3 class="title">Java1234 Vue3 后台管理系统</h3><el…...

【Spring】(三)Spring 使用注解存储和读取 Bean对象
文章目录 前言一、使用注解储存 Bean 对象1.1 配置扫描路径1.2 类注解储存 Bean 对象1.2.1 Controller(控制器存储)1.2.2 Service(服务储存)1.2.3 Repository(仓库存储)1.2.4 Component(组件储存…...

ParallelCollectionRDD [0] isEmpty at KyuubiSparkUtil.scala:48问题解决
ParallelCollectionRDD [0] isEmpty at KyuubiSparkUtil.scala:48问题解决 这个问题出现在使用Kyubi Spark Util处理ParallelCollectionRDD的过程中,具体是在KyubiSparkUtil.scala文件的第48行调用isEmpty方法时出现的。该问题可能是由以下几个原因引起的࿱…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...