QWidget多窗口使用
QWidget 多窗口使用
在 Qt 中,QWidget 是所有界面组件的基类,可以作为一个子组件嵌入到其他组件中,也可以作为独立窗口使用。当 QWidget 不设置父窗口(即 parent == nullptr),它就会成为一个顶级窗口,也就是说它会拥有自己的窗口句柄和标题栏。
QWidget 作为独立窗口的基本用法
#include <QApplication>
#include <QWidget>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget window;
window.resize(400, 300); // 设置窗口大小
window.setWindowTitle("独立QWidget窗口"); // 设置窗口标题
window.show(); // 显示窗口
return app.exec();
}
如果不设置 window.setParent(...),它就会是一个独立窗口。
常见设置
设置窗口标志(Window Flags)
可以通过 setWindowFlags 设置窗口类型:
window.setWindowFlags(Qt::Window); // 明确设置为一个窗口
其他常见的 flag 包括:
Qt::Dialog:对话框样式Qt::Tool:工具窗口(在主窗口最前,但最小化时跟随主窗口)Qt::FramelessWindowHint:无边框窗口Qt::WindowStaysOnTopHint:置顶窗口
组合使用:
window.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
设置窗口位置和大小
window.resize(800, 600); // 设置大小
window.move(100, 100); // 设置初始显示位置
窗口控制相关函数
show():显示窗口hide():隐藏窗口close():关闭窗口raise():将窗口置于顶层activateWindow():激活窗口(获取焦点)
自定义子类作为窗口
可以继承 QWidget 来创建自己的窗口类:
#include <QWidget>
#include <QPushButton>
class MyWindow : public QWidget {
public:
MyWindow() {
setWindowTitle("我的窗口");
resize(400, 300);
QPushButton *btn = new QPushButton("点击关闭", this);
btn->move(150, 130);
connect(btn, &QPushButton::clicked, this, &QWidget::close);
}
};
然后在 main.cpp 中:
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MyWindow window;
window.show();
return app.exec();
}
注意事项
- 必须调用
show()才能显示窗口。 - 如果在没有父窗口的情况下创建
QWidget,它会自动成为顶级窗口。 - 可以通过
isWindow()来判断是否为窗口。 - 如果设置了父窗口,那么这个 QWidget 将不会作为独立窗口出现。
示例:用户名密码管理
formchangepassword.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FormChangePassword</class>
<widget class="QWidget" name="FormChangePassword">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>280</width>
<height>240</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>用户名</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEditUser"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>旧密码</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditOldPassword"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>新密码</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEditNewPassword"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>新密码确认</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="lineEditNewPassword2"/>
</item>
<item row="4" column="0" colspan="2">
<widget class="QPushButton" name="pushButtonChange">
<property name="text">
<string>修改密码</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
formchangepassword.h
#ifndef FORMCHANGEPASSWORD_H
#define FORMCHANGEPASSWORD_H
#include <QCryptographicHash> // 用于计算密码的哈希值
#include <QWidget> // QWidget 是所有窗口部件的基类
// 前向声明 UI 命名空间中的 FormChangePassword 类(由 Qt Designer 生成)
namespace Ui {
class FormChangePassword;
}
/**
* @brief FormChangePassword 是一个用于修改密码的独立窗口类
*
* 该窗口接收旧密码的哈希值,用户输入新密码后进行验证并发送新密码哈希值。
*/
class FormChangePassword : public QWidget {
Q_OBJECT // 启用 Qt 的元对象系统(支持信号和槽)
public :
/**
* @brief 构造函数
* @param parent 父窗口指针,默认为 nullptr(独立窗口)
*/
explicit FormChangePassword(QWidget* parent = nullptr);
/// 析构函数,自动释放 UI 指针
~FormChangePassword();
signals:
/**
* @brief 向主窗口发送新的密码哈希
* @param strUser 用户名
* @param baNewHash 新密码的哈希值(使用 QByteArray 存储)
*/
void sendNewUserPassword(QString strUser, QByteArray baNewHash);
private slots:
/**
* @brief 当“修改密码”按钮被点击时执行的槽函数
* 用于检查旧密码是否正确,并发送新密码哈希。
*/
void on_pushButtonChange_clicked();
public slots:
/**
* @brief 接收旧密码哈希值的槽函数(由主窗口调用)
* @param strUser 用户名
* @param baOldHash 旧密码的哈希值
*/
void recvOldUserPassword(QString strUser, QByteArray baOldHash);
private:
/// 指向 UI 界面对象的指针
Ui::FormChangePassword* ui;
/// 初始化窗口的一些默认设置(如标题、提示等)
void init();
/// 保存当前用户名
QString m_strUser;
/// 保存旧密码的哈希值,用于验证用户输入的旧密码是否正确
QByteArray m_baOldHash;
};
#endif // FORMCHANGEPASSWORD_H
formchangepassword.cpp
#include "formchangepassword.h"
#include "ui_formchangepassword.h"
#include <QDebug>
#include <QMessageBox>
// 构造函数:初始化 UI 并调用自定义初始化函数
FormChangePassword::FormChangePassword(QWidget* parent) : QWidget(parent), ui(new Ui::FormChangePassword) {
ui->setupUi(this); // 设置 UI 元素(由 Qt Designer 生成)
init(); // 自定义初始化函数
}
// 析构函数:释放 UI 指针
FormChangePassword::~FormChangePassword() {
delete ui;
}
// 自定义初始化函数:设置界面样式和控件属性
void FormChangePassword::init() {
// 设置窗口标题
setWindowTitle(tr("修改用户密码"));
// 设置三个密码框为“密码模式”,隐藏输入内容
ui->lineEditOldPassword->setEchoMode(QLineEdit::Password);
ui->lineEditNewPassword->setEchoMode(QLineEdit::Password);
ui->lineEditNewPassword2->setEchoMode(QLineEdit::Password);
// 用户名只读,防止篡改,并设置背景颜色
ui->lineEditUser->setReadOnly(true);
ui->lineEditUser->setStyleSheet("background-color: rgb(200,200,255);");
// 为旧密码框设置提示信息
ui->lineEditOldPassword->setToolTip(tr("旧密码验证成功才能修改为新密码。"));
}
// 接收主窗口传来的用户名和旧密码哈希,并显示用户名
void FormChangePassword::recvOldUserPassword(QString strUser, QByteArray baOldHash) {
// 保存到成员变量中备用
m_strUser = strUser;
m_baOldHash = baOldHash;
// 显示用户名到只读框
ui->lineEditUser->setText(m_strUser);
// 清空输入框内容,准备新输入
ui->lineEditOldPassword->clear();
ui->lineEditNewPassword->clear();
ui->lineEditNewPassword2->clear();
}
// 点击“修改密码”按钮后的处理逻辑
void FormChangePassword::on_pushButtonChange_clicked() {
// 获取三个密码框的输入内容(去除前后空格)
QString strOldPassword = ui->lineEditOldPassword->text().trimmed();
QString strNewPassword = ui->lineEditNewPassword->text().trimmed();
QString strNewPassword2 = ui->lineEditNewPassword2->text().trimmed();
// 检查密码框是否为空
if (strOldPassword.isEmpty() || strNewPassword.isEmpty() || strNewPassword2.isEmpty()) {
QMessageBox::information(this, tr("密码框检查"), tr("三个密码都不能为空。"));
return;
}
// 检查两个新密码是否一致
if (strNewPassword != strNewPassword2) {
QMessageBox::information(this, tr("新密码检查"), tr("两个新密码不一致。"));
return;
}
// 使用 SHA256 对旧密码计算哈希值
QByteArray baOldHashCheck = QCryptographicHash::hash(strOldPassword.toUtf8(), QCryptographicHash::Sha256);
baOldHashCheck = baOldHashCheck.toHex(); // 转成十六进制字符串方便比较
// 验证旧密码是否正确
if (baOldHashCheck != m_baOldHash) {
QMessageBox::information(this, tr("旧密码检查"), tr("输入的旧密码不正确,不能修改密码。"));
return;
}
// 若旧密码验证通过,则计算新密码的哈希值
QByteArray baNewHash = QCryptographicHash::hash(strNewPassword.toUtf8(), QCryptographicHash::Sha256);
baNewHash = baNewHash.toHex(); // 统一格式为 Hex 字符串
// 发出信号,将新密码哈希和用户名传递回主窗口处理
emit sendNewUserPassword(m_strUser, baNewHash);
}
mainwidget.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWidget</class>
<widget class="QWidget" name="MainWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>518</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWidget</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>用户名</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEditUser"/>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>密码</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEditPassword"/>
</item>
<item>
<widget class="QPushButton" name="pushButtonAddUser">
<property name="text">
<string>添加用户</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QListWidget" name="listWidgetShow"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButtonChangePassword">
<property name="text">
<string>修改用户密码</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonDelUser">
<property name="text">
<string>删除选定用户</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include "formchangepassword.h" // 子窗口类,用于修改用户密码
#include <QCryptographicHash> // 用于生成密码的哈希值
#include <QListWidget> // UI 中的列表控件
#include <QListWidgetItem> // 列表中的每一项
#include <QMap> // 用于保存用户名与密码哈希值的映射
#include <QWidget> // QWidget 是所有窗口部件的基类
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWidget;
}
QT_END_NAMESPACE
/**
* @brief MainWidget 是应用的主窗口类,提供用户管理界面
*
* 功能包括添加用户、删除用户、修改用户密码,并将用户信息存储在 QMap 中。
*/
class MainWidget : public QWidget {
Q_OBJECT // 启用 Qt 的元对象系统(支持信号和槽)
public :
/**
* @brief 构造函数
* @param parent 父窗口指针,默认为 nullptr
*/
MainWidget(QWidget* parent = nullptr);
/// 析构函数,自动释放资源
~MainWidget();
/**
* @brief 刷新用户列表显示
* 将当前 m_mapUserAndHash 中的用户显示到 QListWidget 中
*/
void updateListShow();
signals:
/**
* @brief 向子窗口发送当前用户名及旧密码哈希
* 用于修改密码时子窗口进行旧密码验证
*/
void sendOldUserPassword(QString strUser, QByteArray baOldHash);
private slots:
/**
* @brief 添加用户按钮点击处理函数
* 获取用户名和密码,生成哈希并存入映射表
*/
void on_pushButtonAddUser_clicked();
/**
* @brief 修改密码按钮点击处理函数
* 弹出子窗口并发送当前用户信息
*/
void on_pushButtonChangePassword_clicked();
/**
* @brief 删除用户按钮点击处理函数
* 从映射表中移除选中的用户
*/
void on_pushButtonDelUser_clicked();
public slots:
/**
* @brief 接收来自子窗口的新密码哈希
* @param strUser 用户名
* @param baNewHash 新的密码哈希值
* 替换 m_mapUserAndHash 中该用户的密码
*/
void recvNewUserPassword(QString strUser, QByteArray baNewHash);
private:
/// UI 指针,管理由 Qt Designer 创建的界面元素
Ui::MainWidget* ui;
/// 保存所有用户名和其对应密码哈希的映射表
QMap<QString, QByteArray> m_mapUserAndHash;
/// 指向修改密码子窗口的指针
FormChangePassword* m_pFormChild;
/**
* @brief 初始化主窗口设置
* 包括界面初始化、信号槽连接等
*/
void init();
};
#endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h"
#include "./ui_mainwidget.h"
#include <QDebug>
#include <QMessageBox>
// 构造函数:初始化 UI 并调用 init 函数设置界面和子窗口
MainWidget::MainWidget(QWidget* parent) : QWidget(parent), ui(new Ui::MainWidget) {
ui->setupUi(this); // 加载 UI 界面
init(); // 初始化窗口、子窗口、信号槽等
}
// 析构函数:释放资源
MainWidget::~MainWidget() {
delete m_pFormChild; // 释放子窗口对象
m_pFormChild = nullptr;
delete ui; // 释放 UI 对象
}
// 初始化函数:设置窗口标题、控件属性并建立信号槽连接
void MainWidget::init() {
// 设置主窗口标题
setWindowTitle(tr("用户名密码管理工具"));
// 设置密码输入框为密码模式,输入内容以 * 显示
ui->lineEditPassword->setEchoMode(QLineEdit::Password);
// 初始化子窗口指针
m_pFormChild = nullptr;
// 创建子窗口,parent 设为 nullptr 防止父窗口控制其生命周期
m_pFormChild = new FormChangePassword(nullptr);
// 连接主窗口 -> 子窗口的信号槽:传递用户名和旧密码哈希
connect(this, &MainWidget::sendOldUserPassword, m_pFormChild, &FormChangePassword::recvOldUserPassword);
// 连接子窗口 -> 主窗口的信号槽:接收修改后的新密码哈希
connect(m_pFormChild, &FormChangePassword::sendNewUserPassword, this, &MainWidget::recvNewUserPassword);
}
// 刷新用户列表显示:根据当前的用户哈希表刷新列表控件
void MainWidget::updateListShow() {
ui->listWidgetShow->clear(); // 清空原有列表项
// 获取所有用户名(即 QMap 的 key 列表)
QList<QString> listKeys = m_mapUserAndHash.keys();
int nCount = listKeys.count();
// 遍历每个用户,将用户名和哈希拼接显示到列表中
for (int i = 0; i < nCount; i++) {
QString curKey = listKeys[i];
QString strTemp = curKey + QString("\t") + m_mapUserAndHash[curKey];
ui->listWidgetShow->addItem(strTemp);
}
}
// 添加用户按钮点击处理函数
void MainWidget::on_pushButtonAddUser_clicked() {
// 获取用户输入的用户名和密码
QString strNewUser = ui->lineEditUser->text().trimmed();
QString strPassword = ui->lineEditPassword->text().trimmed();
// 检查用户名或密码是否为空
if (strNewUser.isEmpty() || strPassword.isEmpty()) {
QMessageBox::information(this, tr("用户名密码检查"), tr("用户名或密码为空,不能添加。"));
return;
}
// 检查用户名是否已存在
if (m_mapUserAndHash.contains(strNewUser)) {
QMessageBox::information(this, tr("用户名检查"), tr("已存在该用户名,不能再新增同名。"));
return;
}
// 生成密码哈希(使用 SHA256)
QByteArray baNewHash = QCryptographicHash::hash(strPassword.toUtf8(), QCryptographicHash::Sha256);
baNewHash = baNewHash.toHex(); // 转换为十六进制字符串方便显示与存储
// 添加到用户-哈希映射表中
m_mapUserAndHash.insert(strNewUser, baNewHash);
// 刷新用户列表显示
updateListShow();
}
// 修改密码按钮点击处理函数
void MainWidget::on_pushButtonChangePassword_clicked() {
int curIndex = ui->listWidgetShow->currentRow(); // 获取当前选中行
if (curIndex < 0) return; // 没有选中任何行
// 获取当前条目
QListWidgetItem* curItem = ui->listWidgetShow->item(curIndex);
// 如果被选中,提取用户名并发送旧密码哈希到子窗口
if (curItem->isSelected()) {
QString curLine = curItem->text(); // 获取文本:"用户名\t密码哈希"
QStringList curKeyValue = curLine.split('\t'); // 拆分成用户名和哈希
QString strUser = curKeyValue[0];
QByteArray baOldHash = m_mapUserAndHash[strUser];
// 发出信号,发送用户名和旧密码哈希给子窗口
emit sendOldUserPassword(strUser, baOldHash);
// 显示修改密码的子窗口
m_pFormChild->show();
// 若子窗口被最小化,恢复原尺寸显示
if (m_pFormChild->isMinimized()) m_pFormChild->showNormal();
// 子窗口置顶
m_pFormChild->raise();
}
}
// 删除用户按钮点击处理函数
void MainWidget::on_pushButtonDelUser_clicked() {
int curIndex = ui->listWidgetShow->currentRow(); // 获取当前行索引
if (curIndex < 0) return;
QListWidgetItem* curItem = ui->listWidgetShow->item(curIndex);
if (curItem->isSelected()) {
QString curLine = curItem->text(); // "用户名\t哈希"
QStringList curKeyValue = curLine.split('\t');
QString strUser = curKeyValue[0];
// 从 map 中删除该用户
m_mapUserAndHash.remove(strUser);
// 从 UI 列表中删除条目
ui->listWidgetShow->takeItem(curIndex); // 先从列表中拿出来
delete curItem; // 然后释放内存
curItem = nullptr;
}
}
// 接收子窗口返回的新密码哈希,并更新用户哈希表
void MainWidget::recvNewUserPassword(QString strUser, QByteArray baNewHash) {
m_mapUserAndHash[strUser] = baNewHash; // 更新哈希值
updateListShow(); // 刷新列表显示
m_pFormChild->hide(); // 隐藏子窗口
QMessageBox::information(this, tr("修改密码"), tr("修改密码成功。")); // 弹窗提示
}
集成已有窗口类方式
把文件属性的例子里面的三个文件 tabpreview.cpp、tabpreview.h、tabpreview.ui 复制粘贴到本示例 fileattrshow 项目文件夹中。
tabpreview.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TabPreview</class>
<widget class="QWidget" name="TabPreview">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>578</width>
<height>415</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QPushButton" name="pushButtonTextPreview">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>52</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>文
本
预
览</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonImagePreview">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>52</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>图
像
预
览</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonBytePreview">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>52</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>字
节
预
览</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="pageTextPreview">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTextBrowser" name="textBrowserText"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageImagePreview">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labelImagePreview">
<property name="text">
<string>图像预览区域</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageBytePreview">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTextBrowser" name="textBrowserByte"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
tabpreview.h
#ifndef TABPREVIEW_H
#define TABPREVIEW_H
#include <QButtonGroup>
#include <QFile>
#include <QPixmap>
#include <QWidget>
namespace Ui {
class TabPreview;
}
class TabPreview : public QWidget {
Q_OBJECT
public:
explicit TabPreview(QWidget* parent = nullptr); // 构造函数,支持传入父窗口指针,默认空指针
~TabPreview(); // 析构函数,释放资源
void initControls(); // 初始化控件及界面设置
public slots:
// 槽函数,响应文件名改变信号,更新预览内容
void onFileNameChanged(const QString& fileName);
private:
Ui::TabPreview* ui; // 指向 UI 界面对象的指针,由 Qt Designer 生成的类
QString m_strFileName; // 用于保存当前预览的文件名
QButtonGroup m_buttonGroup; // 按钮分组,管理多个互斥的按钮
QPixmap m_image; // 用于保存当前加载的预览图像
};
#endif // TABPREVIEW_H
tabpreview.cpp
#include "tabpreview.h"
#include "ui_tabpreview.h"
TabPreview::TabPreview(QWidget* parent) : QWidget(parent), ui(new Ui::TabPreview) {
ui->setupUi(this);
initControls(); // 初始化控件,设置按钮组、样式等
}
TabPreview::~TabPreview() {
delete ui; // 释放 UI 指针资源
}
void TabPreview::initControls() {
// 设置三个按钮为可切换状态(类似复选框的选中与未选中状态)
ui->pushButtonTextPreview->setCheckable(true);
ui->pushButtonImagePreview->setCheckable(true);
ui->pushButtonBytePreview->setCheckable(true);
// 按钮分组,分组内按钮互斥(只能选中一个)
// 为每个按钮分配唯一 ID(0, 1, 2)
m_buttonGroup.addButton(ui->pushButtonTextPreview, 0);
m_buttonGroup.addButton(ui->pushButtonImagePreview, 1);
m_buttonGroup.addButton(ui->pushButtonBytePreview, 2);
// 绑定按钮分组的点击信号到堆栈控件,切换不同的页面
connect(&m_buttonGroup, &QButtonGroup::idClicked, ui->stackedWidget, &QStackedWidget::setCurrentIndex);
// 设置所有被选中按钮的样式,背景色为黄色
this->setStyleSheet("QPushButton:checked { background-color: yellow }");
// 设置字节浏览区背景颜色为浅蓝色
ui->textBrowserByte->setStyleSheet("background-color: #AAEEFF");
// 设置图片预览标签背景颜色为浅灰色
ui->labelImagePreview->setStyleSheet("background-color: #E0E0E0");
}
void TabPreview::onFileNameChanged(const QString& fileName) {
m_strFileName = fileName;
// 尝试将文件作为图片加载
bool isImage = m_image.load(m_strFileName);
if (isImage) {
// 是图片时,清空文字提示,显示图片
ui->labelImagePreview->setText("");
ui->labelImagePreview->setPixmap(m_image);
} else {
// 不是图片时,清空显示的图片,显示提示文字
m_image = QPixmap(); // 清空图片
ui->labelImagePreview->setPixmap(m_image);
ui->labelImagePreview->setText(tr("不是支持的图片,无法以图片预览。"));
}
// 打开文件,读取前200字节做文本和十六进制预览
QFile fileIn(m_strFileName);
if (!fileIn.open(QIODevice::ReadOnly)) {
// 无法打开文件,打印调试信息
qDebug() << tr("文件无法打开:") << m_strFileName;
} else {
QByteArray baData = fileIn.read(200); // 读取前200字节
// 转为本地编码的文本显示
QString strText = QString::fromLocal8Bit(baData);
ui->textBrowserText->setText(strText);
// 转为大写十六进制字符串显示
QString strHex = baData.toHex().toUpper();
ui->textBrowserByte->setText(strHex);
}
// 根据文件类型,默认切换到对应预览页
if (isImage) {
// 图片文件,切换到图片预览按钮(触发 clicked 信号)
ui->pushButtonImagePreview->click();
} else {
// 非图片文件
if (m_strFileName.endsWith(".txt", Qt::CaseInsensitive) || m_strFileName.endsWith(".h", Qt::CaseInsensitive) || m_strFileName.endsWith(".cpp", Qt::CaseInsensitive) ||
m_strFileName.endsWith(".c", Qt::CaseInsensitive)) {
// 纯文本相关文件,切换到文本预览页
ui->pushButtonTextPreview->click();
} else {
// 其他文件,切换到字节预览页
ui->pushButtonBytePreview->click();
}
}
}
fileattrwidget.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FileAttrWidget</class>
<widget class="QWidget" name="FileAttrWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>450</width>
<height>276</height>
</rect>
</property>
<property name="windowTitle">
<string>FileAttrWidget</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>文件全名</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="lineEditFullName"/>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="pushButtonSelectFile">
<property name="text">
<string>选择文件</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>文件短名</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QLineEdit" name="lineEditShortName"/>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="pushButtonPreview">
<property name="text">
<string>预览文件</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>文件大小</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="lineEditFileSize"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>创建时间</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLineEdit" name="lineEditTimeCreated"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>访问时间</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLineEdit" name="lineEditTimeRead"/>
</item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>修改时间</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLineEdit" name="lineEditTimeModified"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
fileattrwidget.h
#ifndef FILEATTRWIDGET_H
#define FILEATTRWIDGET_H
#include "tabpreview.h" // 自定义的预览窗口类
#include <QFile> // 用于文件操作
#include <QFileDialog> // 用于打开文件对话框
#include <QFileInfo> // 提供文件信息类
#include <QWidget> // QWidget 基类
QT_BEGIN_NAMESPACE
namespace Ui {
class FileAttrWidget; // 前向声明 UI 类(由 Qt Designer 自动生成)
}
QT_END_NAMESPACE
// 文件属性展示与操作的主窗口类,继承自 QWidget
class FileAttrWidget : public QWidget {
Q_OBJECT
public:
// 构造函数,支持父窗口传入
FileAttrWidget(QWidget* parent = nullptr);
// 析构函数,负责资源释放
~FileAttrWidget();
signals:
// 当用户选择了新文件,文件名发生变化时发出该信号
void fileNameChanged(const QString& fileName);
private slots:
// 点击“选择文件”按钮的槽函数,弹出文件选择对话框
void on_pushButtonSelectFile_clicked();
// 点击“预览”按钮的槽函数,打开预览窗口
void on_pushButtonPreview_clicked();
private:
Ui::FileAttrWidget* ui; // UI 指针,管理界面控件(由 Qt Designer 自动生成)
TabPreview* m_pPreviewWnd; // 文件预览子窗口指针
QString m_strFileName; // 当前选择的文件全路径名
QFileInfo m_fileInfo; // 文件信息对象,获取如大小、时间等属性
void init(); // 初始化函数,负责界面设置、信号连接等
};
#endif // FILEATTRWIDGET_H
fileattrwidget.cpp
#include "fileattrwidget.h"
#include "./ui_fileattrwidget.h"
#include <QDateTime>
#include <QDebug>
#include <QMessageBox>
// 构造函数,初始化 UI 和界面状态
FileAttrWidget::FileAttrWidget(QWidget* parent) : QWidget(parent), ui(new Ui::FileAttrWidget) {
ui->setupUi(this); // 初始化 UI 界面控件(Qt Designer 生成的界面)
init(); // 执行初始化操作
}
// 析构函数,释放资源
FileAttrWidget::~FileAttrWidget() {
delete m_pPreviewWnd; // 释放预览窗口对象
m_pPreviewWnd = nullptr;
delete ui; // 释放 UI 界面对象
}
// 初始化函数,设置控件属性、创建子窗口、连接信号槽
void FileAttrWidget::init() {
// 将所有信息展示框设置为只读,防止用户修改
ui->lineEditFullName->setReadOnly(true);
ui->lineEditShortName->setReadOnly(true);
ui->lineEditFileSize->setReadOnly(true);
ui->lineEditTimeCreated->setReadOnly(true);
ui->lineEditTimeRead->setReadOnly(true);
ui->lineEditTimeModified->setReadOnly(true);
// 初始化预览窗口指针
m_pPreviewWnd = nullptr;
m_pPreviewWnd = new TabPreview(nullptr); // 创建预览窗口实例
// 设置预览窗口标题
m_pPreviewWnd->setWindowTitle(tr("预览文件"));
// 当文件名发生变化时,将信号连接到预览窗口的槽函数以更新内容
connect(this, &FileAttrWidget::fileNameChanged, m_pPreviewWnd, &TabPreview::onFileNameChanged);
}
// “选择文件”按钮的槽函数
void FileAttrWidget::on_pushButtonSelectFile_clicked() {
// 打开文件选择对话框,允许选择任意文件
QString strName = QFileDialog::getOpenFileName(this, tr("选择文件"), tr(""), tr("All files(*)"));
strName = strName.trimmed(); // 去掉前后空格
if (strName.isEmpty()) return; // 用户未选择文件直接返回
// 记录选中的文件路径
m_strFileName = strName;
// 使用 QFileInfo 提取文件属性
m_fileInfo.setFile(m_strFileName);
// 获取文件大小(字节)
qint64 nFileSize = m_fileInfo.size();
// 设置全路径、短文件名、文件大小信息
ui->lineEditFullName->setText(m_strFileName);
ui->lineEditShortName->setText(m_fileInfo.fileName());
ui->lineEditFileSize->setText(tr("%1 字节").arg(nFileSize));
// 获取文件创建、读取、修改时间,并格式化为字符串
QString strTimeCreated = m_fileInfo.birthTime().toString("yyyy-MM-dd HH:mm:ss");
QString strTimeRead = m_fileInfo.lastRead().toString("yyyy-MM-dd HH:mm:ss");
QString strTimeModified = m_fileInfo.lastModified().toString("yyyy-MM-dd HH:mm:ss");
// 将时间信息设置到对应控件中
ui->lineEditTimeCreated->setText(strTimeCreated);
ui->lineEditTimeRead->setText(strTimeRead);
ui->lineEditTimeModified->setText(strTimeModified);
// 发送文件名变化信号,通知预览窗口更新
emit fileNameChanged(m_strFileName);
}
// “预览”按钮的槽函数
void FileAttrWidget::on_pushButtonPreview_clicked() {
if (m_strFileName.isEmpty()) return; // 如果未选择文件则不操作
// 如果预览窗口已显示,先隐藏
if (m_pPreviewWnd->isVisible()) {
m_pPreviewWnd->hide();
}
// 设置预览窗口为应用程序级模态(阻塞其他窗口操作)
m_pPreviewWnd->setWindowModality(Qt::ApplicationModal);
// 显示预览窗口
m_pPreviewWnd->show();
}

浙公网安备 33010602011771号