和上次一样,新建一个Qt Widget Application工程。不同的是,这次我们不使用ui文件,而是直接用c++代码来控制界面。
在main.cpp中,已经默认写好了MainWindow的创建。QMainWindow是包含菜单栏、工具栏和状态栏的。但是现在运行一下,什么也没有。
创建菜单
我们需要自己创建菜单,也就是MenuBar
在MainWindow的构造函数里添加三句。高亮的部分是新添的代码。
#include <QMenu>
#include <QMenuBar>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QMenuBar *menubar = menuBar();
QMenu *file = menubar->addMenu("&File");
QMenu *help = menubar->addMenu("&Help");
}
menuBar()是MainWindow的成员函数,它返回一个MenuBar。我们调用一次menuBar就会创建一个菜单栏。
使用MenuBar的addMenu函数,可以返回一个菜单。addMenu的参数就是菜单的名字,其中&表示快捷键,windows中按下Alt+F就相当于点击了这个菜单。
使用Qt的类要包含相应的头文件,Qt的类名和头文件名字是相同的,所以我们include了用到的QMenu和QMenuBar
运行一下
可以看到菜单栏出现了,有File和Help两个项目。现在点击一下,是没有东西出现的。
要添加菜单的内容,就要使用QAction对象了。QAction是一个动作,它不但可以添加到菜单,还可以添加到工具栏。
我们继续在MainWindow的构造函数里添加QAction
#include <QMenu>
#include <QMenuBar>
#include <QAction>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QMenuBar *menubar = menuBar();
QMenu *file = menubar->addMenu("&File");
QMenu *help = menubar->addMenu("&Help");
QAction *openAction = new QAction("&Open", this);
file->addAction(openAction);
QAction *saveAction = new QAction("&Save", this);
file->addAction(saveAction);
}
大多数类的构造函数都接受一个parent参数,表示它的父控件。如果为0表示没有父控件,是一个顶级的控件。
QAction的构造函数有几种形式的重载,把光标放到QAction按下F1键可以查看帮助文档
可以看到有三种:
QAction(QObject * parent) QAction(const QString & text, QObject * parent) QAction(const QIcon & icon, const QString & text, QObject * parent)
第一个仅仅需要父对象指针,第二个是动作的名称和父对象,第三个是图标、名称和父对象。
现在运行一下
可以看到动作已经自动添加到菜单了。
创建工具栏
创建工具栏和菜单栏类似,不一样的是添加动作的时候直接添加到工具栏QToolBar。
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include <QtoolBar>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QMenuBar *menubar = menuBar();
QMenu *file = menubar->addMenu("&File");
QMenu *help = menubar->addMenu("&Help");
QAction *openAction = new QAction("&Open", this);
file->addAction(openAction);
QAction *saveAction = new QAction("&Save", this);
file->addAction(saveAction);
QToolBar *toolBar = addToolBar("Toolbar");
toolBar->addAction(openAction);
toolBar->addAction(saveAction);
}
因为本程序只有一个工具栏,取名为Toolbar ,如果添加多个工具栏,也许需要命名更具体一些。
和QMenu类似,使用成员函数addAction就可以添加动作。可以发现这里QAction在菜单栏和工具栏都添加了。这也是这种机制的好处,只需要关心动作,而菜单和工具栏的显示由Qt负责。
运行一下可以看到工具栏,还可以自由拖动
现在工具栏显示的是文字,一点也不像普通的工具栏。我们尝试给它添加图标。其实添加图标还是在QAction里添加的
给QAction添加图标
图标的路径可以是绝对路径或相对路径,还可以把图片添加到程序中保存。下面演示把图片加到资源文件中。
首先我们找两个图标文件,命名为file_open.png和file_save.png
然后在Qt中右键点击项目名字,新建文件
选择Qt类型下面的Qt Resource File
我把它命名为res
在res.qrc上右键选择Open in Editor
然后可以添加前缀和文件了。我们添加前缀/Action并添加两个文件。
然后我们修改一下QAction的代码,使用第三种构造函数
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include <QtoolBar>
#include <QIcon>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QMenuBar *menubar = menuBar();
QMenu *file = menubar->addMenu("&File");
QMenu *help = menubar->addMenu("&Help");
QAction *openAction = new QAction(QIcon(":/Action/file_open.png"), "&Open", this);
file->addAction(openAction);
QAction *saveAction = new QAction(QIcon(":/Action/file_save.png"), "&Save", this);
file->addAction(saveAction);
QToolBar *toolBar = addToolBar("Toolbar");
toolBar->addAction(openAction);
toolBar->addAction(saveAction);
}
在QIcon的构造函数中,字符串英文冒号开头表示从资源文件中找,不带冒号可以是绝对路径或相对路径。
运行一下就可以看到图标了
添加编辑区域
编辑区域我们直接使用Qt中的QTextEdit。
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include <QtoolBar>
#include <QIcon>
#include <QTextEdit>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QMenuBar *menubar = menuBar();
QMenu *file = menubar->addMenu("&File");
QMenu *help = menubar->addMenu("&Help");
QAction *openAction = new QAction(QIcon(":/Action/file_open.png"), "&Open", this);
file->addAction(openAction);
QAction *saveAction = new QAction(QIcon(":/Action/file_save.png"), "&Save", this);
file->addAction(saveAction);
QToolBar *toolBar = addToolBar("Toolbar");
toolBar->addAction(openAction);
toolBar->addAction(saveAction);
QTextEdit *textedit = new QTextEdit(this);
setCentralWidget(textedit);
}
使用MainWindow的setCentralWidget设置窗口的主控件就是QTextEdit,这是合理的。运行一下,有记事本的样子了
QTextEdit本身支持一些鼠标右键的文本框通用行为
open 和save
现在点击open和save按钮是没有反应的,我们需要添加自己的函数来进行相应的动作。
修改mainwindow.h文件,添加两个成员函数
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
void openfile();
void savefile();
};
openfile和savefile的时候,都需要把文件内容显示到QTextEdit或者从QTextEdit读内容。
在构造函数中,我们所有的指针变量都是构造函数的局部变量,这样构造完之后就找不到这些指针了。所以我们把构造函数的
QTextEdit *textedit
变成mainwindow的成员变量。
mainwindow.h
class QTextEdit;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
void openfile();
void savefile();
QTextEdit *textedit;
};
这样mainwindow.cpp里的构造函数里就不用定义QTextEdit *textedit了,直接用textedit就可以啦。
然后我们在mainwindow.cpp里写openfile和savefile的实现
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QMenuBar *menubar = menuBar();
QMenu *file = menubar->addMenu("&File");
QMenu *help = menubar->addMenu("&Help");
QAction *openAction = new QAction(QIcon(":/Action/file_open.png"), "&Open", this);
file->addAction(openAction);
QAction *saveAction = new QAction(QIcon(":/Action/file_save.png"), "&Save", this);
file->addAction(saveAction);
QToolBar *toolBar = addToolBar("Toolbar");
toolBar->addAction(openAction);
toolBar->addAction(saveAction);
textedit = new QTextEdit(this);
setCentralWidget(textedit);
connect(openAction, QAction::triggered, this, openfile);
connect(saveAction, QAction::triggered, this, savefile);
}
void MainWindow::openfile()
{
}
void MainWindow::savefile()
{
}
注意 我们使用connect来连接信号和槽。connect有多种参数重载的形式,我们用的是其中一种
QMetaObject::Connection QObject::connect ( const QObject * sender, PointerToMemberFunction signal, const QObject * receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection )
第一个参数是发送信号的控件,第二个参数是成员函数。
第三个参数是响应信号的控件,第四个参数是响应控件的槽函数。
也就是说 当sender发出signal信号时,自动调用receiver的method函数
我们添加的这个,当动作触发的时候,调用响应的成员函数。
connect(openAction, QAction::triggered, this, openfile); connect(saveAction, QAction::triggered, this, savefile);
上面是Qt5的语法,在Qt4中不支持。Qt4和5同时支持的是这种connect
QMetaObject::Connection QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection )
用字符串的方式表示函数,怎么把函数变成对应的字符串呢,这就要使用SIGNAL和SLOT宏了。
使用这个需要在类的成员函数的地方声明哪个是slots(槽函数)
修改mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void openfile();
void savefile();
private:
QTextEdit *textedit;
};
在槽函数前面加上slots修饰。
然后上面两个connect也可以这样写
connect(openAction, SIGNAL(triggered()), this, SLOT(openfile())); connect(saveAction, SIGNAL(triggered()), this, SLOT(savefile()));
SIGNAL和SLOT有个好处,它包含了参数的信息,可以区分不同的重载函数。SIGNAL()括号中是函数原型,只不过不写参数名。例如SIGNAL(func(int, bool,))
打开文件
打开文件,我们需要QString类,这是Qt的字符串类,我们用来存储文件的路径。使用QFileDialog文件对话框来选择文件。
#include <QFileDialog>
void MainWindow::openfile()
{
QString path = QFileDialog::getOpenFileName(
this, //父窗口
tr("Open File"), //对话框名字
".", //默认目录 . 表示当前目录
tr("Text Files(*.txt);;ini File(*.ini)"), //过滤器 只打开txt类型 或 ini 使用;;分隔
);
if (!path.isEmpty())
{
}
}
使用QFileDialog的getOpenFileName函数可以打开一个文件对话框,这个函数有六个参数,除了我们用到的4个还有两个:默认过滤器和选项。在上面按F1可以查看函数原型。
现在,我们运行程序,点击openfile ,就会打开一个文件对话框。
选择一个txt文件后,路径就会保存到path中,path不为空的时候,使用QFile打开文件。
为了突出重点,这里文件错误直接返回,没有给出提示。
#include <QFileDialog>
#include <QFile>
void MainWindow::openfile()
{
QString path = QFileDialog::getOpenFileName(
this, //父窗口
tr("Open File"), //对话框名字
".", //默认目录
tr("Text Files(*.txt);;ini File(*.ini)"), //过滤器 只打开txt类型 或 ini 使用;;分隔
);
if (!path.isEmpty())
{
QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return; //打开失败
file.close();
}
}
然后使用QTextStream读取文件流。使用readAll一次性读取,实际应用时只可以用于小文件,若文件过大,应采用其他方法。
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
void MainWindow::openfile()
{
QString path = QFileDialog::getOpenFileName(
this, //父窗口
tr("Open File"), //对话框名字
".", //默认目录
tr("Text Files(*.txt);;ini File(*.ini)") //过滤器 只打开txt类型 或 ini 使用;;分隔
);
if (!path.isEmpty())
{
QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return; //打开失败
QTextStream in(&file);
textedit->setText(in.readAll());
file.close();
}
}
现在运行就可以打开文件了
保存文件
savefile函数和openfile是类似的,使用的是getSaveFileName,打开方式为Write。
void MainWindow::savefile()
{
QString path = QFileDialog::getSaveFileName(
this, //父窗口
tr("Open File"), //对话框名字
".", //默认目录
tr("Text Files(*.txt);;ini File(*.ini)") //过滤器 只打开txt类型 或 ini 使用;;分隔
);
if (!path.isEmpty())
{
QFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return; //打开失败
QTextStream out(&file);
out << textedit->toPlainText();
file.close();
}
}
到现在,一个可以打开和保存的简陋的记事本那就完成了。

















支付宝打赏
微信打赏