[项目:微服务即时通讯系统客户端(基于C++QT)]三,左侧界面搭建

news2024/11/14 20:27:23

三,左侧界面搭建

一,导入

先把MainWidget类做成“单例类”

采用的是单例模式,让某一个类,在指定进程中只有唯一的实例

先看一下MainWidget的框架

QWidget//这部分是头文件保护宏,确保该头文件只被包含一次,防止因重复包含导致的编译错误。
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

//引入 Qt 的 QWidget 类,这个类是所有用户界面对象的基类。
#include <QWidget>

//这段代码使用 Qt 的命名空间机制。
//它定义了一个名为 Ui 的命名空间,里面声明了一个名为 mainWidget 的类。
//这个类通常是通过 Qt Designer 生成的,用于管理 UI 元素。
QT_BEGIN_NAMESPACE
namespace Ui {
class mainWidget;
}
QT_END_NAMESPACE

//这里定义了一个名为 mainWidget 的类,它继承自 QWidget。Q_OBJECT
class mainWidget : public QWidget
{
    Q_OBJECT

public:
    mainWidget(QWidget *parent = nullptr);
    ~mainWidget();

private:
    Ui::mainWidget *ui;
};
#endif // MAINWIDGET_H

//包含了之前定义的 mainWidget 类的头文件。
#include "mainwidget.h"
//包含了由 Qt Designer 生成的 UI 代码的头文件,通常负责设置 UI 元素。
#include "./ui_mainwidget.h"

//这行代码定义了一个静态指针成员变量 instance,初始化为 nullptr。这通常用于实现单例模式,确保 mainWidget 类只有一个实例
mainWidget* mainWidget::instance=nullptr;

//构造函数,接受一个可选的父窗口部件指针
mainWidget::mainWidget(QWidget *parent)
    : QWidget(parent)        //调用基类 QWidget 的构造函数,并将父部件传递给它
    , ui(new Ui::mainWidget) //创建一个新的 Ui::mainWidget 对象,负责设置 UI。
{
    ui->setupUi(this);       //调用 setupUi 方法,将 UI 组件设置到当前 mainWidget 实例中
}

//析构函数
mainWidget::~mainWidget()
{
    delete ui;
}

二,需求分析

53f9dff501159a97d4c122149e72b38.png

三,代码书写

前置:设置图标,需要把图标图片,导入到项目中,通过qrc文件进行管理

在本章节大致使用了一下素材

素材网站:

https://www.aigei.com/

https://www.iconfont.cn/

userAvatar.jpeglogo.png

apply_active.pngapply_inactive.pngfriend_active.pngfriend_inactive.pngsession_active.pngsession_inactive.png

一,mainWidget.h的编写

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>
#include <QPushButton>

QT_BEGIN_NAMESPACE
namespace Ui {
class mainWidget;
}
QT_END_NAMESPACE

class mainWidget : public QWidget
{
    Q_OBJECT

private:
    static mainWidget* instance;//指针指向当前的类的唯一实例
    //静态指针,用于指向 mainWidget 类的唯一实例
    //这是单例模式的核心,确保全局只存在一个 mainWidget 对象
    //对于单例模式来说,最关键的部分,不是创建实例,而是限制别人创建实例
    mainWidget(QWidget *parent = nullptr);
    //他是私有的防止外部代码直接创建mainWidget实例

public:
    static mainWidget*  getInstance();//静态成员函数用于获取当前实例
    ~mainWidget();//析构函数

private:
    Ui::mainWidget *ui;

    //窗口最左侧部分
    QWidget* windowLeft;
    //窗口中间部分
    QWidget* windowMid;
    //窗口右侧部分
    QWidget* windowRight;

    //用户头像
    QPushButton* userAvatar;
    //会话标签页按钮
    QPushButton* sessionTabBtn;
    //好友标签页按钮
    QPushButton* friendTabBtn;
    //好友申请标签页按钮
    QPushButton* applyTabBtn;

    //枚举类型当前的激活的标签页是哪一个
    enum ActiveTab{
        SESSION_LIST,
        FRIEND_LIST,
        APPLY_LIST
    };

    ActiveTab activeTab=SESSION_LIST;//默认为会话列表



    //初始化主窗口的样式布局
    void initMainWindow();
    //初始化左侧窗口布局
    void initLeftWindow();
    //初始化中间窗口布局
    void initMidWindow();
    //初始化右侧窗口布局
    void initRightWindow();

