Ubuntu18.04中配置QT5.11開發環境

準備工做

參考 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就正常編譯經過了.瀏覽器

緣由說明 https://www.qtcentre.org/threads/32635-compilation-errors-quot-gcc-all-No-such-file-or-directory-quot bash

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.

Qt5經常使用快捷鍵

F2  跳轉到變量的聲明, 切換函數的聲明和實現
F4  在頭文件和C文件之間切換
Ctrl + B 構建
Ctrl + R 運行

Qt5教程

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);
}

QScrollBar組件

結合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));

Qt5的Layout

經常使用的Layout主要有如下幾種

  • QHBoxLayout 將組件按水平進行佈局, 寬度平均分割, 高度佔滿外框
  • QVBoxLayout 將組件按垂直進行佈局, 高度平均分割, 寬度佔滿外框
  • QGridLayout 將組件按表格進行佈局, 相似於HTML中的table tr td, 能夠水平跨列, 也能夠垂直跨行
  • QFormLayout 用於有多個成對的標籤+輸入的界面, 能夠設置不一樣風格以及在寬帶不足時自動換行
  • QGraphicsAnchorLayout 用於在組件上添加錨點, 並以錨點的相對位置來控制佈局
  • QStackedLayout 對於內部的組件, 每次只顯示一個(能夠用於製做標籤頁)

而在主窗口中, 經常使用的底層佈局並不是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()
                        }
                    }
                }
            }
        }
    }
}

Qt界面程序編寫的步驟

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);
    }
}
相關文章
相關標籤/搜索