Qt学习笔记(一)

来源:互联网 发布:阿里云勒索病毒查杀 编辑:程序博客网 时间:2024/05/16 10:06

1、在Qt Creator中创建项目,当工程更改之后,要注意经常qmake以保持最新状态。

2、qt中的信号和槽  signal  slot

任何使用 信号和槽的类在头文件中必须 进行Q_OBJECT 宏声明。

槽和普通的C++成员函数几乎是一样的----可以是虚函数;可以被重载;可以是公有的、保护的或者私有的,并且也可以被其他C++成员函数直接调用;他们的参数可以是任意类型。唯一不同的是:槽还可以和信号直接连接在一起,在这种情况下,每当发送这个信号的时候,就会自动调用这个槽函数。

connect(sender,  SIGNAL(signal), receiver, SLOT(slot));

sender 和 receiver都是QObject的指针,signal和slot是不带参数的函数名。实际上SIGNAL和SLOT宏会将他们的参数转换成相应的字符串。

 connect(lineEdit, SIGNAL(textChanged(const QString&)),  this, SLOT(enableFindButton(const QString&)));
(1)一个信号可以连接多个槽,当发射这个信号的时候,会以不确定的顺序一个接一个的调用这些槽。

connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int)));

connect(slider, SIGNAL(valueChanged(int)), this, SLOT(updateStatusBarIndicator(int)));

(2)多个信号可以连接同一个槽,无论发射的是哪一个信号都会调用这个槽

connect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError());

connect(calculator, SIGNAL(divisionByZero()), this, SLOT(handleMathError()));

(3)一个信号可以与另外一个信号相连接,当发射第一个信号时,也会发射第二个信号。除此之外,信号与信号之间的连接和信号与槽之间的连接是难以区分的。

