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方法时出现的。该问题可能是由以下几个原因引起的࿱…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...

自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...