    //信号槽
    void initSignalSlot();
    //槽函数
    void switchTabToSession();
    void switchTabToFriend();
    void switchTabToApply();


    void loadSessionList();
    void loadFriendList();
    void loadApplyList();
};
#endif // MAINWIDGET_H

本头文件采用了单例模式

单例模式(Singleton Pattern)是一种设计模式,它确保一个类在程序运行期间只会创建一个实例,并且提供一个全局访问点。

到底此处的代码怎么实现的单例模式呢?

  1. 私有构造函数

  2. 静态实例指针static mainWidget* instance;

  3. 静态访问接口static mainWidget* getInstance();

回顾静态成员的特点
  • 静态成员变量:属于类本身,而不是某个对象。所有类的实例共享同一个静态成员变量,它在程序运行时只会分配一块内存空间。

  • 静态成员函数:同样属于类,而不是对象。它只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非静态成员函数(因为非静态成员是属于具体对象的)。

单例模式ABC详解

A.构造函数是私有的mainWidget(QWidget *parent = nullptr);

防止外部代码直接创建mainWidget实例

B.**static** 修饰的 **instance** 是一个静态成员变量,它属于 **mainWidget** 类本身,而不是某个特定的 mainWidget 对象.

程序整个周期只存在一份,所有访问mainWidget类的都共享一个instance指针,初始值为nullptr的时候说明还没有创建。

静态成员变量 instance 可以在 mainWidget 类的任何成员函数中访问,也可以通过 getInstance() 方法从类的外部访问。

C.提供对单例实例的访问

mainWidget* mainWidget::getInstance()
{
    //此处不传入参数,以桌面为父窗口
    //由于此时的窗口是整个程序的主窗口,父窗口就设定为桌面,本身就是常规设定。
    if(instance==nullptr)
    {
        instance=new mainWidget();
    }
    return instance;
}

第一次调用 getInstance() 时,创建一个新的实例

第二次以后调用,直接返回instance实例;

二,逐步编写mainwidget.cpp

一,构造函数

mainWidget::mainWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::mainWidget)
{
    ui->setupUi(this);
    //this指向被构造的mainWidget
    this->setWindowTitle("Pokemon聊天室");
    this->setWindowIcon(QIcon(":/resource/image/logo.png"));

    //初始化主窗口的样式布局
    initMainWindow();
    //初始化左侧窗口布局
    initLeftWindow();
    //初始化中间窗口布局
    initMidWindow();
    //初始化右侧窗口布局
    initRightWindow();
    //初始化信号槽
    initSignalSlot();
}

二,initMainWindow初始化主窗口

void mainWidget::initMainWindow()
{
    //创建一个水平布局管理器
    QHBoxLayout* layout=new QHBoxLayout();

    //处理间隔Spacing就是layout内部元素之间的间隔距离,设置为0就是紧紧挨着水平的紧紧挨着
    layout->setSpacing(0);

    this->setLayout(layout);//将 layout 设置为 mainWidget 窗口的布局管理器
    
    //创建三个QWidget子部件
    windowLeft=new QWidget();
    windowMid=new QWidget();
    windowRight=new QWidget();
    //这三个部分马上添加到布局管理器中

    //在设计界面的时候,会涉及到很多尺寸,间距,边框,字体相关细节。
    windowLeft->setFixedWidth(60);//左侧导航栏为固定的70像素
    windowMid->setFixedWidth(230);//中间
    windowRight->setMinimumWidth(500);//右边聊天框 根据窗口自动化改变
    //可以使用qq的截图来进行判定大小

    //设计颜色qss
    windowLeft->setStyleSheet("QWidget {background-color:rgb(23,23,23)}");//左侧导航栏为固定的70像素
    windowMid->setStyleSheet("QWidget {background-color:rgb(46,46,46)}");
    windowRight->setStyleSheet("QWidget {background-color:rgb(255,255,255)}");

    //子窗口添加到布局管理器中
    layout->addWidget(windowLeft);
    layout->addWidget(windowMid);
    layout->addWidget(windowRight);


}

image.png

三,initLeftWindow()