connect(lineEdit, SIGNAL(textChanged(const QString&), this, SIGNAL(updateRecord(const QString&)));

(4)连接可以被移除, 这种情况较少用到,因为当删除对象时,Qt会自动移除和这个对象相关的所有连接。

disconnect(lcd, SIGNAL(overflow()), this,  SLOT(handleMathError()));

要把信号成功连接到槽(或者连接到另外一个信号),他们的参数必须具有相同的顺序和相同的类型:

connect(ftp, SIGNAL(rawCommandReply(int, QString&)), this,  SLOT(processReply(int, QString&)));

如果参数类型不匹配,或者如果信号或槽不存在,则当应用程序使用调试模式构建后,qt会在运行时发出警告。如果在信号和槽的名字中包含了参数名,qt也会发出警告。


3、Qt类的构造函数,如在类GotoCellDialog中, QRetExpValidator qREV = new QRetExpValidator(regExp, this);//将参数this传递给QRegExpValidator的构造函数,使它成为GotoCellDialog对象的一个子对象。这样以后就不用担心有关删除QRegExpValidator的事情了:当删除它的父对象时,它也会被自动删除。

Qt的父子对象机制是在QObject中实现的。当利用一个父对象创建一个子对象(一个窗口部件、一个检查器或者是任意其他类型)时,父对象会把这个子对象添加到自己的子对象列表中。当删除这个父对象时,它会遍历子对量列表并且删除每一个子对象。然后这些子对象再去删除它们自己所包含的每个子对象。如此反复递归调用,直至清空所有子对象为止。这种父子对象机制可在很大程度上简化内存管理操作,降低内存泄露的风险。需要明确删除的对象是那些使用new创建出来的并且没有父对象的对象。并且,如果再删除一个父对象之前先删除了它的子对象,Qt会自动从它的父对象的子对象列表中将其移除。

对于窗口部件,父对象还有另外一层含义:子窗口部件会显示在它的父对象所在的区域中。当删除这个父窗口的部件时,不仅子对象会从内存中消失,而且它也会在屏幕上消失。


4、动态对话框

动态对话框就是在程序运行的时候使用的Qt设计师的.ui文件创建而来的那些对话框,动态对话框不需要通过uic将.ui文件转换成c++代码,相反,它是在程序运行时使用QUiLoader类载入该文件的,例如:

QUiLoader  uiLoader;

QFile  file("sortdialog.ui");

QWidget *sortDialog = uiLoader.load(&file);

if (sortDialog){

}

可以使用 QObject::findChild<T>()来访问这个窗体中的各个子窗口部件:

QComboBox * primaryCombo = sortDialog->findChild<QComboBox*> ("primaryCombo");

if (primaryCombo){

}

这里的findChild<T>()函数是一个模板成员函数,它可以返回与给定的名字和类型相匹配的子对象。

QUiLoader类放在一个独立的库中,为了在Qt应用程序中使用QUiLoader,必须在这个应用程序的.pro文件中加入这一行: CONFIG += uitools

动态对话框使得不重新编译应用程序而可以改变窗体布局。动态对话框也同样可用于创建小型终端应用程序,这些程序只有一个内置的前端窗体,并且只是在需要的时候才会去创建所有的其他窗体。


5、内置的窗口部件类和对话框类

Qt提供了一整套内置的窗口部件和常用对话框,这可以满足绝大多数情况。如 QMenuBar, QStatusBar, QToolBar ,QSplitter等。

Qt按钮有 QPushButton, QToolButton, QCheckButton, QRadioButton等四类。

Qt的容器窗口部件是一种可以包含其他窗口部件的窗口部件。QFrame也可以用于它自身,这只是为了画一条直线,它也可以用作许多其他窗口部件的基类,如QToolBox和QLabel。

QTabWidget和QToolBox是多页窗口部件。在多页窗口中,每一页都是一个子窗口部件,并从0开始编号这些页。对于一个QTabWidget,它的每一个Tab标签的形状和位置都可以设置。


6、图形用户界面程序通常会使用许多图片,为应用程序提供图片方法很多,常用的一种为:

(1)把图片保存到文件中,并且在运行时载入他们

(2)把XPM文件包含在源代码中(这一方法之所以可行,是因为XPM文件也是有效的C++文件)

(3)使用Qt的资源机制

Qt的资源机制方法比运行时载入文件更为方便,并且该方法适用于所支持的任意文件格式。为了使用Qt的资源系统,必须创建一个资源文件,并且在识别该资源文件的.pro文件中添加一行代码。  RESOURCES = spreadsheet.qrc

资源文件自身使用了XML形式,所有的资源文件都会被编译到应用程序的可执行文件中,因此并不会弄丢他们。当引用这些资源时,需要使用带路径前缀:/(冒号前缀)的形式。


7、创建菜单和工具栏

Qt通过“动作”(Action)的概念简化了有关菜单和工具栏的编程。一个动作就是一个可以添加到任意数量的菜单和工具栏上的项。在Qt中创建菜单和工具栏包括以下步骤:

(1)创建并且设置动作

(2)创建菜单栏并且把动作添加到菜单上

(3)创建工具栏并且把动作添加到工具栏上

    newAction = new QAction(tr("&New"), this);
    newAction->setIcon(":/images/new.png");
    newAction->setShortcut(QKeySequence::New);
    newAction->setStatusTip("Create a new spreadsheet file");
    connect(newAction, SIGNAL(triggered()), this, SLOT(newFile()));

8、任意Qt窗口部件都可以有一个与之相关的QActions列表,要为应用程序提供一个上下文菜单,可以将所需要的动作添加到该窗口部件中,并且将该窗口部件的上下文菜单策略设置为一个现实这些动作的上下文菜单。当用户在一个窗口部件上右键,或者键盘上按下一个与平台相关的按键时,就可以激活这些上下文菜单。

spreadsheet->addAction(cutAction);

spreadsheet->addAction(copyAction);

spreadsheet->addAction(pasteAction);

spreadsheet->setContextMenuPolicy(Qt::ActionsContextMenu);

一种更为高级的提供上下文菜单的方法是重新实现QWidget::contextMenuEvent()函数,创建一个QMenu窗口部件,在其中添加那些动作,并且再对该窗口部件调用exec函数。


9、QMainWindow::statusBar()函数返回一个指向状态栏的指针【在第一次调用statusBar()函数的时候会创建状态栏】

void MainWindow::createStatusBar()
{
    locationLabel = new QLabel(" W999 ");//先设定文本{最大情形,用于确定合适的大小}
    locationLabel->setAlignment(Qt::AlignHCenter);//设置居中对齐
    locationLabel->setMinimumSize(locationLabel->sizeHint());//设置合适的大小, sizeHint(布局管理中的控件默认尺寸,如果控件不在布局管理中就为无效的值
    formulaLabel = new QLabel;
    formulaLabel->setIndent(3);//设置缩进
    statusBar()->addWidget(locationLabel);
    statusBar()->addWidget(formulaLabel, 1);//第二个参数为伸展因子,表示可以伸展,默认没有为0,表示不能伸展
    connect(spreadsheet, SIGNAL(currentCellChanged(int, int, int, int)),
            this, SLOT(updateStatusBar()));
    connect(spreadsheet, SIGNAL(modified()),
            this, SLOT(spreadsheetModified()));
    updateStatusBar();
}


10、QMessageBox用于提供简单的对话框,有 waring(), informatioin(), quesion(), critical()等形式。

 if (isWindowModified()) {
        int r = QMessageBox::warning(this, tr("Spreadsheet"),
                        tr("The document has been modified.\n"
                           "Do you want to save your changes?"),
                        QMessageBox::Yes | QMessageBox::No
                        | QMessageBox::Cancel);
        if (r == QMessageBox::Yes) {
            return save();
        } else if (r == QMessageBox::Cancel) {
            return false;
        }
上例中,QMessageBox::warning()语法提供:QMessageBox::warning(parent, title, message, buttons);


11、Qt中现成的Dialog,比如QFileDialog

 QString fileName = QFileDialog::getOpenFileName(this,
                                   tr("Open Spreadsheet"), ".",
                                   tr("Spreadsheet files (*.sp)"));
        if (!fileName.isEmpty())
            loadFile(fileName);
上例中,QFileDialog::getOpenFileName()静态函数获得选择打开的文件名,这个函数会弹出一个对话框,让用户选择一个文件,并且返回这个文件名---或者,如果用户单击了Cancel按钮,则返回一个字符串。

传递给QFileDialog::getOpenFileName的第一个参数为父窗口部件,用于对话框和其他窗口部件的这种父子对象关系意义并不相同。对话框通常拥有自主权,但是如果他有父对象,那么在默认情况下,它就会居中放到父对象上。一个子对话框也会共用他的父对象的任务栏。

第二个参数是这个对话框应当使用的标题。第三个参数是从哪一级目录开始查找,上例中'.'表示从当前目录开始。第四个参数指定了文件过滤器。文件过滤器由一个描述文本和一个通配符组成。


QFileDialog:::getSaveFileName(this, tr("Save SpreadSheet"), ".", tr("Spreadsheet files(*.sp)"));

该函数对话框,会要求用户确认是否覆盖文件。


12、

void MainWindow::openRecentFile()
{
    if (okToContinue()) {
        QAction *action = qobject_cast<QAction *>(sender());
        if (action)
            loadFile(action->data().toString());
    }
}

上例中,使用QObject:::sender()可以查出是哪个特有动作调用了槽。qobject_cast<T>()函数可以在Qt的moc(meta-object compiler, 元对象编辑器)所生成的元信息基础上执行动态类型强制转换(dynamic cast)。它返回一个指向所需的QObject子类的指针,或者是在该对象不能被转换成所需的那种类型时返回0。与标准C++的dynamic_cast<T>不同,Qt的qobject_cast<T>可正确的跨越动态库边界。当然如果确切知道转换的目的类型和需要转换的对象的实际类型相同,则也可以直接使用static_cast<T>或者传统的C风格的数据类型强制转换代替原有的数据转换方式。


13、对话框有模态和非模态之分,和MFC中类似。

void MainWindow::find()
{
    if (!findDialog) {
        findDialog = new FindDialog(this);
        connect(findDialog, SIGNAL(findNext(const QString &,
                                            Qt::CaseSensitivity)),
                spreadsheet, SLOT(findNext(const QString &,
                                           Qt::CaseSensitivity)));
        connect(findDialog, SIGNAL(findPrevious(const QString &,
                                                Qt::CaseSensitivity)),
                spreadsheet, SLOT(findPrevious(const QString &,
                                               Qt::CaseSensitivity)));
    }
    findDialog->show();
    findDialog->raise();
    findDialog->activateWindow();
}
上例为非模态对话框的例子,调用show(), raise(), activeWindow()来确保窗口位于其他窗口之上并且是可见的和激活的。只调用show()就可以让一个隐藏的窗口变为可见的、位于最上方的和激活的,但是也有可能是在对话框已经是可见的时候又再次调用了它,在这种情况下,show()调用可能什么也不做,那么就必须使用raise()和activeWindow()来让对话框位于最上方且激活。

void MainWindow::sort()
{
    SortDialog dialog(this);
    QTableWidgetSelectionRange range = spreadsheet->selectedRange();
    dialog.setColumnRange('A' + range.leftColumn(),
                          'A' + range.rightColumn());
dialog.exec();
}
上例中使用模态对话框,通过exec()函数来实现。【如果对话框使用show(),则为非模态对话框,如果使用exec(),则为模态对话框】


14、应用程序中经常需要存储程序的一些设置,可以使用QSettings实现。

默认情况下,QSettings会存储应用程序中与特定平台相关的一些设置信息。如在Windows系统中使用注册表,在unix系统中它把设置信息存储在文本文件中。QSettings把设置信息存储为键值对的形式

void MainWindow::writeSettings()
{
    QSettings settings("Software Inc.", "Spreadsheet");
    settings.setValue("geometry", saveGeometry());
    settings.setValue("recentFiles", recentFiles);
    settings.setValue("showGrid", showGridAction->isChecked());
    settings.setValue("autoRecalc", autoRecalcAction->isChecked());
}

void MainWindow::readSettings()
{
    QSettings settings("Software Inc.", "Spreadsheet");
    restoreGeometry(settings.value("geometry").toByteArray());
    recentFiles = settings.value("recentFiles").toStringList();
    updateRecentFileActions();
    bool showGrid = settings.value("showGrid", true).toBool();
    showGridAction->setChecked(showGrid);
    bool autoRecalc = settings.value("autoRecalc", true).toBool();
    autoRecalcAction->setChecked(autoRecalc);
}

15、当用户关闭一个主窗口时,默认行为是隐藏它,所以他还会存在内存中。如果需要关闭之后,彻底释放内存。则可以

MainWindow::MainWindow()

{

.....

setAttribute(Qt::WA_DeleteOnClose);

......

}


16、程序启动界面

使用QSplashScreen类来实现,类QSplashScreen会在应用程序的主窗口出现之前显示一个图片,它可以在这个图片上显示一些信息,用来通知用户有关程序初始化的过程。通常程序启动画面的代码会放在main()函数中,位于QApplication::exec()调用之前。下例显示程序启动画面,表示启动时载入一些模块和网络连接的建立:

int main(int argc,  char* argv[])
{
QApplication app(argc, argv);
QSplashScreen *splash = new QSplashScreen;
    splash->setPixmap(QPixmap(":/images/splash->png"));
    splash->show();
    
    Qt::Alignment topRight = Qt::AlignRight|Qt::AlignTop;
    splash->showMessage(QObject::tr("Setting up the main window..."), 
                        topRight, Qt::white);
    
    MainWindow mainWindow;
    
    splash->showMessage(QObject::tr("Loading modules..."), topRight, Qt::white);
    loadModules();
    splash->showMessage(QObject::tr("Establishing connections..."), topRight, Qt::white);
    establishConnections();
    
    mainWindow.show();
    splash->finish(&mainWindow);
    delete splash;
    
    return app.exec();
}
    

17、中央窗口部件

QMainWindow的中央区域可以被任意类的窗口部件所占用:

(1)使用一个标准的Qt窗口部件

向QTableWidget或者QTextEdit这样的标准窗口部件可以用作中央窗口部件。在这种情况下,这个应用程序的功能,如文件的载入和保存等,必须在其他的地方实现。

(2)使用一个自定义的窗口部件

特殊的应用程序通常需要在自定义的窗口部件见中显示数据

(3)使用一个带布局管理器的普通QWidget

有时应用程序的中央区域会被许多窗口部件所占用。这时可以通过使用一个作为所有这些其他窗口部件父对象的QWidget,以及通过使用布局管理器管理这些子窗口部件的大小和位置来完成这一特殊情况。

(4)使用切分窗口

多个窗口部件一起使用的另一种方法是使用QSplitter。QSplitter会在水平或竖直方向上排列它的子窗口部件,用户可以使用切分条(splitter handle)来控制他们的尺寸大小。切分窗口可以包含所有类型的窗口部件,包括其他切分窗口。

(5)使用多文档界面工作空间

如果应用程序使用的是多文档界面,那么他的中央区域会被QMdiArea窗口部件占据,并且每个多文档窗口界面都是他的一个子窗口部件。


18、Qt中文件载入和保存

下例中使用自定义的二进制格式实现文件的载入和保存。使用QFile和QDataStream来实现,由他们共同提供与平台无关的二进制数据的输入/输出接口。

//文件的写入

bool Spreadsheet::writeFile(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly)) {
        QMessageBox::warning(this, tr("Spreadsheet"),
                             tr("Cannot write file %1:\n%2.")
                             .arg(file.fileName())
                             .arg(file.errorString()));
        return false;
    }
    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_4_3);
    out << quint32(MagicNumber);
    QApplication::setOverrideCursor(Qt::WaitCursor);
    for (int row = 0; row < RowCount; ++row) {
        for (int column = 0; column < ColumnCount; ++column) {
            QString str = formula(row, column);
            if (!str.isEmpty())
                out << quint16(row) << quint16(column) << str;
        }
    }
    QApplication::restoreOverrideCursor();
    return true;
}
//文件的读取
bool Spreadsheet::readFile(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly)) {
        QMessageBox::warning(this, tr("Spreadsheet"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(file.fileName())
                             .arg(file.errorString()));
        return false;
    }


    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_4_3);


    quint32 magic;
    in >> magic;
    if (magic != MagicNumber) {
        QMessageBox::warning(this, tr("Spreadsheet"),
                             tr("The file is not a Spreadsheet file."));
        return false;
    }
    clear();
    quint16 row;
    quint16 column;
    QString str;
    QApplication::setOverrideCursor(Qt::WaitCursor);
    while (!in.atEnd()) {
        in >> row >> column >> str;
        setFormula(row, column, str);
    }
    QApplication::restoreOverrideCursor();
    return true;
}

