写在前面
博主当前是计科大一下学生,大一下的课程设计自命题做一个app,具体功能可见专栏第一篇的需求文档。现在的进度约等于0,不过博主已经把Qt基础知识学完了,具体情况可以看博主的另一篇专栏。这一篇主要记录一下利用Qt中的Stacked Widget控件实现界面切换,弥补前期错误。
当前文件展示
非常惨烈,由于博主在画ui的时候根本不知道stacked Widget的存在,以为界面切换就是不同窗口来回变,所以就出了这种错误。
补救措施
(1)分板块画窗口
我的规划是,登陆注册算一个ui,待办及子界面算一个ui,打卡及子界面算一个ui,规划及子界面算一个ui,笔记及子界面算一个ui。
各个ui的切换依靠hide和show函数,ui内的切换依靠stacked Widget控件
把这里的东西复制下来然后删掉,向里边添加一个Stacked Widget控件,再把这个内容粘贴到第一个窗口,再跟上注册界面的内容。其他ui也效仿这个做
那些残留的ui、cpp、h就没什么用了,看着也很碍眼,直接删了
当然这里的删了是要在项目文件夹里删了,然后还要在pro文件里把和这些文件有关的语句全删了
附工程文件代码,顺手把sql和network库加上,因为这个项目要用到TCP服务器和MySQL
QT += core gui sql networkgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++17# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0SOURCES += \clock.cpp \main.cpp \mainwindow.cpp \note.cpp \plan.cpp \task.cppHEADERS += \clock.h \mainwindow.h \note.h \plan.h \task.hFORMS += \clock.ui \mainwindow.ui \note.ui \plan.ui \task.uiTRANSLATIONS += \Tick_Task_zh_CN.ts \
CONFIG += lrelease
CONFIG += embed_translations# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
然后下面我就面临了一个可怕的问题:这里很多按钮都是一样的,所以我就把他们的命名设成一样的了。如果不在同一个文件里还好,现在在一个文件里,系统已经自动改了标号,我需要把这些命名再重新改了,要不然后边很难做
改了很长时间,终于勉强改好了,起码给了一个名字我知道它在我的项目里指的是啥
图中为一部分命名,这个东西越改越乱,我的建议是不要想到一个写一个,先想清楚都有什么类型再决定怎么命名
(2)实现界面切换
好了,现在正片开始了,控件已经全部搭好,现在终于可以实现具体功能了。现在先把各个按钮的界面切换功能实现
这是用户进入系统以后看到的第一个界面,我们先把按钮点击注册然后切换到注册界面功能实现
不得不说转到槽真的非常方便
void MainWindow::on_sign_registerButton_clicked()
{ui->stackedWidget->setCurrentIndex(1);
}
在槽函数里写这样一句,就可以实现点击注册键跳到注册界面。
代码解释:调用对象ui的stackedWidget的setCurrentIndex函数,参数是界面的索引,索引从0开始,我的注册放在了第二张,索引就是1
我们依次类推,可以先实现每个ui的内部转换,然后再实现各个大功能的转换
现在用stacked Widget的代码我就不附了,后边ui间的切换我再附一下
好了,经过一段时间,博主已经把每个ui内部的切换弄好了,现在开始弄ui间的切换
分割线---------------------------------------------------------------------------------------------------------------分割线
这是博主最开始的想法,碰壁了
Task *t;
要在构造函数里声明这样一个Task类的指针
//这个函数实现了从登录界面转到待办界面------------------------------------------检测账号密码是否正确还未完成
void MainWindow::on_sign_loginButton_clicked()
{this->hide();t = new Task;t->show();
}
这个代码的意思就是在堆上开一个Task类,然后把主窗口隐藏,再打开新窗口。注释可以很好地提醒我哪些功能还没做,后续可以跟进。注意:要在析构函数里把内存释放掉
接下来就是完成其他主界面的切换,和这个代码是类似的
分割线---------------------------------------------------------------------------------------------------------------分割线
博主还是太年轻了,想当然地以为每个功能类里都声明其他三个类的指针,然后仿照上边这个写法就行了。但是在处理clock类的时候出现了状况,我包含"task,h"的时候编译器报错,说不能递归调用头文件。如果不能在一个类中调用其他三个类的头文件,也就不能声明其他类的对象,就宣告了我的计划破产
现在博主有了更好的方法:利用信号和槽
具体思路:利用mainwindow做中介,其他四个功能类负责发出信号,界面切换按钮按下之后只负责发出信号,其他什么都不用干,然后mainwindow里用connect函数捕获发出的信号,再调用它里边的界面切换函数。这样的话只有mainwindow里需要包含另外四个头文件,功能头文件做好自己就可以了
Task *t;Clock *c;Plan *p;Note *n;
在MainWindow类的private里声明这四个类的指针
在构造函数里开辟空间
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), t(new Task(this)), c(new Clock(this)), p(new Plan(this)), n(new Note(this))
{ui->setupUi(this);
}
在析构函数里释放空间
MainWindow::~MainWindow()
{delete ui;delete t;delete c;delete p;delete n;
}
signals:void taskToPlan();void taskToClock();void taskToNote();
以Task类为例,像这样声明三个信号,然后在槽函数里发出信号
//这个函数的作用是发出一个要切换到打卡界面的信号
void Task::on_task_clockOutButton_clicked()
{emit taskToClock();
}//这个函数的作用是发出一个要切换到规划界面的信号
void Task::on_task_planButton_clicked()
{emit taskToPlan();
}//这个函数的作用是发出一个要切换到笔记界面的信号
void Task::on_task_noteButton_clicked()
{emit taskToNote();
}
这样做好以后在MainWindow里调用切换界面的槽函数就行,不过需要先在MainWindow声明这些函数
void switchTaskToClock();void switchTaskToPlan();void switchTaskToNote();void switchClockToTask();void switchClockToPlan();void switchClockToNote();void switchPlanToTask();void switchPlanToClock();void switchPlanToNote();void switchNoteToTask();void switchNoteToClock();void switchNoteToPlan();
好了,赏心悦目。然后就是具体实现,我为了举例子就在这里先实现switchTaskToClock函数
在MainWindow里写connect函数
connect(t, &Task::taskToPlan, this, &MainWindow::switchTaskToPlan);connect(t, &Task::taskToClock, this, &MainWindow::switchTaskToClock);connect(t, &Task::taskToNote, this, &MainWindow::switchTaskToNote);
实现界面切换函数
void MainWindow::switchTaskToClock()
{t->hide();c->show();
}
好了,例子就举到这里。剩下的博主就自己实现了,然后就不附代码了
经过很长一段时间,博主终于弄好了切换,跑出来震惊我了
出现这个的原因是在构造函数时把所有的窗口都show了,需要把它们hide一下
博主在做到这一步的时候又遇到了问题。因为最开始的时候我把登录注册当成了主窗口,其他界面是子窗口,this->hide()就全消失了,所以现在还要把登陆注册独立出来
现在博主要新开一个login.ui及h和cpp,然后按照上边几个界面处理方法来做了。不过由于mainwindow碰的壁太多,所以需要把那里的部分槽函数删了,然后再实现信号传递的槽函数
在这里博主要纠正前边的一个错误
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), t(new Task()), c(new Clock()), p(new Plan()), n(new Note()), l(new Login())
这里创建指针的时候不能传入this指针,如果传入this指针的话就相当于是在主窗口内展示,它们实际上没有独立性,为了保持它们的独立性,这里不应该传参,直接默认空指针就行
然后跑出来的话肯定主窗口和后续窗口就是独立的,所以主窗口直接就不用show了,直接show()Login类的窗口
效果展示
博主不知道怎么挂视频,挂不出来,总之是跑出来了,而且很流畅
篇末总结
这个项目的界面切换博主是走了很多弯路的,虽然在博客中并没有很明显的体现,但是博主线下做这个项目的时候一直是焦头烂额的状态。
不过万幸的是最后博主也算顺利搞定界面切换了。看到能跑通的项目博主内心是非常高兴的,感觉一切都是值得的
从无到有实在是很难,我在CSDN、b站、抖音等很多渠道都去搜在Qt中如何实现界面切换,但是没有人告诉我stacked Widget类,也没人告诉我用信号与槽,所以博主在这方面一直栽跟头,有错误也一直找不到
归根结底还是博主对Qt的理解欠缺,Qt只是一个工具,熟悉它的人自然可以返璞归真。这个进度对博主来说只是冰山一角,后续还要硬肝