//H 代表 Horizontal(水平),V 代表 Vertical(垂直)
void mainWidget::initLeftWindow()
{
    QVBoxLayout* layout =new QVBoxLayout();
    layout->setSpacing(20);//设置按钮的间距
    layout->setContentsMargins(0,70,0,0);//设置左上右下的间距
    windowLeft->setLayout(layout);

    //添加用户头像
    userAvatar =new QPushButton();
    userAvatar->setFixedSize(45,45);//按钮本身的尺寸
    userAvatar->setIconSize(QSize(45,45));//按钮自身的尺寸
    userAvatar->setIcon(QIcon(":/resource/image/userAvatar.jpg"));
    userAvatar->setStyleSheet("QPushButton{ background-color:transparent;}");
    layout->addWidget(userAvatar,1,Qt::AlignTop |   Qt::AlignCenter);//1是占据空间的权重,靠上对齐,水平居中

    layout->addStretch(5);//添加空白处

    //添加会话标签按钮
    sessionTabBtn=new QPushButton();
    sessionTabBtn->setFixedSize(45,45);//按钮本身的尺寸
    sessionTabBtn->setIconSize(QSize(45,45));//按钮自身的尺寸
    sessionTabBtn->setIcon(QIcon(":/resource/image/session_active.png"));
    sessionTabBtn->setStyleSheet("QPushButton{ background-color:transparent;}");
    layout->addWidget(sessionTabBtn,1,Qt::AlignTop |   Qt::AlignCenter);
    //添加好友标签页按钮
    friendTabBtn=new QPushButton();
    friendTabBtn->setFixedSize(45,45);//按钮本身的尺寸
    friendTabBtn->setIconSize(QSize(45,45));//按钮自身的尺寸
    friendTabBtn->setIcon(QIcon(":/resource/image/friend_inactive.png"));
    friendTabBtn->setStyleSheet("QPushButton{ background-color:transparent;}");
    layout->addWidget(friendTabBtn,1,Qt::AlignTop |   Qt::AlignCenter);
    //添加好友申请标签页按钮
    applyTabBtn=new QPushButton();
    applyTabBtn->setFixedSize(45,45);//按钮本身的尺寸
    applyTabBtn->setIconSize(QSize(45,45));//按钮自身的尺寸
    applyTabBtn->setIcon(QIcon(":/resource/image/apply_inactive.png"));
    applyTabBtn->setStyleSheet("QPushButton{ background-color:transparent;}");
    layout->addWidget(applyTabBtn,1,Qt::AlignTop |   Qt::AlignCenter);;

    layout->addStretch(20);//添加下面的空白处

    //连接信号槽,处理标签页按钮切换的问题
}

四,initSignalSlot()

void mainWidget::initSignalSlot()
{
    /
    ///连接信号槽,处理标签页按钮切换的问题
    /

    connect(sessionTabBtn,&QPushButton::clicked,this,&mainWidget::switchTabToSession);
    //连接的信号来自sessionTabBtn这个指向 QPushButton 对象的指针
    //&QPushButton::clicked 表示 QPushButton 类中的 clicked 信号,clicked 信号在 QPushButton 被点击时触发
    //槽(slot)函数所属对象的指针,指的是 mainWidget 类的一个实例对象,槽函数是在当前对象(即 mainWidget 类的实例)中定义的
    //最后一个位置是槽函数:每当 sessionTabBtn 发出 clicked 信号时,这个槽函数就会被调用
    connect(friendTabBtn,&QPushButton::clicked,this,&mainWidget::switchTabToFriend);
    connect(applyTabBtn,&QPushButton::clicked,this,&mainWidget::switchTabToApply);

}

addWidget的参数中1的作用

拉伸系数的作用:它决定了当布局空间发生变化(如窗口大小改变时),该部件在布局中占据的比例。如果拉伸系数为 1,那么它在布局中将获得与其他拉伸系数为 1 的部件相同的空间份额。如果拉伸系数为 0,则该部件的大小不会随布局的空间变化而变化(即固定大小)

五,槽函数

