#include class TreeNode: public QObject { public: TreeNode(const QString& name, TreeNode *parent) : QObject(parent) { setObjectName(name); } QString getName() const { return objectName(); } TreeNode* getParentNode() const { return static_cast(parent()); } int getRank() const { if (parent()) { QObjectList ch = parent()->children(); for (int i = 0; i < ch.size(); i++) { if (ch[i] == this) { return i; } } } return -1; } int getChildCount() const { return children().size(); } TreeNode* getChildNode(int index) const { return static_cast(children().at(index)); } bool isVarReadOnly(int index) const { return false; } int getVarCount() const { return m_variables.size(); } void appendVar(const QString& name, const QString& value) { m_variables.append(Variable(name, value)); } const QString& getVariable(int index) const { return m_variables[index].variable; } const QString& getValue(int index) const { return m_variables[index].value; } bool getActive(int index) const { return m_variables[index].active; } bool getForce(int index) const { return m_variables[index].force; } private: struct Variable { Variable() { active = true; force = false; } Variable(const QString& name, const QString& val) { variable = name; value = val; active = true; force = false; } QString variable; QString value; bool active; bool force; }; QList m_variables; }; class TreeModel: public QAbstractItemModel { public: enum Column { kNodeColumn, kActiveColumn, kForceColumn, kVariableColumn, kValueColumn, kStatusColumn, kColumnCount }; TreeModel(TreeNode *root, QObject *parent = 0) : QAbstractItemModel(parent), m_root_node(root) {} virtual int columnCount(const QModelIndex& parent) const; virtual int rowCount(const QModelIndex& parent) const; virtual QModelIndex index(int row, int column, const QModelIndex& parent) const; virtual QModelIndex parent(const QModelIndex& index) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; virtual QVariant data(const QModelIndex& index, int role) const; virtual Qt::ItemFlags flags(const QModelIndex& index) const; TreeNode* indexToNode(const QModelIndex& index) const; QModelIndex nodeToIndex(const TreeNode *node) const; int indexToVar(const QModelIndex& index, TreeNode **node) const; QModelIndex varToIndex(const TreeNode *node, int var_index) const; private: TreeNode *m_root_node; }; TreeNode* TreeModel::indexToNode(const QModelIndex& index) const { if (!index.isValid()) { return NULL; } const TreeNode *node = static_cast(index.internalPointer()); if (node == NULL) { return m_root_node; } else { int rank = index.row() - node->getVarCount(); if (rank < 0 || rank >= node->getChildCount()) { return NULL; } return node->getChildNode(rank); } } QModelIndex TreeModel::nodeToIndex(const TreeNode *node) const { TreeNode *parent = node->getParentNode(); int row = parent ? parent->getVarCount() + node->getRank() : 0; return createIndex(row, kNodeColumn, parent); } int TreeModel::indexToVar(const QModelIndex& index, TreeNode **node) const { if (!index.isValid()) { return -1; } TreeNode *n = static_cast(index.internalPointer()); if (n == NULL || index.row() >= n->getVarCount()) { return -1; } *node = n; return index.row(); } QModelIndex TreeModel::varToIndex(const TreeNode *node, int var_index) const { return createIndex(var_index, 0, const_cast(node)); } int TreeModel::columnCount(const QModelIndex& parent) const { return kColumnCount; } int TreeModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) { return m_root_node ? 1 : 0; } TreeNode *node = indexToNode(parent); if (node) { return node->getVarCount() + node->getChildCount(); } else { return 0; } } QModelIndex TreeModel::index(int row, int column, const QModelIndex& parent) const { return createIndex(row, column, indexToNode(parent)); } QModelIndex TreeModel::parent(const QModelIndex& index) const { TreeNode *parent = static_cast(index.internalPointer()); if (parent == NULL) { return QModelIndex(); } else { return nodeToIndex(parent); } } QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case kActiveColumn: return "Enabled"; case kForceColumn: return "Force"; case kVariableColumn: return "Variable"; case kValueColumn: return "Value"; case kStatusColumn: return "Status"; } } return QVariant(); } QVariant TreeModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } if (role == Qt::CheckStateRole) { TreeNode *node; int var = indexToVar(index, &node); if (var >= 0) { switch (index.column()) { case kActiveColumn: return node->getActive(var) ? Qt::Checked : Qt::Unchecked; case kForceColumn: return node->getForce(var) ? Qt::Checked : Qt::Unchecked; } } return QVariant(); } if (role == Qt::DisplayRole) { TreeNode *node = indexToNode(index); if (node) { if (index.column() != kNodeColumn) { return QVariant(); } return node->getName(); } int var = indexToVar(index, &node); if (var >= 0) { switch (index.column()) { case kVariableColumn: return node->getVariable(var); case kValueColumn: return node->getValue(var); case kStatusColumn: if (!node->getActive(var)) { return "disabled"; } else if (node->isVarReadOnly(var)) { return "read-only"; } else { return "OK"; } default: return QVariant(); } } } return QVariant(); } Qt::ItemFlags TreeModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (index.isValid()) { TreeNode *node; if (indexToVar(index, &node) >= 0 && (index.column() == kActiveColumn || index.column() == kForceColumn)) { flags |= Qt::ItemIsUserCheckable; } } return flags; } class MainWindow: public QMainWindow { public: MainWindow(QWidget *parent = 0) : QMainWindow(parent) { // build a tree m_root_node = new TreeNode("root", NULL); TreeNode *branch1 = new TreeNode("branch1", m_root_node); branch1->appendVar("leaf1a", "Margherita"); branch1->appendVar("leaf1b", "Regina"); branch1->appendVar("leaf1c", "Salsiccia & Broccoli"); TreeNode *branch2 = new TreeNode("branch2", m_root_node); branch2->appendVar("leaf2a", "Iroquois"); branch2->appendVar("leaf2b", "Navaho"); branch2->appendVar("leaf2c", "Mohawk"); TreeNode *branch3 = new TreeNode("branch3", m_root_node); branch3->appendVar("leaf3a", "Orange"); branch3->appendVar("leaf3b", "Strawberry"); branch3->appendVar("leaf3c", "Banana"); // set up the model and view QTreeView *view = new QTreeView; TreeModel *model = new TreeModel(m_root_node, view); view->setModel(model); view->expandToDepth(1); view->header()->setSectionResizeMode(TreeModel::kValueColumn, QHeaderView::Stretch); view->header()->setStretchLastSection(false); int i, n = model->columnCount(QModelIndex()); for (i = 0; i < n; i++) { if (i != TreeModel::kNodeColumn && i != TreeModel::kValueColumn) { view->resizeColumnToContents(i); } } setCentralWidget(view); // menu QMenu *file_menu = menuBar()->addMenu("&File"); QAction *action = new QAction("&Quit", this); action->setShortcuts(QKeySequence::Quit); file_menu->addAction(action); connect(action, SIGNAL(triggered()), qApp, SLOT(quit())); resize(400, 300); } virtual ~MainWindow() { delete m_root_node; } private: TreeNode *m_root_node; }; int main(int argc, char *argv[]) { QApplication qapp(argc, argv); qapp.setStyleSheet("QTreeView { background: black; color: white }"); MainWindow mainwin; mainwin.show(); return QApplication::exec(); }