19、函数对象(仿函数)。

对象中实现了()操作符,这样允许将这个类像函数一样使用

class Square
{
    public:
int operator()(int x) const
{
return x*x;
}
}
Square square;
int y = square(5);//即相当于调用平方函数,结果y = 25

//比较函数的例子

bool SpreadsheetCompare::operator()(const QStringList &row1,
                                    const QStringList &row2) const
{
    for (int i = 0; i < KeyCount; ++i) {
        int column = keys[i];
        if (column != -1) {
            if (row1[column] != row2[column]) {
                if (ascending[i]) {
                    return row1[column] < row2[column];
                } else {
                    return row1[column] > row2[column];
                }
            }
        }
    }
    return false;
}
如果前一个元素比后一个元素小,则返回true,否则返回false,则最后形成升序排列。


20、创建自定义窗口部件

通过对一个已经存在的Qt窗口部件进行子类化或者直接对QWidget进行子类化,就可以创建自定义窗口部件。自定义的窗口部件也可以放在Qt 设计师中进行可视化调用。

例子1:通过子类化已经存在的Qt窗口部件进行自定义窗口部件。

#ifndef HEXSPINBOX_H
#define HEXSPINBOX_H
#include <QSpinBox>
class HexSpinBox : public QSpinBox
{
    Q_OBJECT
public:
    explicit HexSpinBox(QWidget *parent = 0);
protected:
    QValidator::State  validate(QString&, int&) const;
    int valueFromText(const QString&)const;
    QString textFromValue(int value)const;
private:
    QRegExpValidator *validator;
};
#endif // HEXSPINBOX_H

#include "hexspinbox.h"
HexSpinBox::HexSpinBox(QWidget *parent) :
    QSpinBox(parent)
{
    setRange(0, 255);
    validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"), this);
}
QValidator::State HexSpinBox::validate(QString &text, int &pos)const
{
    return validator->validate(text, pos);
}
QString HexSpinBox::textFromValue(int value)const
{
    return QString::number(value, 16).toUpper();
}
int HexSpinBox::valueFromText(const QString& text)const
{
    bool ok;
    return text.toInt(&ok, 16);
}