void mainWidget::switchTabToSession()
{
    //1.记录当前切换到了哪一个标签页
    activeTab=SESSION_LIST;
    //2.调整当前图片显示情况,把会话的按钮图标设为active,另两个图标设为inactive
    sessionTabBtn->setIcon(QIcon(":/resource/image/session_active.png"));
    friendTabBtn->setIcon(QIcon(":/resource/image/friend_inactive.png"));
    applyTabBtn->setIcon(QIcon(":/resource/image/apply_inactive.png"));
    //3.在主窗口中间部分,加载会话列表数据
    this->loadSessionList();
}
void mainWidget::switchTabToFriend()
{
    //1.记录当前切换到了哪一个标签页
    activeTab=FRIEND_LIST;
    //2.调整当前图片显示情况,把会话的按钮图标设为active,另两个图标设为inactive
    sessionTabBtn->setIcon(QIcon(":/resource/image/session_inactive.png"));
    friendTabBtn->setIcon(QIcon(":/resource/image/friend_active.png"));
    applyTabBtn->setIcon(QIcon(":/resource/image/apply_inactive.png"));
    //3.在主窗口中间部分,加载会话列表数据
    this->loadFriendList();
}
void mainWidget::switchTabToApply()
{
    //1.记录当前切换到了哪一个标签页
    activeTab=APPLY_LIST;
    //2.调整当前图片显示情况,把会话的按钮图标设为active,另两个图标设为inactive
    sessionTabBtn->setIcon(QIcon(":/resource/image/session_inactive.png"));
    friendTabBtn->setIcon(QIcon(":/resource/image/friend_inactive.png"));
    applyTabBtn->setIcon(QIcon(":/resource/image/apply_active.png"));
    //3.在主窗口中间部分,加载会话列表数据
    this->loadApplyList();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2156937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

240922-chromadb的基本使用

A. 背景介绍 ChromaDB 是一个较新的开源向量数据库&#xff0c;专为高效的嵌入存储和检索而设计。与其他向量数据库相比&#xff0c;ChromaDB 更加专注于轻量化、简单性和与机器学习模型的无缝集成。它的核心目标是帮助开发者轻松管理和使用高维嵌入向量&#xff0c;特别是与生…

【软件工程】数据流图和数据字典

一、数据流图 3.符号 分析结果 二、数据字典 例题 选择题

使用build_chain.sh离线搭建匹配的区块链,并通过命令配置各群组节点的MySQL数据库

【任务】 登陆Linux服务器&#xff0c;以MySQL分布式存储方式安装并部署如图所示的三群组、四机构、 七节点的星形组网拓扑区块链系统。其中&#xff0c;三群组名称分别为group1、group2和group3&#xff0c; 四个机构名称为agencyA、agencyB、agencyC、agencyD。p2p_port、cha…

Python | Leetcode Python题解之第429题N叉树的层序遍历

题目&#xff1a; 题解&#xff1a; class Solution:def levelOrder(self, root: Node) -> List[List[int]]:if not root:return []ans list()q deque([root])while q:cnt len(q)level list()for _ in range(cnt):cur q.popleft()level.append(cur.val)for child in c…

爬虫过程 | 蜘蛛程序爬取数据流程(初学者适用)

蜘蛛程序&#xff08;也称网络爬虫&#xff0c;是搜索引擎的重要组成部分&#xff09; 主要功能&#xff1a;遍历互联网&#xff0c;抓取网站信息并建立索引&#xff0c;便于用户在搜索引擎中检索到最新的网页内容工作原理&#xff1a;从初始网站页面的URL开始&#xff0c;发送…

qt-C++笔记之Q_DECLARE_METATYPE和qRegisterMetaType

qt-C笔记之Q_DECLARE_METATYPE和qRegisterMetaType code review! 文章目录 qt-C笔记之Q_DECLARE_METATYPE和qRegisterMetaType一.Q_DECLARE_METATYPE使用方法应用场景 二.为什么需要注册类型&#xff1f;三.使用 Q_DECLARE_METATYPE 处理自定义类型的简短示例3.1.自定义类型定…

《独孤九剑》游戏源码(客户端+服务端+数据库+游戏全套源码)大小2.38G

《独孤九剑》游戏源码&#xff08;客户端服务端数据库游戏全套源码&#xff09;大小2.38G ​ 下载地址&#xff1a; 通过网盘分享的文件&#xff1a;【源码】《独孤九剑》游戏源码&#xff08;客户端服务端数据库游戏全套源码&#xff09;大小2.38G 链接: https://pan.baidu.co…

生信服务器 | 组蛋白甲基化修饰、DNA亲和纯化测序、优青博导团队指导设计、解读实验结果。

查看原文>>>生信服务器 | 组蛋白甲基化修饰、DNA亲和纯化测序、优青博导团队免费指导设计、解读实验结果、一台服务器解决您所有的分析困扰!

VLDB 2024 圆桌会议回顾:展望物联网与 AI 时代的时序数据库

回顾我们在 VLDB 2024 8 月 26 日至 8 月 30 日&#xff0c;数据库领域的顶级国际会议 VLDB 2024 在广州举行。IoTDB 最新研发成果的三篇论文被本次大会录用&#xff08;详见&#xff1a;IoTDB 在顶级会议 VLDB 2024&#xff1a;四篇最新论文入选&#xff0c;特邀做 TPC 报告与…

6.7泊松噪声

基础概念 在OpenCV联合C中给一张图片添加泊松噪声&#xff08;Poisson Noise&#xff09;可以通过生成随机数并在图像的每个像素上加上这些随机数来实现。泊松噪声是一种统计分布服从泊松分布的噪声&#xff0c;通常用于模拟光子计数等场景。 使用泊松噪声的场景 泊松噪声通…

【记录】Excel|不允许的操作:合并或隐藏单元格出现的问题列表及解决方案

人话说在前&#xff1a;这篇的内容是2022年5月写的&#xff0c;当时碰到了要批量处理数据的情况&#xff0c;但是又不知道数据为啥一直报错报错报错&#xff0c;说不允许我操作&#xff0c;最终发现是因为存在隐藏的列或行&#xff0c;于是就很无语地写了博客&#xff0c;但内容…

Codeforces Round 972 (Div. 2) E2. Subtangle Game (Hard Version)(博弈+双指针 sg函数思想)

题目 思路来源 稲葉廻代码 题解 这个题比easy version的数据范围大了比较多&#xff0c; 不能再直接dp[i][j][k]表示数组a的第i个做开始局面时&#xff0c;位置(j,k)为起点时的获胜情况了 当然你把第一维压到bitset里&#xff0c;然后前缀和优化一下&#xff0c;还是可以通…

中序遍历二叉树全过程图解

文章目录 中序遍历图解总结拓展&#xff1a;回归与回溯 中序遍历图解 首先看下中序遍历的代码&#xff0c;其接受一个根结点root作为参数&#xff0c;判断根节点是否为nil&#xff0c;不为nil则先递归遍历左子树。 func traversal(root *TreeNode,res *[]int) {if root nil …

Tomcat中间件常见漏洞复现

#1.CVE-2017-12615 -----Tomcat put方法任意文件写入漏洞 1.打开靶场 cd vulhub/tomcat/CVE-2017-12615 docker-compose up -d docker ps 2.访问8080端口&#xff0c;来到靶场 3.首页进抓包&#xff0c;Tomcat允许适⽤put⽅法上传任意⽂件类型&#xff0c;但不允许jsp后缀…

redisson 延迟队列实现任务过期监听

一、需求&#xff1a; 任务超过一个小时以后&#xff0c;如果还为待执行状态&#xff0c;则自动转为结束状态。 二、实现: 创建延迟队列的监听任务RedisDelayedQueueListener&#xff0c;消费延迟队列&#xff1b;创建新增延迟队列的类&#xff0c;用于创建延迟队列&#xf…

LeetCode 热题 100 回顾17

干货分享&#xff0c;感谢您的阅读&#xff01;原文见&#xff1a;LeetCode 热题 100 回顾_力code热题100-CSDN博客 一、哈希部分 1.两数之和 &#xff08;简单&#xff09; 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标…

HTML翻牌器:用CSS和HTML元素创造动态数字展示

HTML翻牌器&#xff1a;用CSS和HTML元素创造动态数字展示 前言 翻牌器是一种数字动态展示形式&#xff0c;在生活中常见的例如翻牌计分、翻牌时钟等。 之所以以翻牌的形式是因为其物理设计的原因使其只能滚动翻牌展示数字&#xff0c;在电子显示设备不普及时&#xff0c;使用…

PMBOK® 第六版 估算活动持续时间

目录 读后感—PMBOK第六版 目录 在项目管理中&#xff0c;尤其是在软件开发这样的复杂项目中&#xff0c;工作内容是多种多样的。从需求分析、设计、编码到测试和部署&#xff0c;每个阶段都有其独特的挑战和不确定性。 没有人能独自完成所有估算工作并做到绝对精准。估算涉及…

【Unity Shader】Special Effects(九)Vortex 旋涡(UI)

源码:[点我获取源码] 索引 Vortex 旋涡思路分析旋涡中心旋涡旋转旋涡强度旋涡动画Vortex 旋涡 旋涡效果可以将一张图像以指定点作为旋涡中心,呈顺时针旋涡动画效果,使用动画播放器: 思路分析 首先,旋涡特效的核心也即是旋转(特别是uv坐标的旋转); 在此基础上,旋涡中…

Vue(15)——组合式API②

生命周期函数 选项式组合式beforeCreate/createdsetupbeforeMountonBeforeMount mountedonMounedbeforeUpdateonBeforeUpdateupdatedonUpdatedbeforeUnmountonBeforeUnmountunmountedonUnmounted 父子通信 父传子基本思想&#xff1a; 父组件中给子组件绑定属性…