简单的文本编辑器,都是基本的功能。虽然基本,但是我也做了三天,算是很好复习了去年学的QT!
QT是一个跨平台的GUI开发框架,我使用的QT5 C++版本的,也有PyQT。我的C++ QT核心的编译是靠MSVC 2017 32-bits!
GUI设计,即图形用户界面设计,单单是一条线一个圈都要用户自己画实在是效率低,所以图形开发框架因运而生!特殊的模块,封装好的按钮、编辑框之类的控件都包含其中,不需要用户自己实现。
界面设计的 .ui文件是xml
通过QT Creator软件,设计界面后软件会自动更改 .ui 的内容!
一般来说,每一个界面文件A.ui对应一个 A.cpp和一个 A.h。
那么,该如何操作界面文件才能使图形设计变得简单呢?
直接接触控件的属性是不行的,太过麻烦——因为单单是按钮控件涉及到的属性就有宽度、高度、位置、按钮上的字、按钮的样式等。
另外,把图形设计和逻辑设计完全隔离开也不行,那样就不能实现交互了!!!
所以,QT的图形设计核心原理就是操作XXX类、XXX界面的时候,同时留一个命名空间ui内的继承于Ui_XXX类的XXX类的指针,来操作界面控件!与界面控件进行交互!
QT中有一种信号机制,设定一种信号,可以是自定义的也可以是已有的信号,再实现一个槽函数,将某个信号与某个槽函数链接起来,完成后,只要发射信号,就能触发槽函数!!!
//1.信号
signals:
void s_close();//自定义信号
//信号不需要实现
//2.槽函数
private slots:
void on_close_triggered();//待执行的槽函数
//3.将信号与槽链接起来
connect(ui->pushbutton ,SIGNAL(s_close()) ,this, SLOT(on_close_triggered()) );
//4.发射信号
emit s_close();
这种机制的核心原理其实相当简单,就是函数指针!
将信号函数和槽函数传入connect函数,会生成一个信号量实例,信号函数和槽函数会成为这个信号量实例的属性,然后C++通过仿函数,即重写小括号,只要使用了信号函数,就能通过信号量实例间接调用到槽函数!
先打破传统的窗口样式,删除边框和使背景透明!
//无边框
setWindowFlags(Qt::FramelessWindowHint);
//透明背景
setAttribute(Qt::WA_TranslucentBackground);
Action是定义控件类,可以放在菜单栏和工具栏中!!!
虽然有很多属性需要用户设置,但其实都没什么用,所有的Action除了变量名,在代码层面都一样!
右击Action,转到槽——自动生成槽函数,并且不用手动链接!!!
在不同的槽函数内写不同的功能就行。
除了需要用户实现的槽函数外,有些功能不需要用户实现,比如隐藏窗口和关闭窗口等!
这种简单的功能,可以直接用QT的信号槽机制,自动绑定!
对于mainwindow模块,
我只设置最小化窗口Action为简单的自动调用的槽函数!
因为,最大化窗口Action不止一个动作,第一次点击最大化,第二次点击就要恢复正常大小。
关闭窗口Action也不止一个动作,在关闭要比较先前保存内容 当前内容是否一致,不一致要询问 是否不保存就退出!确定就退出!内容与之前保存内容一致就直接关闭!
这个模块用来实现【替换】这个功能!
——这个类在mainwindow模块中留有一个指针,用于两个模块的交互!
当点击替换Action后, 会调用 这个类的实例的show()函数,会创建新的线程!
当关闭这个对话窗口后,发射关闭信号到mainwindow模块,将两条内容通过这个类的实例传递过去。
设置取消按钮绑定关闭对话窗口!
手动设置确定按钮与自定义槽函数on_submit_clicked()绑定!将输入的两条内容保存下来!
自定义信号s_close(),在mainwindow模块里,绑定s_close()与on_Close_triggered()自定义槽函数——作用是在关闭对话框后,调用替换字符串的函数!实现替换的功能!
在工程文件.pro内添加代码
win32-msvc*: {
QMAKE_CFLAGS *= /utf-8
QMAKE_CXXFLAGS *= /utf-8
}
比如一个按钮的形状、透明度、背景这些属性不是固定不变的,可以编辑其样式表进行设计!!!
更改菜单栏和工具栏的stylesheet属性,添加下面代码:
background-color:rgb(200, 200, 158);
border-color:rgb(90,240,100);
border-width: 5px;
border-image: url(:/images/images/1.png) 6 6 5 5;
mainwindow窗口中text editor周围一圈是透明的,用来设置背景图片!
设置背景图片的方法如下:
MainWindow类继承于QMainWndow,QMainWindow继承QWidget,重写的方法都来自个类!!!
void MainWindow::paintEvent(QPaintEvent *event){
if(background=="")return;
QPixmap pixmap = QPixmap(background).scaled(this->size());
QPainter painter(this);
painter.drawPixmap(this->rect(), pixmap);
}
右击Text Edit生成槽函数
on_textEdit_copyAvailable(bool b),on_textEdit_selectionChanged(),
前者在选择了内容后被调用,后者改变选择内容后被调用!
相同点都是设置Action。
void MainWindow::on_textEdit_copyAvailable(bool b){
//允许剪切复制
ui->aCut->setEnabled(b);
ui->aCopy->setEnabled(b);
}
void MainWindow::on_textEdit_selectionChanged(){
//更改选定内容的字体格式
QTextCharFormat fmt = ui->textEdit->currentCharFormat();
ui->aBold->setChecked(fmt.font().bold());
ui->aItalic->setChecked(fmt.font().italic());
ui->aUnderline->setChecked(fmt.font().underline());
}
\;
\;
\;
\;
【全选】
将全部内容复制到剪切板上
/*
这里的全选,是全部复制到剪切板!
虽然在复制,粘贴全部内容时便捷了,
但是在剪切全部内容时麻烦了!
*/
void MainWindow::on_aChooseAll_triggered()
{
QClipboard* c = QApplication::clipboard();
//textEdit全部内容
QString content = ui->textEdit->toPlainText();
//粘贴到剪切板上
c->setText(content);
}
【剪切】
将选定内容复制剪切板后,删除Text Edit中这段内容
void MainWindow::on_aCut_triggered()
{
QClipboard* c = QApplication::clipboard();
//选定内容
QString content = ui->textEdit->textCursor().selectedText();
//粘贴到剪切板上
c->setText(content);
//字符串删除操作
//删除选定的内容
QString text = ui->textEdit->toPlainText();
text.remove(content);
ui->textEdit->setText(text);
}
【复制】
将选定内容复制到剪切板上
void MainWindow::on_aCopy_triggered()
{
QClipboard* c = QApplication::clipboard();
//选定内容
QString content = ui->textEdit->textCursor().selectedText();
//粘贴到剪切板上
c->setText(content);
}
【粘贴】
从剪切板上粘贴到光标处
void MainWindow::on_aPaste_triggered()
{
QClipboard* c = QApplication::clipboard();
//ui->textEdit->append(c->text()); incorrect
//将内容粘贴到光标处
ui->textEdit->paste();
}
与文件操作有关的Action有下列几个
在工程文件.pro内添加一行
RC_ICONS = images/app.ico
先用Release版本编译一边,
再在build中找到对应的release版本DemllieText.exe,将其放入一个新文件夹,
找到QT的QT 5.12.2 (MSVC 2017 32-bits)发布程序,进入DemllieText.exe目录,
执行打包代码
windeployqt DemllieText.exe
下面的代码只有.h和.cpp文件的,工程文件.pro与界面设计文件.ui就不放上来了,对这两个文件的更改上面有提到!
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//命令行测试
#include
#include
#include
using namespace std;
#define out qDebug() //qDebug()<
//替换的窗口
#include "dialog_replace.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
//重写方法
protected:
//移动窗口
void mouseMoveEvent(QMouseEvent *event);
//设置左键点击
void mousePressEvent(QMouseEvent *event);
//设置背景图片
void paintEvent(QPaintEvent *event);
//设置左键释放
void mouseReleaseEvent(QMouseEvent *event);
private:
bool beingClicked; //左鼠标点击实现移动
QPoint curPos; //屏幕上点击的坐标点
QString background; //背景图片的路径
QString curFileName; //当前文件名
QString formerSaveContent; //先前的内容
// QLabel* curFileName; //当前文件名
// QLabel* cursorRow; //光标行
// QLabel* cursorCol; //光标列
QSpinBox* fontSize; //字体大小
QFontComboBox* fontCombo; //字体选择框
void Init_UI();
void Init_Connects();
//槽方法
private slots:
void on_textEdit_copyAvailable(bool b);
void on_textEdit_selectionChanged();
//Action槽函数
void on_aCut_triggered();
void on_aCopy_triggered();
void on_aPaste_triggered();
void on_aBold_triggered(bool checked);
void on_aItalic_triggered(bool checked);
void on_aUnderline_triggered(bool checked);
void on_aLargest_triggered();
void on_aClear_triggered();
void on_aNew_triggered();
void on_aShowToolBar_triggered(bool checked);
void on_aOpen_triggered();
void on_aSave_triggered();
void on_aSaveAs_triggered();
void on_aReplace_triggered();
void on_aHelp_triggered();
void on_aChangeBackground_triggered();
void on_aChooseAll_triggered();
//自定义槽函数
void on_fontCombo_Changed(const QString& arg);
void on_fontSize_Changed(int fontsize);
//替换窗口关闭后的槽函数
void on_Dialog_Replace_Closed();
// //关闭窗口时询问是否保存文件
void on_Close_triggered();
private:
Ui::MainWindow* ui;
Dialog_Replace* dlg;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//创建替换窗口
dlg = new Dialog_Replace();
//ui初始化
Init_UI();
//Init_UI()必须在Init_Connects()前面
//信号槽关联初始化
Init_Connects();
//无边框
setWindowFlags(Qt::FramelessWindowHint);
//透明背景
setAttribute(Qt::WA_TranslucentBackground);
//如果启用了鼠标跟踪,即使没有按下按钮,控件也会接收到鼠标移动事件。
//setMouseTracking(true);
//ui->mainToolBar->hide();//初始化隐藏工具栏
beingClicked = false;
//初始化背景图片
background="";
//设置主控件为textEdit
//setCentralWidget(ui->textEdit);//设置了主控件后,就算不设置布局也会自动铺满
//允许粘贴
//什么时候都允许粘贴,有没有内容是另一回事了
ui->aPaste->setEnabled(ui->textEdit->canPaste());
}
MainWindow::~MainWindow()
{
delete ui;
delete dlg;
}
void MainWindow::Init_UI(){
//1.在状态栏上添加组件
// //增加标签显示当前文件名
// curFileName = new QLabel();
// curFileName->setMinimumWidth(150);
// //初始化当前文件名
// curFileName->setText("untitled.txt");
// ui->statusBar->addWidget(curFileName);
// //增加标签显示当前光标的行和列
// cursorRow = new QLabel();
// cursorRow->setMinimumWidth(20);
// cursorRow->setText("行:");
// ui->statusBar->addWidget(cursorRow);
// cursorCol = new QLabel();
// cursorCol->setMinimumWidth(20);
// cursorCol->setText("列");
// ui->statusBar->addWidget(cursorCol);
//2.在工具栏上添加组件
//增加一个分割条
ui->mainToolBar->addSeparator();
//增加字体选择框
fontCombo = new QFontComboBox();
fontCombo->setMinimumWidth(20);
ui->mainToolBar->addWidget(new QLabel("字体:"));
ui->mainToolBar->addWidget(fontCombo);
//增加一个字体大小选择框
fontSize = new QSpinBox();
fontSize->setMinimumWidth(70);
fontSize->setMinimum(5);
fontSize->setMaximum(100);
ui->mainToolBar->addWidget(new QLabel("字体大小:"));
ui->mainToolBar->addWidget(fontSize);
//3.更改窗口名
setWindowTitle("DemllieText");
//4.设置鼠标光标
//setCursor(Qt::CrossCursor);//十字
//setCursor(Qt::WaitCursor);//圆圈
//setCursor(QCursor(QPixmap("images/mouse.png")));//自定义光标
}
void MainWindow::Init_Connects(){
/*
自动创建的on_函数是不用connect的,如果connect会导致一次点击执行两次
要么删除connect,要么在connect()后面加上一个参数
connect( ... , Qt::UniqueConnection)
*/
connect(fontSize,SIGNAL(valueChanged(int)),this,SLOT(on_fontSize_Changed(int)));
connect(fontCombo,SIGNAL(currentIndexChanged(const QString&)),this,SLOT(on_fontCombo_Changed(const QString&)));
//替换对话框关闭信号
connect(dlg,SIGNAL(s_close()),this,SLOT(on_Dialog_Replace_Closed()));
//关闭文件
connect(ui->aClose,SIGNAL(triggered()),this,SLOT(on_Close_triggered()));
}
/*
编辑器关闭时,判断内容是否保存
如果没有保存,询问是否放弃保存并退出!!!
*/
void MainWindow::on_Close_triggered(){
//当前内容和之前保存的内容一致
if(ui->textEdit->toPlainText() == formerSaveContent ){
close();
return;
}
if(QMessageBox::warning(this,"警告","文件没有保存,确定退出吗?",
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes ){
close();
}
}
void MainWindow::mouseMoveEvent(QMouseEvent *event){
if(beingClicked){
//移动鼠标
QPoint tempPos = event->globalPos() - curPos;
move(pos() + tempPos);
curPos = event->globalPos();
}
}
void MainWindow::mousePressEvent(QMouseEvent *event){
if(event->buttons() == Qt::LeftButton ){
beingClicked = true;
curPos = event->globalPos();
}
}
void MainWindow::paintEvent(QPaintEvent *event){
if(background=="")return;
QPixmap pixmap = QPixmap(background).scaled(this->size());
QPainter painter(this);
painter.drawPixmap(this->rect(), pixmap);
}
void MainWindow::mouseReleaseEvent(QMouseEvent *event){
beingClicked = false;
}
/*--------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------*/
void MainWindow::on_textEdit_copyAvailable(bool b){
//允许剪切复制
ui->aCut->setEnabled(b);
ui->aCopy->setEnabled(b);
}
void MainWindow::on_textEdit_selectionChanged(){
//更改选定内容的字体格式
QTextCharFormat fmt = ui->textEdit->currentCharFormat();
ui->aBold->setChecked(fmt.font().bold());
ui->aItalic->setChecked(fmt.font().italic());
ui->aUnderline->setChecked(fmt.font().underline());
}
void MainWindow::on_aCut_triggered()
{
QClipboard* c = QApplication::clipboard();
//选定内容
QString content = ui->textEdit->textCursor().selectedText();
//粘贴到剪切板上
c->setText(content);
//字符串删除操作
//删除选定的内容
QString text = ui->textEdit->toPlainText();
text.remove(content);
ui->textEdit->setText(text);
}
void MainWindow::on_aCopy_triggered()
{
QClipboard* c = QApplication::clipboard();
//选定内容
QString content = ui->textEdit->textCursor().selectedText();
//粘贴到剪切板上
c->setText(content);
}
/*
这里的全选,是全部复制到剪切板!
虽然在复制,粘贴全部内容时便捷了,
但是在剪切全部内容时麻烦了!
*/
void MainWindow::on_aChooseAll_triggered()
{
QClipboard* c = QApplication::clipboard();
//textEdit全部内容
QString content = ui->textEdit->toPlainText();
//粘贴到剪切板上
c->setText(content);
}
void MainWindow::on_aPaste_triggered()
{
QClipboard* c = QApplication::clipboard();
//ui->textEdit->append(c->text()); incorrect
//将内容粘贴到光标处
ui->textEdit->paste();
}
void MainWindow::on_aClear_triggered()
{
ui->textEdit->clear();
}
void MainWindow::on_aReplace_triggered()
{
dlg->show();
//对话框打开后,会创建新线程
}
void MainWindow::on_Dialog_Replace_Closed(){
if(dlg->pre == "" || dlg->last == "")return;
//清除字符串前后的空格
dlg->pre.trimmed();
dlg->last.trimmed();
//字符串替换操作
QString text = ui->textEdit->toPlainText();
text.replace(dlg->pre,dlg->last);
ui->textEdit->setText(text);
}
void MainWindow::on_aBold_triggered(bool checked)
{
QTextCharFormat fmt;
fmt=ui->textEdit->currentCharFormat();
if(checked){
fmt.setFontWeight(QFont::Bold);
}
else{
fmt.setFontWeight(QFont::Normal);
}
ui->textEdit->mergeCurrentCharFormat(fmt);
}
void MainWindow::on_aItalic_triggered(bool checked)
{
QTextCharFormat fmt;
fmt=ui->textEdit->currentCharFormat();
if(checked){
fmt.setFontItalic(true);
}
else{
fmt.setFontItalic(false);
}
ui->textEdit->mergeCurrentCharFormat(fmt);//设置部分字符串格式
}
void MainWindow::on_aUnderline_triggered(bool checked)
{
/*
显示全体字符串的下划线设置,不是我需要的
QFont font=ui->txtEdit->font();
font.setUnderline(checked);
ui->txtEdit->setFont(font);
*/
QTextCharFormat fmt;
fmt=ui->textEdit->currentCharFormat();
if(checked){
fmt.setFontUnderline(true);
}
else{
fmt.setFontUnderline(false);
}
ui->textEdit->mergeCurrentCharFormat(fmt);
}
void MainWindow::on_aLargest_triggered()
{
static bool flag=true;
//第一次最大化窗口,第二次恢复正常窗口
if(flag){
showMaximized();
flag=false;
}
else{
showNormal();
flag=true;
}
}
void MainWindow::on_aShowToolBar_triggered(bool checked)
{
if(checked){
ui->mainToolBar->hide(); //incorrect
//ui->mainToolBar->setVisible(false);
}
else{
ui->mainToolBar->show(); //incorrect
//ui->mainToolBar->setVisible(true);
}
}
/*
文件内容为空,并且curFileName为空,就不需要保存了
——说明此时编辑器刚打开
*/
void MainWindow::on_aNew_triggered(){
if(ui->textEdit->toPlainText() == "" && curFileName == ""){
return;
}
//保存旧的内容
on_aSave_triggered();
curFileName="";
//新文本编辑
ui->textEdit->clear();
ui->textEdit->setHidden(false);
}
void MainWindow::on_aOpen_triggered(){
//先保存没有保存的内容
if(ui->textEdit->toPlainText() != formerSaveContent){
on_aSaveAs_triggered();
}
QString startPath = QDir::currentPath();
QString filter = "文本文件(*.txt)";
QString fileName = QFileDialog::getOpenFileName(this,"打开",startPath,filter);
if(fileName=="")return;
QFile file(fileName);
if(!file.open(QIODevice::ReadWrite)){
QMessageBox::critical(this,"错误","文件打开失败!");
return;
}
QTextStream stream(&file);
for (;false == stream.atEnd();) {
ui->textEdit->setPlainText(stream.readAll());
}
//更新textEdit的显示
ui->textEdit->show();
file.close();
curFileName = fileName;
}
void MainWindow::on_aSave_triggered()
{
if(curFileName==""){
//保存为
on_aSaveAs_triggered();
return;
}
//保存
QString content = ui->textEdit->toPlainText();
if( content !=""){
QFile file(curFileName);
if(!file.open(QIODevice::ReadWrite)){
QMessageBox::critical(this,"错误","文件保存失败!");
return;
}
QTextStream stream(&file);
stream << content;
formerSaveContent = content;//更新保存内容
file.close();
}
}
void MainWindow::on_aSaveAs_triggered(){
QString startPath = QDir::currentPath();
QString filter = "文本文件(*.txt)";
QString fileName = QFileDialog::getSaveFileName(this,"保存为",startPath,filter);
if(fileName=="")return;
QFile file(fileName);
if(!file.open(QIODevice::ReadWrite)){
QMessageBox::critical(this,"错误","文件保存失败!");
return;
}
QTextStream stream(&file);
QString content = ui->textEdit->toPlainText();
stream << content;
formerSaveContent = content;//更新保存内容
file.close();
curFileName = fileName;
}
void MainWindow::on_aHelp_triggered()
{
//关于作者:Demllie
//制作时间:2020-6-25
//联系方式:2042153158
QString str = "作者:Demllie\n制作时间:2020-6-25\n联系方式:2042153158";
QMessageBox::information(this,"关于作者",str,QMessageBox::Ok);
}
void MainWindow::on_aChangeBackground_triggered()
{
//QString startPath = "C:\\Users\\LX\\Desktop\\";
QString startPath = QDir::currentPath();
QString filter = "图片文件(*.jpg *.png *.bmp *jpeg)";
//找到图片文件作为背景
QString file = QFileDialog::getOpenFileName(this,"选择背景图片",startPath,filter);
if(file=="")return;
background = file;
}
//自定义槽函数
void MainWindow::on_fontCombo_Changed(const QString& arg){
QTextCharFormat fmt;
fmt.setFontFamily(arg);//设置字体
ui->textEdit->mergeCurrentCharFormat(fmt);//更新字体格式
}
void MainWindow::on_fontSize_Changed(int fontsize){
QTextCharFormat fmt;
fmt.setFontPointSize(fontsize);
ui->textEdit->mergeCurrentCharFormat(fmt);
}
//dialog_replace.h
#ifndef DIALOG_REPLACE_H
#define DIALOG_REPLACE_H
#include
namespace Ui {
class Dialog_Replace;
}
class Dialog_Replace : public QDialog
{
Q_OBJECT
public:
explicit Dialog_Replace(QWidget *parent = nullptr);
~Dialog_Replace();
private slots:
void on_submit_clicked();
//重写方法
protected:
void closeEvent(QCloseEvent* event);
public:
QString pre;//要替换的内容
QString last;//替换后的内容
//自定义信号
signals:
void s_close();
private:
Ui::Dialog_Replace *ui;
};
#endif // DIALOG_REPLACE_H
//dialog_replace.cpp
#include "dialog_replace.h"
#include "ui_dialog_replace.h"
Dialog_Replace::Dialog_Replace(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog_Replace)
{
ui->setupUi(this);
connect(ui->pushButton_submit,SIGNAL(clicked()),this,SLOT(on_submit_clicked()));
//设置窗口模态,窗口不关闭,其他窗口不能执行
setModal(true);
//设置窗口名
setWindowTitle("替换");
}
Dialog_Replace::~Dialog_Replace()
{
delete ui;
}
void Dialog_Replace::on_submit_clicked(){
pre = ui->lineEdit_pre->text();
last = ui->lineEdit_last->text();
ui->lineEdit_pre->clear();
ui->lineEdit_last->clear();
close();
}
void Dialog_Replace::closeEvent(QCloseEvent* event){
//发射关闭信号
emit s_close();
//清空内容
pre = "";
last = "";
}
//main.cpp
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}