参考 https://wiki.qt.io/Install_Qt_5_on_Ubuntu .php
# 安装g++
sudo apt install build-essential # sudo apt install libfontconfig1 # 安装openGL支持 sudo apt install mesa-common-dev libglu1-mesa-dev
从ustc镜像直接下载安装包, 地址是 http://mirrors.ustc.edu.cn/qtproject/official_releases/qt/5.11/5.11.1/mysql
执行chmod u+x使其可执行linux
直接运行 qt-opensource-linux-x64-5.11.1.run, 会出现安装界面, 这一步能够配置网络代理, 往下进行须要填写本身在QT注册的帐户和口令, 会进行在线验证.git
设置安装目录时, 能够将路径配置到/opt下, 例如 /opt/qt/Qt5.11.1, 会在最后一步时弹出提示输入su口令.github
组件选择: 若是空间足够的话, 除了一个deprecated的之外, 都选上吧. 实测, 整个安装下来占地5.4GBredis
而后就能够在菜单中找到Qt Creator, 启动彷佛很是快, 一秒不到就打开界面了, 彻底不像一个数百MB的程序啊sql
在网上找了一个例子, 项目名叫First, 可是编译中出现了错误windows
$ gcc first.o all -o first gcc: error: all: No such file or directory
这个是由于文件名称first引发了歧义. 将项目名改成MyFirst就正常编译经过了.浏览器
The Makefile contains a generated (fake) target called "first" and it also contains a set of explicit and implied rules related to the source file "first.cpp". The make command builds your TARGET (which is named after the directory if not specified) and then decides that because "first.o" changed it must rebuild the "first" target: at this point it gets confused.
$ make /usr/bin/moc -DQT_NO_DEBUG -DQT_SQL_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4/QtSql -I/usr/include/qt4 -I. -I. first.cpp -o first.moc g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_SQL_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4/QtSql -I/usr/include/qt4 -I. -I. -o first.o first.cpp g++ -Wl,-O1 -Wl,-rpath,/usr/lib/qt4 -o simple_example first.o -L/usr/lib/qt4 -lQtSql -L/usr/lib/mysql -L/usr/lib/qt4 -lQtGui -L/usr/X11R6/lib -lQtCore -lgthread-2.0 -lrt -lglib-2.0 -lpthread gcc first.o all -o first // <<<<< confused, decides to rebuild "first" target using a file called "all" gcc: all: No such file or directory make: *** [first] Error 1
You get similar confusion (that make recovers from) if "TARGET = first" even with no files called "first.cpp".
$ make /usr/bin/moc -DQT_NO_DEBUG -DQT_SQL_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4/QtSql -I/usr/include/qt4 -I. -I. main.cpp -o main.moc g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_SQL_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4/QtSql -I/usr/include/qt4 -I. -I. -o main.o main.cpp make: Circular all <- first dependency dropped. // <<<< confused but recognises the situation g++ -Wl,-O1 -Wl,-rpath,/usr/lib/qt4 -o first main.o -L/usr/lib/qt4 -lQtSql -L/usr/lib/mysql -L/usr/lib/qt4 -lQtGui -L/usr/X11R6/lib -lQtCore -lgthread-2.0 -lrt -lglib-2.0 -lpthread
Adding
.PHONY = first
to Makefile fixes this problem but probably generates another if the TARGET = first. The change would be overwritten by qmake anyway. Easier to just avoid it.
F2 跳转到变量的声明, 切换函数的声明和实现
F4 在头文件和C文件之间切换
Ctrl + B 构建
Ctrl + R 运行
bogotobogo.com的这个教程不错, 值得推荐 http://www.bogotobogo.com/Qt/Qt_tutorial_list_New.php
来源是
http://www.bogotobogo.com/Qt/Qt5_QMainWindow_QAction_ImageViewer.php
http://www.bogotobogo.com/Qt/Qt5_QMainWindow_QAction_ImageViewer_B.php
其中的主要部分代码为
MenuWindow.pro
#------------------------------------------------- # # Project created by QtCreator 2018-08-27T14:02:44 # #------------------------------------------------- QT += core gui QT += printsupport greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = MenuWindow TEMPLATE = app # The following define makes your compiler emit warnings if you use # any feature of Qt which has been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ mainwindow.cpp \ mydialog.cpp HEADERS += \ mainwindow.h \ mydialog.h FORMS += \ mainwindow.ui \ mydialog.ui
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QLabel> #include <QScrollArea> #include <QMenu> #include <QAction> #include "mydialog.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void open(); void print(); void zoomIn(); void zoomOut(); void normalSize(); void fitToWindow(); void about(); void newWindow(); private: Ui::MainWindow *ui; MyDialog *myDialog; QLabel *imageLabel; QScrollArea *scrollArea; QMenu *fileMenu; QMenu *viewMenu; QMenu *helpMenu; QAction *openAct; QAction *printAct; QAction *newWindowAct; QAction *exitAct; QAction *zoomInAct; QAction *zoomOutAct; QAction *normalSizeAct; QAction *fitToWindowAct; QAction *aboutAct; QAction *aboutQtAct; double scaleFactor; void updateActions(); void scaleImage(double factor); void adjustScrollBar(QScrollBar *scrollBar, double factor); }; #endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QtWidgets> #include <QFileDialog> #include <QMessageBox> #ifndef QT_NO_PRINTER #include <QPrintDialog> #endif MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); openAct = new QAction(tr("&Open..."), this); openAct->setShortcut(tr("Ctrl+O")); printAct = new QAction(tr("&Print..."), this); printAct->setShortcut(tr("Ctrl+P")); printAct->setEnabled(false); newWindowAct = new QAction(tr("&New Window"), this); newWindowAct->setShortcut(tr("Ctrl+N")); exitAct = new QAction(tr("E&xit"), this); exitAct->setShortcut(tr("Ctrl+Q")); zoomInAct = new QAction(tr("Zoom &In (25%)"), this); zoomInAct->setShortcut(tr("Ctrl+=")); //(Ctrl)(+) zoomInAct->setEnabled(false); zoomOutAct = new QAction(tr("Zoom &Out (25%)"), this); zoomOutAct->setShortcut(tr("Ctrl+-")); //(Ctrl)(-) zoomOutAct->setEnabled(false); normalSizeAct = new QAction(tr("&Normal Size"), this); normalSizeAct->setShortcut(tr("Ctrl+S")); normalSizeAct->setEnabled(false); fitToWindowAct = new QAction(tr("&Fit to Window"), this); fitToWindowAct->setEnabled(false); fitToWindowAct->setCheckable(true); fitToWindowAct->setShortcut(tr("Ctrl+F")); aboutAct = new QAction(tr("&About"), this); aboutQtAct = new QAction(tr("About &Qt"), this); fileMenu = new QMenu(tr("&File"), this); fileMenu->addAction(openAct); fileMenu->addAction(printAct); fileMenu->addAction(newWindowAct); fileMenu->addSeparator(); fileMenu->addAction(exitAct); viewMenu = new QMenu(tr("&View"), this); viewMenu->addAction(zoomInAct); viewMenu->addAction(zoomOutAct); viewMenu->addAction(normalSizeAct); viewMenu->addSeparator(); viewMenu->addAction(fitToWindowAct); helpMenu = new QMenu(tr("&Help"), this); helpMenu->addAction(aboutAct); helpMenu->addAction(aboutQtAct); menuBar()->addMenu(fileMenu); menuBar()->addMenu(viewMenu); menuBar()->addMenu(helpMenu); imageLabel = new QLabel(this); imageLabel->setBackgroundRole(QPalette::Base); imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); imageLabel->setScaledContents(true); scrollArea = new QScrollArea(this); scrollArea->setBackgroundRole(QPalette::Dark); scrollArea->setWidget(imageLabel); setCentralWidget(scrollArea); setWindowTitle(tr("Image Viewer")); resize(500, 400); connect(openAct, SIGNAL(triggered()), this, SLOT(open())); connect(printAct, SIGNAL(triggered()), this, SLOT(print())); connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); connect(zoomInAct, SIGNAL(triggered()), this, SLOT(zoomIn())); connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(zoomOut())); connect(normalSizeAct, SIGNAL(triggered()), this, SLOT(normalSize())); connect(fitToWindowAct, SIGNAL(triggered()), this, SLOT(fitToWindow())); connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); connect(newWindowAct, SIGNAL(triggered(bool)), this, SLOT(newWindow())); } MainWindow::~MainWindow() { delete ui; } void MainWindow::open() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::currentPath()); if (!fileName.isEmpty()) { QImage image(fileName); if (image.isNull()) { QMessageBox::information(this, tr("Image Viewer"), tr("Cannot load %1.").arg(fileName)); return; } imageLabel->setPixmap(QPixmap::fromImage(image)); scaleFactor = 1.0; printAct->setEnabled(true); fitToWindowAct->setEnabled(true); updateActions(); if (!fitToWindowAct->isChecked()) imageLabel->adjustSize(); } } void MainWindow::print() { } void MainWindow::newWindow() { myDialog = new MyDialog(this); myDialog->resize(300, 200); /*myDialog->setModal(true); myDialog->exec();*/ myDialog->show(); } void MainWindow::zoomIn() { scaleImage(1.25); } void MainWindow::zoomOut() { scaleImage(0.8); } void MainWindow::normalSize() { imageLabel->adjustSize(); scaleFactor = 1.0; } void MainWindow::fitToWindow() { bool fitToWindow = fitToWindowAct->isChecked(); scrollArea->setWidgetResizable(fitToWindow); if (!fitToWindow) { normalSize(); } updateActions(); } void MainWindow::about() { QMessageBox::about(this, tr("About Image Viewer"), tr("<b>Image Viewer</b> example.")); } void MainWindow::updateActions() { zoomInAct->setEnabled(!fitToWindowAct->isChecked()); zoomOutAct->setEnabled(!fitToWindowAct->isChecked()); normalSizeAct->setEnabled(!fitToWindowAct->isChecked()); } void MainWindow::scaleImage(double factor) { Q_ASSERT(imageLabel->pixmap()); scaleFactor *= factor; imageLabel->resize(scaleFactor * imageLabel->pixmap()->size()); adjustScrollBar(scrollArea->horizontalScrollBar(), factor); adjustScrollBar(scrollArea->verticalScrollBar(), factor); zoomInAct->setEnabled(scaleFactor < 3.0); zoomOutAct->setEnabled(scaleFactor > 0.333); } void MainWindow::adjustScrollBar(QScrollBar *scrollBar, double factor) { int value = scrollBar->value(); int step = scrollBar->pageStep(); value = int(factor * value + ((factor - 1) * step/2)); scrollBar->setValue(value); }
结合Qt5中对于QScrollBar的说明, 对void MainWindow::scaleImage(double factor) 和 void MainWindow::adjustScrollBar(QScrollBar *scrollBar, double factor) 这两个方法进行分析.
a. 这是滚动条的滑块, 提供了快速的定位, 可是不能提供精确的定位
b. 箭头按钮用于精确地浏览定位, 对于文本编辑器, 典型的步长是一行, 而对于图片则是20像素.
c. 翻页控制部分, 点击的效果等价于一次翻页, 正常状况下, 一个翻页对于scrollbar的value的变化等于滑块的长度.
每一个滚动条有一个value字段, 用于标示此滑块距离滚动条起点的距离. 这个值必定是介于 minimum() 和 maximum() 之间. 在 minimum value和 maximum value时, 滑块的位置分别如上图所示. 因此要注意, 这个滚动条的长度并不是等于maximum().
由此可知, 对于前面例子中图片放大factor倍时, 对于scrollbar.value:
1. 若是value不变, 那么当图片放大时, 由于maximum value增大, 故滑块一边缩小, 一边位置会往起始方向收缩
2. 若是value与factor进行同等比例的放大, 那么滑块一边缩小, 一边起始边界的位置保持不变
3. 若是value与factor进行同等比例放大的同时, 增长滑块长度(pageStep)的收缩长度, 那么滑块一边缩小, 一边中心位置保持不变. 因此上面adjustScrollBar()方法的处理为
value = int(factor * value + ((factor - 1) * step/2));
经常使用的Layout主要有如下几种
而在主窗口中, 经常使用的底层布局并不是Layout, 而是QSplitter. 这个自带了对内部widget的自动拉伸, 以及分隔栏的鼠标拖动调整,很是方便. 能够参考Redis Desktop Manager的布局设计(是qml格式的, 可是能够看出来布局的设计) https://github.com/uglide/RedisDesktopManager/blob/0.9/src/qml/app.qml
import QtQuick 2.0 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.1 import QtQuick.Dialogs 1.2 import QtQml.Models 2.2 import QtQuick.Window 2.2 import Qt.labs.settings 1.0 import "." import "./common" import "./common/platformutils.js" as PlatformUtils import "./value-editor" import "./connections-tree" import "./console" import "./server-info" import "./bulk-operations" ApplicationWindow { id: approot visible: true objectName: "rdm_qml_root" title: "Redis Desktop Manager " + Qt.application.version width: 1100 height: 800 property double wRatio : (width * 1.0) / (Screen.width * 1.0) property double hRatio : (height * 1.0) / (Screen.height * 1.0) property var currentValueFormatter Component.onCompleted: { if (hRatio > 1 || wRatio > 1) { console.log("Ratio > 1.0. Resize main window.") width = Screen.width * 0.9 height = Screen.height * 0.8 } if (PlatformUtils.isOSXRetina(Screen)) { bottomTabView.implicitHeight = 100 } } Settings { category: "windows_settings" property alias x: approot.x property alias y: approot.y property alias width: approot.width property alias height: approot.height } SystemPalette { id: sysPalette } FontLoader { id: monospacedFont Component.onCompleted: { source = "qrc:/fonts/Inconsolata-Regular.ttf" } } QuickStartDialog { id: quickStartDialog objectName: "rdm_qml_quick_start_dialog" width: PlatformUtils.isOSX() ? 600 : approot.width * 0.8 } GlobalSettings { id: settingsDialog } ConnectionSettignsDialog { id: connectionSettingsDialog objectName: "rdm_connection_settings_dialog" onTestConnection: { if (connectionsManager.testConnectionSettings(settings)) { hideLoader() showMsg(qsTr("Successful connection to redis-server")) } else { hideLoader() showError(qsTr("Can't connect to redis-server")) } } onSaveConnection: connectionsManager.updateConnection(settings) } MessageDialog { id: notification objectName: "rdm_qml_error_dialog" visible: false modality: Qt.WindowModal icon: StandardIcon.Warning standardButtons: StandardButton.Ok function showError(msg) { icon = StandardIcon.Warning text = msg open() } function showMsg(msg) { icon = StandardIcon.Information text = msg open() } } BulkOperationsDialog { id: bulkOperationDialog } Connections { target: bulkOperations onOpenDialog: { bulkOperationDialog.operationName = operationName bulkOperationDialog.open() } } Connections { target: connectionsManager onEditConnection: { connectionSettingsDialog.settings = config connectionSettingsDialog.open() } onError: { notification.showError(err) } Component.onCompleted: { if (connectionsManager.size() == 0) quickStartDialog.open() } } toolBar: AppToolBar {} BetterSplitView { anchors.fill: parent orientation: Qt.Horizontal BetterTreeView { id: connectionsTree Layout.fillHeight: true Layout.minimumWidth: 350 Layout.minimumHeight: 500 } BetterSplitView { orientation: Qt.Vertical BetterTabView { id: tabs objectName: "rdm_qml_tabs" currentIndex: 0 Layout.fillHeight: true Layout.fillWidth: true Layout.minimumWidth: 650 Layout.minimumHeight: 30 onCurrentIndexChanged: { if (tabs.getTab(currentIndex).tabType) { if (tabs.getTab(currentIndex).tabType == "value") { var realIndex = currentIndex - serverStatsModel.tabsCount(); if (welcomeTab) { realIndex -= 1 } viewModel.setCurrentTab(realIndex); } else if (tabs.getTab(currentIndex).tabType == "server_info") { var realIndex = currentIndex; if (welcomeTab) { realIndex -= 1 } serverStatsModel.setCurrentTab(index); } } } WelcomeTab { id: welcomeTab clip: true objectName: "rdm_qml_welcome_tab" property bool not_mapped: true onClose: tabs.removeTab(index) function closeIfOpened() { var welcomeTab = tabs.getTab(0) if (welcomeTab && welcomeTab.not_mapped) tabs.removeTab(0) } } ServerInfoTabs { model: serverStatsModel } Connections { target: serverStatsModel onRowsInserted: if (welcomeTab) welcomeTab.closeIfOpened() } ValueTabs { objectName: "rdm_qml_value_tabs" model: valuesModel } AddKeyDialog { id: addNewKeyDialog objectName: "rdm_qml_new_key_dialog" width: approot.width * 0.7 height: { if (approot.height > 500) { return approot.height * 0.7 } else { return approot.height } } } Connections { target: valuesModel onKeyError: { if (index != -1) tabs.currentIndex = index notification.showError(error) } onRowsInserted: { if (welcomeTab) welcomeTab.closeIfOpened() } onNewKeyDialog: addNewKeyDialog.open() } } BetterTabView { id: bottomTabView Layout.fillWidth: true Layout.minimumHeight: PlatformUtils.isOSXRetina()? 15 : 30 tabPosition: Qt.BottomEdge Consoles { objectName: "rdm_qml_console_tabs" model: consoleModel } Connections { target: consoleModel onChangeCurrentTab: { bottomTabView.currentIndex = i + 1 } } BetterTab { closable: false title: "Log" icon: "qrc:/images/log.svg" BaseConsole { id: logTab readOnly: true textColor: "#6D6D6E" Connections { target: appLogger onEvent: logTab.append(msg) Component.onCompleted: appLogger.getMessages() } } } } } } }
1. 设计好全局的QAction, 这个能够被顶栏菜单QMenuBar, 工具栏QToolBar 以及 右键弹出菜单QMenu引用.
2. 设计好主窗体布局和主要widget
3. 开始制做各类slot, 而且将QAction connect到对应的slot上, 这一步须要开始设计各个弹出的QDialog
一个未完成的主窗口例子
rockredismain.h
#ifndef ROCKREDISMAIN_H #define ROCKREDISMAIN_H #include <QMainWindow> #include <QSplitter> #include <QTreeWidget> #include <QTableWidget> #include <QMenuBar> #include <QMenu> #include <QAction> #include <QToolBar> #include <QLabel> #include <QScrollArea> #include <QVBoxLayout> namespace Ui { class RockRedisMain; } class RockRedisMain : public QMainWindow { Q_OBJECT public: explicit RockRedisMain(QWidget *parent = 0); ~RockRedisMain(); private slots: void onCustomContextMenuRequested(const QPoint& point); void showContextMenu(QTreeWidgetItem* item, const QPoint& globalPos); void onCurrentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); private: Ui::RockRedisMain *ui; QSplitter * splitter; QScrollArea *scrollArea; QTreeWidget *treeWidget; QTableWidget* tableWidget; QMenu *fileMenu; QMenu *helpMenu; QToolBar *fileToolBar; QAction *newConnectionAct; QAction *editConnectionAct; QAction *removeConnectionAct; QAction *exitAct; QAction *aboutAct; void initMenu(); QTreeWidgetItem* addTreeNode(QString name, QString description); QTreeWidgetItem* addChildNode(QTreeWidgetItem *parent, QString name, QString description); }; #endif // ROCKREDISMAIN_H
rockredismain.cpp
#include "rockredismain.h" #include "ui_rockredismain.h" RockRedisMain::RockRedisMain(QWidget *parent) : QMainWindow(parent), ui(new Ui::RockRedisMain) { ui->setupUi(this); initMenu(); splitter = new QSplitter(Qt::Horizontal, this); setCentralWidget(splitter); treeWidget = new QTreeWidget(); treeWidget->setHeaderHidden(true); //treeWidget->setHeaderLabels(QStringList() << "Connections"); treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); QTreeWidgetItem* item = addTreeNode("Folder", "It's a folder"); addChildNode(item, "A Connection Folder", "first"); addChildNode(item, "B Connection File", "second"); item = addTreeNode("File", "it's a file"); addChildNode(item, "A", "first"); addChildNode(item, "B", "second"); addTreeNode("Folder", "It's a folder"); addTreeNode("File", "it's a file"); addTreeNode("Folder", "It's a folder"); addTreeNode("File", "it's a file"); addTreeNode("Folder", "It's a folder"); addTreeNode("File", "it's a file"); treeWidget->resize(100, 100); splitter->addWidget(treeWidget); tableWidget = new QTableWidget(); splitter->addWidget(tableWidget); splitter->setStretchFactor(1,1); setWindowTitle(tr("RockRedis")); resize(800, 600); connect(treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomContextMenuRequested(QPoint))); connect(treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(onCurrentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*))); } RockRedisMain::~RockRedisMain() { delete ui; } void RockRedisMain::initMenu() { fileMenu = new QMenu(tr("&File"), this); helpMenu = new QMenu(tr("&Help"), this); menuBar()->addMenu(fileMenu); menuBar()->addMenu(helpMenu); newConnectionAct = new QAction(tr("&New Connection"), this); newConnectionAct->setShortcut(tr("Ctrl+N")); newConnectionAct->setStatusTip(tr("Add a new connection")); //connect(newConnectionAct, SIGNAL(triggered()), this, SLOT(open())); fileMenu->addAction(newConnectionAct); editConnectionAct = new QAction(tr("&Edit Connection"), this); editConnectionAct->setShortcut(tr("Alt+E")); editConnectionAct->setEnabled(false); //connect(editConnectionAct, SIGNAL(triggered()), this, SLOT(print())); fileMenu->addAction(editConnectionAct); removeConnectionAct = new QAction(tr("Remove Connection"), this); editConnectionAct->setShortcut(tr("Del")); removeConnectionAct->setEnabled(false); //connect(newWindowAct, SIGNAL(triggered(bool)), this, SLOT(newWindow())); fileMenu->addAction(removeConnectionAct); exitAct = new QAction(tr("E&xit"), this); exitAct->setShortcut(tr("Ctrl+Q")); connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); fileMenu->addAction(exitAct); aboutAct = new QAction(tr("&About"), this); helpMenu->addAction(aboutAct); } QTreeWidgetItem* RockRedisMain::addTreeNode(QString name, QString description) { QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget, 0); item->setText(0, name); item->setText(1, description); return item; } QTreeWidgetItem* RockRedisMain::addChildNode(QTreeWidgetItem *parent, QString name, QString description) { QTreeWidgetItem *item = new QTreeWidgetItem(parent, 1); item->setText(0, name); item->setText(1, description); return item; } void RockRedisMain::onCustomContextMenuRequested(const QPoint& point) { QTreeWidgetItem* item = treeWidget->itemAt(point); if (item) { // Note: We must map the point to global from the viewport to // account for the header. showContextMenu(item, treeWidget->mapToGlobal(point)); } } void RockRedisMain::showContextMenu(QTreeWidgetItem* item, const QPoint& point) { QMenu menu; switch (item->type()) { case 0: menu.addAction(editConnectionAct); menu.addAction(removeConnectionAct); break; case 1: menu.addAction("This is a type 2"); break; } menu.exec(point); } void RockRedisMain::onCurrentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) { if (current == NULL) { editConnectionAct->setEnabled(false); removeConnectionAct->setEnabled(false); } else if (current->type() == 0) { editConnectionAct->setEnabled(true); removeConnectionAct->setEnabled(true); } else { editConnectionAct->setEnabled(false); removeConnectionAct->setEnabled(false); } }