前言
qt中定義了很多簡單實(shí)用的數(shù)據(jù)展示視圖控件,,比如表格和樹形表等,。由于表格的結(jié)構(gòu)和數(shù)據(jù)庫存儲(chǔ)類似,所以qt提供一個(gè)類來實(shí)現(xiàn)表格視圖和數(shù)據(jù)庫的直接連接,。但是對于樹形結(jié)構(gòu),在數(shù)據(jù)庫中的表達(dá)和視圖的表達(dá)有較大差異,qt尚未提供相關(guān)的功能,。 本文的背景是要做一個(gè)模板的管理功能。由于模板的組織方式是層級(jí)結(jié)構(gòu)(樹形結(jié)構(gòu)),,所以要用到樹形控件,。又要求具有持久化保存模板的功能,將來還可能擴(kuò)展的遠(yuǎn)程的模板統(tǒng)一管理,,所以使用數(shù)據(jù)庫進(jìn)行模板的保存,。
所以本文要介紹的是筆者最近做的一個(gè)基于QTreeWidget的自定義樹形控件,,可以實(shí)現(xiàn)右擊菜單,,每次更改樹結(jié)構(gòu)后實(shí)時(shí)修改數(shù)據(jù)庫,初始化時(shí)從數(shù)據(jù)庫讀取樹形結(jié)構(gòu)并顯示,。 具體效果如下: 但由于內(nèi)容比較多,,筆者打算分為兩篇進(jìn)行介紹,首先在這一篇中,,介紹qt控件的繼承以及在界面中把控件提升(promete)為自定義的控件,。在下一篇中,介紹如何把對樹的操作寫入數(shù)據(jù)庫以及如何在初始化過程中從數(shù)據(jù)庫讀取樹結(jié)構(gòu),。
具體實(shí)現(xiàn)
控件類的定義
首先,,自定義的控件要繼承自QTreeWidget,然后需要添加一些增加根節(jié)點(diǎn),,刪除節(jié)點(diǎn),,增加目錄,增加模板,,重命名節(jié)點(diǎn)等想要實(shí)現(xiàn)功能的槽函數(shù),,這些槽函數(shù)會(huì)在后面右擊菜單中調(diào)用,。右擊菜單在qt中是一個(gè)action,,需要定義幾個(gè)QAction變量,。initTreeWidget()函數(shù)在程序啟動(dòng)時(shí)從數(shù)據(jù)庫讀取樹形結(jié)構(gòu),但是本文只介紹控件的自定義相關(guān)內(nèi)容,,這個(gè)函數(shù)中會(huì)生成一個(gè)固定的樹。
class QMyTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
explicit QMyTreeWidget(QWidget *parent = 0);
private slots:
void on_treeWidget_customContextMenuRequested(const QPoint &pos);
void addRootNode();
void deleteAllNodes();
void addCategory();
void addTemplate();
void deleteNode();
void reNameNode();
private:
void initTreeWidget();
QTreeWidgetItem* curItem; //當(dāng)前被右擊的item
//空白地方點(diǎn)擊
QAction *addRootNodeAction;
QAction *deleteAllNodesAction;
//節(jié)點(diǎn)上點(diǎn)擊
QAction *addCategoryAction;
QAction *addTemplateAction;
QAction *deleteNodeAction;
QAction *reNameNodeAction;
};
action和槽函數(shù)連接
addRootNodeAction = new QAction("&addRootNode", this);
deleteAllNodesAction = new QAction("&deleteAllNodes", this);
addCategoryAction = new QAction("&addCategory", this);
addTemplateAction = new QAction("&addTemplate", this);
deleteNodeAction = new QAction("&deleteNode", this);
reNameNodeAction = new QAction("&reNameNode", this);
connect(addRootNodeAction,SIGNAL(triggered()),this,SLOT(addRootNode()));
connect(deleteAllNodesAction,SIGNAL(triggered()),this,SLOT(deleteAllNodes()));
connect(addCategoryAction,SIGNAL(triggered()),this,SLOT(addCategory()));
connect(addTemplateAction,SIGNAL(triggered()),this,SLOT(addTemplate()));
connect(deleteNodeAction,SIGNAL(triggered()),this,SLOT(deleteNode()));
connect(reNameNodeAction,SIGNAL(triggered()),this,SLOT(reNameNode()));
右擊菜單實(shí)現(xiàn)
首先,,需要把控件的contextMenuPolicy選項(xiàng)設(shè)為CustomContextMenu 然后把右擊菜單動(dòng)作和槽函數(shù)連接起來:
connect(this,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(on_treeWidget_customContextMenuRequested(QPoint)));
在槽函數(shù)中實(shí)現(xiàn)不同位置點(diǎn)擊彈出不同的菜單
void QMyTreeWidget::on_treeWidget_customContextMenuRequested(const QPoint &pos)
{
curItem=this->itemAt(pos); //獲取當(dāng)前被點(diǎn)擊的節(jié)點(diǎn)
//在空白位置點(diǎn)擊,,彈出菜單:添加根節(jié)點(diǎn),刪除所有模板,。
if(curItem == NULL)
{
QMenu *popMenu =new QMenu(this);//定義一個(gè)右鍵彈出菜單
popMenu->addAction(addRootNodeAction);
popMenu->addAction(deleteAllNodesAction);
popMenu->exec(QCursor::pos());
}
else{
QVariant var = curItem->data(0,Qt::UserRole);
qDebug() << var.toString() << endl;
//讀取表,,對isCategory字段進(jìn)行判斷
if(1) //選中目錄
{
qDebug() << "category" << endl;
QMenu *popMenu =new QMenu(this);//定義一個(gè)右鍵彈出菜單
popMenu->addAction(addCategoryAction);
popMenu->addAction(addTemplateAction);
popMenu->addAction(deleteNodeAction);
popMenu->addAction(reNameNodeAction);
popMenu->exec(QCursor::pos());
}
else //選中模板
{
qDebug() << "template" << endl;
QMenu *popMenu =new QMenu(this);//定義一個(gè)右鍵彈出菜單
popMenu->addAction(deleteNodeAction);
popMenu->addAction(reNameNodeAction);
popMenu->exec(QCursor::pos());
}
}
}
槽函數(shù)實(shí)現(xiàn)過程
重命名節(jié)點(diǎn):
void QMyTreeWidget::reNameNode()
{
qDebug() << "reNameNode" << endl;
QInputDialog dia(this);
dia.setWindowTitle("Input Dialog");
dia.setLabelText("Please input text:");
dia.setInputMode(QInputDialog::TextInput);//可選參數(shù):DoubleInput TextInput
if(dia.exec() == QInputDialog::Accepted)
{
qDebug() << dia.textValue();
curItem->setText(0,dia.textValue());
//更新數(shù)據(jù)庫
//curTtem->data dia.textValue()
}
}
刪除節(jié)點(diǎn):
void QMyTreeWidget::deleteNode()
{
qDebug() << "deleteNode" << endl;
//curItem->data() 當(dāng)前UUID 刪除該索引
QTreeWidgetItem* parent = curItem->parent();
if(parent == NULL)
{
//得到索引
int index = 0;
int count = this->topLevelItemCount();
for(int i = 0;i<count;i++)
{
//QTreeWidget
//ui->treeWidget->TopLevelItem()
QTreeWidgetItem* temp = this->topLevelItem(i);
if(curItem->data(0,Qt::UserRole).toString() == temp->data(0,Qt::UserRole).toString())
{
index = i;
}
}
this->takeTopLevelItem(index);
}
else
parent->removeChild(curItem);
//刪除數(shù)據(jù)庫一行
//curTtem->data
}
增加一個(gè)葉節(jié)點(diǎn),,該節(jié)點(diǎn)下不能再添加節(jié)點(diǎn)
void QMyTreeWidget::addTemplate()
{
qDebug() << "addTemplate" << endl;
//新節(jié)點(diǎn)ID 根 模板名稱 顏色值 是否目錄
//隨機(jī)UUID 當(dāng)前UUID title colors 0
//curItem->data() 當(dāng)前UUID
QTreeWidgetItem *temp=new QTreeWidgetItem(curItem);
QInputDialog dia(this);
dia.setWindowTitle("Input Dialog");
dia.setLabelText("Please input text:");
dia.setInputMode(QInputDialog::TextInput);//可選參數(shù):DoubleInput TextInput
if(dia.exec() == QInputDialog::Accepted)
{
qDebug() << dia.textValue();
temp->setText(0,dia.textValue());
QVariant var;
QString str = QUuid::createUuid().toString();
var.setValue(str);
temp->setData(0,Qt::UserRole,var);
}
}
添加一個(gè)目錄,,該節(jié)點(diǎn)可以有子節(jié)點(diǎn)
void QMyTreeWidget::addCategory()
{
qDebug() << "addCategory" << endl;
//在TEMPLATE表中新加一行
//新節(jié)點(diǎn)ID 根 模板名稱 顏色值 是否目錄
//隨機(jī)UUID 當(dāng)前UUID title colors 1
//curItem->data() 當(dāng)前UUID
QTreeWidgetItem *temp=new QTreeWidgetItem(curItem);
QInputDialog dia(this);
dia.setWindowTitle("Input Dialog");
dia.setLabelText("Please input text:");
dia.setInputMode(QInputDialog::TextInput);//可選參數(shù):DoubleInput TextInput
if(dia.exec() == QInputDialog::Accepted)
{
qDebug() << dia.textValue();
temp->setText(0,dia.textValue());
QVariant var;
QString str = QUuid::createUuid().toString();
var.setValue(str);
temp->setData(0,Qt::UserRole,var);
//數(shù)據(jù)庫添加一行
//UUID: str Title:dia.textValue()
}
}
刪除所有節(jié)點(diǎn):
void QMyTreeWidget::deleteAllNodes()
{
qDebug() << "deleteAllNodes" << endl;
this->clear();
//清空數(shù)據(jù)庫表
}
添加根節(jié)點(diǎn),在整個(gè)樹為空時(shí),,在任意位置點(diǎn)擊可以選擇
void QMyTreeWidget::addRootNode()
{
qDebug() << "addRootNode" << endl;
//在TEMPLATE表中新加一行
//新節(jié)點(diǎn)ID 根 模板名稱 顏色值 是否目錄
//隨機(jī)UUID -1 title colors 1
QTreeWidgetItem *temp=new QTreeWidgetItem(this);
QInputDialog dia(this);
dia.setWindowTitle("Input Dialog");
dia.setLabelText("Please input text:");
dia.setInputMode(QInputDialog::TextInput);//可選參數(shù):DoubleInput TextInput
if(dia.exec() == QInputDialog::Accepted)
{
qDebug() << dia.textValue();
temp->setText(0,dia.textValue());
QVariant var;
QString str = QUuid::createUuid().toString();
var.setValue(str);
temp->setData(0,Qt::UserRole,var);
}
}
初始化一個(gè)固定的樹
void QMyTreeWidget::initTreeWidget()
{
//初始化時(shí)從數(shù)據(jù)庫讀取當(dāng)前樹結(jié)構(gòu),,需要使用遞歸的方法
//initTreeWidget() 的實(shí)現(xiàn)
this->clear();
//ui->treeWidget->clear();
this->setHeaderHidden(true);
//ui->treeWidget->setHeaderHidden(true);
//第一組
QTreeWidgetItem *group1=new QTreeWidgetItem(this);
group1->setText(0,"group1");
QVariant var;
QString str = QUuid::createUuid().toString();
var.setValue(str);
group1->setData(0,Qt::UserRole,var);
QTreeWidgetItem *item11=new QTreeWidgetItem(group1);
item11->setText(0,"item11");
QTreeWidgetItem *newnew=new QTreeWidgetItem(item11);
newnew->setText(0,"newnew");
QTreeWidgetItem *item12=new QTreeWidgetItem(group1);
item12->setText(0,"item12");
QTreeWidgetItem *item13=new QTreeWidgetItem(group1);
item13->setText(0,"item13");
//第二組
QTreeWidgetItem *group2=new QTreeWidgetItem(this);
group2->setText(0,"group2");
QTreeWidgetItem *item21=new QTreeWidgetItem(group2);
item21->setText(0,"item21");
QTreeWidgetItem *item22=new QTreeWidgetItem(group2);
item22->setText(0,"item22");
QTreeWidgetItem *item23=new QTreeWidgetItem(group2);
item23->setText(0,"item23");
item23->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable);
item23->setCheckState(0,Qt::Unchecked);
}
至此,控件的自定義已經(jīng)完成,。這個(gè)demo可以實(shí)現(xiàn)右鍵操作,,并且控件結(jié)構(gòu)隨之變化。但是在程序重新啟動(dòng)后,,上次更改過的內(nèi)容全部丟失,。要解決問題,需要用數(shù)據(jù)庫保存樹結(jié)構(gòu),。這將在下一篇中進(jìn)行介紹,。預(yù)知后事如何,請聽下回分解,。
|