要将子类/视图用于自定义对象列表的模型

时间:2011-07-20 15:53:18

标签: c++ model-view-controller qt qt4

我没有足够的Qt经验来做出好的设计选择。经验丰富的Qt程序员的任何帮助将非常感激 我试图找出哪个模型要子类,要使用哪个视图,委托子类/扩展我应该做什么......

我的问题类似于:我想要显示这些区域,每行1个:

class Zone{

    //inputs
    string country;  //edited with a QComboBox
    string city;     //edited with a QComboBox
    int ageMin;

    //stored result
    int nbInhabitantsOlderThanMin;
}

这就是我想做的事情,以及每个要求让我想到的设计选择:

  • 我想显示列表( - > QListView
  • 但要显示1项我需要多列( - > QTableView
  • 我希望双击一行以触发在自定义小部件中进行编辑,因为无法编辑nbInhabitantsOlderThanMin,选择国家/地区会限制可以选择的城市列表在QComboBox中(反之亦然,在我的实例中)( - >我应该在某处使用 QDataWidgetMapper (或子类?)...)

    所以,虽然行的版本应该发生在一个小部件中,但是显示很简单/不是自定义的,并且子类化委托(例如 QStyledItemDelegate )(我不太确定这个)似乎没有正确的方法来让1个自定义小部件与许多子输入小部件同时编辑3个字段。
    我认为要建模的数据有利于子类化 QAbstractListModel 的模型,但是与默认委托查看兼容的许多列的显示有利于 QAbstractTableModel ..

所以我真的不知道要采用哪种设计。任何有经验的帮助点都非常受欢迎:)

3 个答案:

答案 0 :(得分:4)

QDataWidgetMapper略有不同。这是一种使用自定义控件从模型(例如QStandardItemModel)显示一个项目的方法。您可以阅读更多相关信息here,附带快照以及如何实施快照的示例。

虽然它确实很酷,但我认为这不是你想要的。主要是因为您指定要以列表格式查看项目。但是,您可以在简单列表中显示所有项目,双击可以使用QDataWidgetMapper打开对话框。在这种情况下,您需要使用QListView / QListWidget实现双击事件。

尽管如此,我个人并不喜欢用户额外窗口的额外负担。我更喜欢谨慎使用弹出窗口。但如果你喜欢这种方法,那就继续吧。 This是QDataWidgetMapper的另一个例子,非常好。

我首选的方法仍然是使用QTableView,并为需要专门编辑的列提供委托。 Here是模型/视图所有内容的完美演绎。因此,如果您决定使用QListView或QTableView,它将为您提供一个良好的开端。它还讨论了如何创建代理来编辑你想要的字段。

那么,您如何创建自定义委托?基本上,您只是继承自QItemDelegate。上面的链接中有一些例子,但我要强调一些重点。

QWidget *ComboBoxDelegate::createEditor(QWidget *parent,
     const QStyleOptionViewItem &/* option */,
     const QModelIndex &index) const
 {
     QComboBox *editor = new QComboBox (parent);
     //  Add items to the combobox here.
     //  You can use the QModelIndex passed above to access the model
     //  Add find out what country was selected, and therefore what cities
     //  need to be listed in the combobox

     return editor;
 }

void ComboBoxDelegate::setEditorData(QWidget *editor,
                                     const QModelIndex &index) const
 {
     int value = index.model()->data(index, Qt::EditRole).toInt();

     QComboBox  *comboBox= static_cast<QComboBox *>(editor);
     int _SelectedItem = // Figure out which is the currently selected index;
     comboBox->setCurrentIndex(_SelectedItem);
 }

void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                    const QModelIndex &index) const
 {
     QComboBox  *comboBox= static_cast<QComboBox *>(editor);
     comboBox->interpretText();
     int value = comboBox->currentIndex();
     //  Translate the current index to whatever you actually want to
     // set in your model.

     model->setData(index, value, Qt::EditRole);
}

填写我在示例中留下的空白,并且您有代表。 现在,如何在QTableView中使用它:

您可以按如下方式为表的特定列设置委托:

setItemDelegateForColumn(_ColumnIndex, new ComboBoxDelegate(_YourTableModel));

并且,如果您想阻止某些列可编辑:

_YourTableModel->setColumnEditable(_ColumnIndex, false);

一旦你的模型设置完毕,其他一切都应该自行解决。

希望有所帮助。

答案 1 :(得分:1)

我刚刚完成了与此非常类似的事情,因为我需要为我想要的每个对象添加多个字段。我做得非常好的方法是将QAbstractListmodel子类化并使用自定义ListItem。我通过使用自定义委托并使用一些javascript来计算每个字段中最大的东西的大小,然后将列大小设置为该宽度来伪造列。我认为这是最简单的方法。

对于下面的评论,区域将继承自:

class ListItem: public QObject {
    Q_OBJECT

public:
    ListItem(QObject* parent = 0) : QObject(parent) {}
    virtual ~ListItem() {}
    virtual QString id() const = 0;
    virtual QVariant data(int role) const = 0;
    virtual QHash<int, QByteArray> roleNames() const = 0;

signals:
    void dataChanged();
};

答案 2 :(得分:1)

首先,您应该重新实现QAbstractItemDelegateQItemDeleagecreateEditor,从而使setEditorData(或setModelData更加方便)。

然后,您应该设置自己的itemDelegate(请参阅QAbstractItemView::setItemDelegate)。

通常没什么区别,用于呈现数据的小部件:可以是QTreeWidetQTreeViewQTableWidgetQTableView。请注意,“小部件”比“视图”更容易使用,但它们并不是那么强大