- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我来自 C# 和 WPF,我们有很好的方法将集合绑定(bind)到 View ,通过当前 DataContext 填充数据。
我一直在学习 Qt,我知道有一种方法可以为 View 提供模型 (QAbstractItemModel),然后通过修改模型可以自动更新 View ,这很好,也是我想要的。
我当前的问题是我想为以下类 Definition
创建一个编辑器 View
class Definition
{
public:
vector<Step*> Steps;
bool SomeBoolMember1;
bool SomeBoolMember2;
int SomeIntMember1;
int SomeIntMember2;
}
步骤
是
class Step
{
public:
vector<Requirement*> Requirements;
bool SomeBoolMember1;
bool SomeBoolMember2;
int SomeIntMember1;
int SomeIntMember2;
}
要求
是
class Requirement
{
public:
RequirementType Type;
}
我的目标是构建一个能够修改 Definition
集合的 View 。可能的 View 可能是这样的:
选择不同的定义时,所选的项目 View 已更新(加载所选定义的数据),然后我们有一个带有步骤列表的scrollView。每个步骤都有一个要求列表。
如您所见,这不是火箭科学,但我知道这可能很乏味。关于如何构建模型/ View 模型以实现所需功能的任何提示?
最佳答案
你的问题很宽泛。 Qt 的模型非常通用,可以通过多种方式构造它们以实现相同的行为。我已经创建了一个简化的示例,您可以如何构建它们。我只使用了Definition
和Step
,将Requirement
添加到Step
类似于添加Step
到定义
...
struct Step
{
bool SomeBoolMember1 {false};
int SomeIntMember1 {0};
};
struct Definition
{
std::vector<std::shared_ptr<Step>> Steps;
bool SomeBoolMember1 {false};
int SomeIntMember1 {0};
};
struct Data
{
std::vector<std::shared_ptr<Definition>> definitions;
};
模型非常简单,我为 Definition
创建了一个模型,为 Step
创建了一个模型。每个模型都有代表这些对象属性的列,模型的行代表对象的一个实例。 Definition
模型有一个 Step
列,它返回给定 Definition
的 Step
模型。
class StepModel : public QAbstractItemModel
{
Q_OBJECT
using BaseClass = QAbstractItemModel;
public:
enum Columns
{
E_BOOL_1,
E_INT_1,
E_REQUIREMENT,
_END
};
StepModel(Definition* definition, QObject* parent) :
BaseClass(parent),
definition(definition)
{
}
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
{
return createIndex(row, column, nullptr);
}
virtual QModelIndex parent(const QModelIndex &child) const override
{
return QModelIndex();
}
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
return definition->Steps.size();
}
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override
{
return _END;
}
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if (role != Qt::DisplayRole) return QVariant();
const auto& step = definition->Steps.at(index.row());
switch (index.column())
{
case E_BOOL_1:
return step->SomeBoolMember1;
case E_INT_1:
return step->SomeIntMember1;
case E_REQUIREMENT:
return QVariant();
}
}
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
{
if (role != Qt::EditRole) return false;
auto& step = definition->Steps.at(index.row());
switch (index.column())
{
case E_BOOL_1:
step->SomeBoolMember1 = value.toBool();
return true;
case E_INT_1:
step->SomeIntMember1 = value.toInt();
return true;
case E_REQUIREMENT:
assert(false);
return false;
}
}
virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
{
assert(count == 1);
beginInsertRows(parent, row, row);
definition->Steps.push_back(std::make_shared<Step>());
endInsertRows();
return true;
}
virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
{
assert(count == 1);
beginRemoveRows(parent, row, row);
definition->Steps.erase(definition->Steps.begin() + row);
endRemoveRows();
return true;
}
private:
Definition* definition;
};
Q_DECLARE_METATYPE(StepModel*)
class DefinitionModel : public QAbstractItemModel
{
Q_OBJECT
using BaseClass = QAbstractItemModel;
public:
enum Columns
{
E_NAME,
E_BOOL_1,
E_INT_1,
E_STEPS,
_END
};
DefinitionModel(QObject* parent) :
BaseClass(parent)
{
}
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
{
return createIndex(row, column, nullptr);
}
virtual QModelIndex parent(const QModelIndex &child) const override
{
return QModelIndex();
}
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
return definitionData.definitions.size();
}
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override
{
return _END;
}
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if (role != Qt::DisplayRole) return QVariant();
const auto& definition = definitionData.definitions.at(index.row());
switch (index.column())
{
case E_NAME:
return QString("Definition %1").arg(index.row() + 1);
case E_BOOL_1:
return definition->SomeBoolMember1;
case E_INT_1:
return definition->SomeIntMember1;
case E_STEPS:
return QVariant::fromValue(new StepModel(definition.get(), nullptr));
}
}
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
{
if (role != Qt::EditRole) return false;
auto& definition = definitionData.definitions.at(index.row());
switch (index.column())
{
case E_BOOL_1:
definition->SomeBoolMember1 = value.toBool();
return true;
case E_INT_1:
definition->SomeIntMember1 = value.toInt();
return true;
default:
assert(false);
return false;
}
}
virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
{
assert(count == 1);
beginInsertRows(parent, row, row);
definitionData.definitions.push_back(std::make_shared<Definition>());
endInsertRows();
return true;
}
virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
{
assert(count == 1);
beginRemoveRows(parent, row, row);
definitionData.definitions.pop_back();
endRemoveRows();
return true;
}
private:
Data definitionData;
};
在这个例子中,DefinitionModel
拥有 Data
的实例,这不是您想要在实际应用程序中做的事情,只是为了简单起见。
Dialog 有点复杂,因为您必须在创建新实例时为 Steps
动态创建小部件。我已经为小部件创建了一个简单的映射,映射到它适当模型的列。每次选择新定义时,都会通过此映射从模型中加载回数据。
namespace Ui
{
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
auto definitionModel = new DefinitionModel(this);
ui->definitionView->setModel(definitionModel);
connect(ui->definitionView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, [=]
{
update();
}, Qt::QueuedConnection);
connect(ui->addDefinition, &QPushButton::pressed, this, [this]
{
auto* model = ui->definitionView->model();
model->insertRow(model->rowCount());
ui->definitionView->update();
ui->definitionView->setCurrentIndex(model->index(model->rowCount() - 1, 0));
});
connect(ui->removeDefinition, &QPushButton::pressed, this, [this]
{
auto* model = ui->definitionView->model();
if (model->rowCount() > 0)
{
const int row = model->rowCount() - 1;
for (const auto& widget : steps[row])
{
unmap(widget);
}
steps.erase(steps.find(row));
model->removeRow(row);
}
});
auto getCurrentDefinition = [this] { return ui->definitionView->currentIndex().row(); };
map(ui->definitionInt, definitionModel, DefinitionModel::E_INT_1, getCurrentDefinition);
map(ui->definitionBool, definitionModel, DefinitionModel::E_BOOL_1, getCurrentDefinition);
connect(ui->addStep, &QPushButton::pressed, this, [=]
{
if (getCurrentDefinition() == -1) return;
auto widget = new QWidget(this);
auto layout = new QHBoxLayout(widget);
auto checkBox = new QCheckBox(widget);
layout->addWidget(checkBox);
auto spinBox = new QSpinBox(widget);
layout->addWidget(spinBox);
auto removeButton = new QPushButton(widget);
removeButton->setText("remove");
layout->addWidget(removeButton);
ui->rightLayout->addWidget(widget);
const int currentDefinition = getCurrentDefinition();
steps[currentDefinition].push_back(widget);
auto model = definitionModel->data(definitionModel->index(currentDefinition, DefinitionModel::E_STEPS), Qt::DisplayRole).value<QAbstractItemModel*>();
model->setParent(widget);
const int rowCount = model->rowCount();
model->insertRow(rowCount);
auto getRow = [=]
{
auto it = std::find(steps[currentDefinition].begin(), steps[currentDefinition].end(), widget);
return std::distance(steps[currentDefinition].begin(), it);
};
map(checkBox, model, StepModel::E_BOOL_1, getRow);
map(spinBox, model, StepModel::E_INT_1, getRow);
connect(ui->definitionView->selectionModel(), &QItemSelectionModel::currentRowChanged, widget, [=] (const QModelIndex& current)
{
widget->setVisible(current.row() == currentDefinition);
});
connect(removeButton, &QPushButton::pressed, widget, [=]
{
model->removeRow(rowCount);
unmap(checkBox);
unmap(spinBox);
ui->rightLayout->removeWidget(widget);
auto it = std::find(steps[getCurrentDefinition()].begin(), steps[getCurrentDefinition()].end(), widget);
steps[currentDefinition].erase(it);
delete widget;
});
update();
});
}
~MainWindow()
{
delete ui;
}
private:
void map(QCheckBox* checkBox, QAbstractItemModel* model, int column, std::function<int()> getRow)
{
connect(checkBox, &QCheckBox::toggled, this, [=] (bool value)
{
model->setData(model->index(getRow(), column), value, Qt::EditRole);
});
auto update = [=]
{
checkBox->setChecked(model->data(model->index(getRow(), column), Qt::DisplayRole).toBool());
};
mapping.emplace(checkBox, update);
}
void map(QSpinBox* spinBox, QAbstractItemModel* model, int column, std::function<int()> getRow)
{
connect(spinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [=] (int value)
{
model->setData(model->index(getRow(), column), value, Qt::EditRole);
});
auto update = [=]
{
spinBox->setValue(model->data(model->index(getRow(), column), Qt::DisplayRole).toInt());
};
mapping.emplace(spinBox, update);
}
void unmap(QWidget* widget)
{
mapping.erase(mapping.find(widget));
}
void update() const
{
for (const auto& pair : mapping)
{
pair.second();
}
}
Ui::MainWindow *ui;
std::map<QWidget*, std::function<void()>> mapping;
std::map<int, std::vector<QWidget*>> steps;
};
用户界面文件:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QHBoxLayout" name="verticalLayout" stretch="1,2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<item>
<widget class="QListView" name="definitionView">
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>10</number>
</property>
<item>
<widget class="QPushButton" name="addDefinition">
<property name="text">
<string>add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeDefinition">
<property name="text">
<string>remove</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="rightMargin">
<number>10</number>
</property>
<item>
<layout class="QVBoxLayout" name="rightLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="definitionBool">
<property name="text">
<string>CheckBox</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="definitionInt"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Steps</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addStep">
<property name="text">
<string>add</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
关于c++ - Qt 嵌套模型 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51439714/
所以我试图设置“内容”类的高度,但它似乎不起作用。我对嵌套 DIV 非常陌生,我已经尝试了我在谷歌搜索中发现的修复程序,但似乎没有任何效果。帮助?
好的,所以我一直在四处寻找,但找不到这个问题的答案。但是,我需要将一个 View 嵌套在另一个 View 中。 我有一个 $layout 正在使用我拥有的 default.layout Blade 文
好的,所以我一直在四处寻找,但找不到这个问题的答案。但是,我需要将一个 View 嵌套在另一个 View 中。 我有一个 $layout 正在使用我拥有的 default.layout Blade 文
基本上,我的问题很简单,但它需要知道 Struts 1.1 并且还活着的人。 我尝试构建的伪代码看起来像这样: IF element.method1 = true THEN IF element
我正在尝试将 Excel 嵌套 IF 语句转换为代码语言,但我不确定我是否正确执行此操作,希望能得到一些帮助 这是Excel语句: =IF(D3="Feather",IF(OR(I3>1000,R3=
如果我们创建两个或三个评论并对其进行多次回复,则“有用”链接在单击时会导致问题,它会对具有相同编号的索引执行 ng-click 操作,从而显示具有相同索引的所有文本。如何解决此嵌套问题,以便在单击链接
我在项目中使用Scala,想与Stripe集成,但它只提供Java API。例如,要创建 session ,我使用: val params = new util.HashMap[String, Any
以下代码有一个 Div,其中连续包含四个较小的 Div。四个 Div 中的每一个还包含一个较小的 Div,但此 Div 未显示。我尝试了各种显示和位置组合,看看 div 是否会出现。 classGoa
我在这里有一个问题,循环是: for (i=0; i < n; ++i) for (j = 3; j < n; ++j) { ...
我正在尝试编写代码来显示具有奇数宽度的形状。形状完成后,将其放置在外部形状内。用户将能够输入用于形状的字符和行数。我希望生成一个形状,并通过 for 循环生成一个外部形状。 ***** .
$(".globalTabs").each(function(){ var $globalTabs = $(this); var parent = $globalTabs.parent
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 9 年前。 Improve th
所以我在这个问题上遇到了一些麻烦,因为变量 i。我只是不确定在第二个 while 循环中如何处理它。对于我的外循环,我知道它将运行 log_4(n^2) 次迭代。对于内部 while 循环,我计算的迭
我似乎找不到在枚举上应用多个 if/then 逻辑的工作方式。 anyOf 不应用条件逻辑,而是表示如果其中任何一个匹配则很好。 allOf 再次不应用条件逻辑,而是测试属性/必填字段的超集。 这是一
如何访问 ReaderT 的内部 monad。 在我的例子中,我有类型: newtype VCSSetupAction a = VCSSetupAction (ReaderT (Maybe VCSCo
这个问题在这里已经有了答案: Add leading zeroes/0's to existing Excel values to certain length (7 个回答) 7年前关闭。 我正在寻
我已经绑定(bind)了很多 AND/OR 函数的组合并且没有运气。 这是我需要创建的: 在 B 列中,我有公司 ID,范围从两个数字字符到六个数字字符。 我需要在 B 列中的每个公司 ID 之前的每
我是 VBA 新手,在尝试编写的宏中使用 If 语句时遇到了一些困难。每个月我都会收到一份 Excel 报告,其中列出了我们公司的哪些员工执行了某些任务。我正在编写的宏旨在将每个员工的数据复制并粘贴到
如果在 B 列中找到单元格 A1 中的值,则使用文本 321 填充除非在 C 列中找到单元格 A1 中的值,在这种情况下填充文本 121反而。如果单元格 A1 的内容不在 B 列或 C 列中,则使用
我有几十万个地址。其中一些在整数之后有粒子。如 4356 A Horse Avenue , 其他格式正常4358 Horse Avenue .有些有“A”,有些有“B”。我正在尝试删除整数和粒子之间的
我是一名优秀的程序员,十分优秀!