diff --git a/.vscode/settings.json b/.vscode/settings.json index e700a27..eaca38c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -143,7 +143,11 @@ "fstream": "cpp", "xmemory0": "cpp", "xstddef": "cpp", - "xtree": "cpp" + "xtree": "cpp", + "qxmlstreamwriter": "cpp", + "qgraphicsdropshadoweffect": "cpp", + "qpushbutton": "cpp", + "qpainter": "cpp" }, "editor.tokenColorCustomizations": { "textMateRules": [ diff --git a/ClientCore/ClientCore.cpp b/ClientCore/ClientCore.cpp index 40bc81a..fdf166e 100644 --- a/ClientCore/ClientCore.cpp +++ b/ClientCore/ClientCore.cpp @@ -181,6 +181,14 @@ void ClientCore::UseFrame(QSharedPointer frame) emit sigOffline(frame); break; } + case FBT_MSGINFO_ASK: { + emit sigMsgAsk(frame); + break; + } + case FBT_MSGINFO_ANSWER: { + emit sigMsgAnswer(frame); + break; + } default: qCritical() << QString("未知的帧类型: %1").arg(frame->type); break; diff --git a/ClientCore/ClientCore.h b/ClientCore/ClientCore.h index 07052fd..bb700b6 100644 --- a/ClientCore/ClientCore.h +++ b/ClientCore/ClientCore.h @@ -78,6 +78,8 @@ signals: void sigFileInfo(QSharedPointer frame); void sigOffline(QSharedPointer frame); void sigYourId(QSharedPointer frame); + void sigMsgAsk(QSharedPointer frame); + void sigMsgAnswer(QSharedPointer frame); signals: void conSuccess(); diff --git a/ClientCore/FileTrans.cpp b/ClientCore/FileTrans.cpp index d217fd5..0bba515 100644 --- a/ClientCore/FileTrans.cpp +++ b/ClientCore/FileTrans.cpp @@ -333,7 +333,6 @@ void FileTrans::SendFile(const QSharedPointer& task) auto* sendThread = new SendThread(clientCore_); sendThread->setTask(task); QMutexLocker locker(&sthMut_); - // TODO: check if already exist upTasks_[task->task.localId] = sendThread; sendThread->run(); } diff --git a/Gui/CMakeLists.txt b/Gui/CMakeLists.txt index d4d316b..2e6bec9 100644 --- a/Gui/CMakeLists.txt +++ b/Gui/CMakeLists.txt @@ -10,8 +10,8 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_definitions(-DUSE_QT_GUI) -find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network) -find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network) +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network Concurrent) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network Concurrent) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) set(PROJECT_SOURCES main.cpp @@ -26,7 +26,7 @@ Form/Transform.h Form/Transform.cpp Form/Transform.ui Control/cusTableWidget.cpp Control/cusTableWidget.h Control/cpTableWidget.h Control/cpTableWidget.cpp GuiUtil/Config.h GuiUtil/Config.cpp -../Res/qss.qrc +../Res/qss.qrc Form/Loading.h Form/Loading.cpp ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) @@ -55,8 +55,9 @@ endif() target_link_libraries(frelayGUI PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network + Qt${QT_VERSION_MAJOR}::Concurrent ClientCore Protocol - Util + Util Struct SingleApplication::SingleApplication ) diff --git a/Gui/Control/CompareControl.cpp b/Gui/Control/CompareControl.cpp index d175b89..da57725 100644 --- a/Gui/Control/CompareControl.cpp +++ b/Gui/Control/CompareControl.cpp @@ -6,6 +6,7 @@ #include #include +#include "Form/Loading.h" #include "GuiUtil/Public.h" #include "ui_CompareControl.h" @@ -80,6 +81,13 @@ void Compare::InitControl() connect(ui->btnLoad, &QPushButton::clicked, this, &Compare::Load); connect(ui->btnLeft, &QPushButton::clicked, this, &Compare::TransToLeft); connect(ui->btnRight, &QPushButton::clicked, this, &Compare::TransToRight); + + // 测试代码 + connect(ui->btnReplace, &QPushButton::clicked, this, [this]() { + // auto* loading = new LoadingDialog(this); + // loading->exec(); + }); + LoadTitles(); } diff --git a/Gui/Form/Loading.cpp b/Gui/Form/Loading.cpp new file mode 100644 index 0000000..ce6be89 --- /dev/null +++ b/Gui/Form/Loading.cpp @@ -0,0 +1,139 @@ +#include "Loading.h" + +LoadingDialog::LoadingDialog(QWidget* parent) : QDialog(parent) +{ + // 如果需要显示任务栏对话框则删除Qt::Tool + setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint); + setAttribute(Qt::WA_TranslucentBackground, true); + initUi(); +} + +/** + * @brief LoadingDialog::initUi UI元素初始化 + */ +void LoadingDialog::initUi() +{ + this->setFixedSize(250, 250); + m_pCenterFrame = new QFrame(this); + m_pCenterFrame->setGeometry(10, 10, 230, 230); + + // 加载Loading动画 + m_pLoadingMovie = new QMovie(":/ico/loading.gif"); + m_pLoadingMovie->setScaledSize(QSize(120, 120)); + m_pMovieLabel = new QLabel(m_pCenterFrame); + m_pMovieLabel->setGeometry(55, 10, 120, 120); + m_pMovieLabel->setScaledContents(true); + m_pMovieLabel->setMovie(m_pLoadingMovie); + m_pLoadingMovie->start(); + + // 提示文本 + m_pTipsLabel = new QLabel(m_pCenterFrame); + m_pTipsLabel->setGeometry(5, 130, 220, 50); + m_pTipsLabel->setAlignment(Qt::AlignCenter | Qt::AlignHCenter); + m_pTipsLabel->setObjectName("tips"); + m_pTipsLabel->setText("加载中,请稍候..."); + m_pTipsLabel->setStyleSheet("QLabel#tips{font-family:\"Microsoft YaHei\";font-size: 15px;color: #333333;}"); + + // 取消按钮 + m_pCancelBtn = new QPushButton(m_pCenterFrame); + m_pCancelBtn->setObjectName("cancelBtn"); + m_pCancelBtn->setText("取消等待"); + m_pCancelBtn->setStyleSheet("QPushButton#cancelBtn{" + "background-color: #edeef6;" + "border-radius: 4px;" + "font-family: \"Microsoft YaHei\";" + "font-size: 14px;" + "color: #333333;" + "}" + "QPushButton#cancelBtn::hover{" + "background:#dcdeea" + "}"); + m_pCancelBtn->setGeometry(25, 180, 180, 35); + m_pCancelBtn->setEnabled(true); + connect(m_pCancelBtn, &QPushButton::clicked, this, &LoadingDialog::cancelBtnClicked); + + // 实例阴影shadow + QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect(this); + shadow->setOffset(0, 0); + shadow->setColor(QColor(32, 101, 165)); + shadow->setBlurRadius(10); + this->setGraphicsEffect(shadow); +} + +/** + * @brief LoadingDialog::setTipsText 设置提示文本 + * @param strTipsText 提示文本 + */ +void LoadingDialog::setTipsText(QString strTipsText) +{ + m_pTipsLabel->setText(strTipsText); +} + +/** + * @brief LoadingDialog::setCanCancel 设置是够允许用户点击取消等待按钮 + * @param bCanCancel 是够允许 + */ +void LoadingDialog::setCanCancel(bool bCanCancel) +{ + m_pCancelBtn->setEnabled(bCanCancel); +} + +/** + * @brief LoadingDialog::moveToCenter 移动对话框到指定窗口中间 + * @param pParent 指定窗口指针 + */ +void LoadingDialog::moveToCenter(QWidget* pParent) +{ + if (pParent != nullptr && pParent != NULL) { + int nParentWidth = pParent->width(); + int nParentHeigth = pParent->height(); + + int nWidth = this->width(); + int nHeight = this->height(); + + int nParentX = pParent->x(); + int nParentY = pParent->y(); + + int x = (nParentX + (nParentWidth - nWidth) / 2); + int y = (nParentY + (nParentHeigth - nHeight) / 2); + + this->move(x, y); + } +} + +/** + * @brief LoadingDialog::cancelBtnClicked 取消按钮槽函数 + */ +void LoadingDialog::cancelBtnClicked() +{ + emit cancelWaiting(); + this->done(USER_CANCEL); +} + +/** + * @brief LoadingDialog::paintEvent 界面绘制 + * @param event + */ +void LoadingDialog::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); // 反锯齿 + painter.setBrush(QBrush(Qt::white)); + painter.setPen(Qt::transparent); + QRect rect = this->rect(); + rect.setLeft(9); + rect.setTop(9); + rect.setWidth(rect.width() - 9); + rect.setHeight(rect.height() - 9); + painter.drawRoundedRect(rect, 8, 8); + QWidget::paintEvent(event); +} + +LoadingDialog::~LoadingDialog() +{ + delete m_pLoadingMovie; + delete m_pMovieLabel; + delete m_pTipsLabel; + delete m_pCancelBtn; + delete m_pCenterFrame; +} diff --git a/Gui/Form/Loading.h b/Gui/Form/Loading.h new file mode 100644 index 0000000..9af0c81 --- /dev/null +++ b/Gui/Form/Loading.h @@ -0,0 +1,45 @@ +#ifndef LOADINGDIALOG_H +#define LOADINGDIALOG_H + +#include +#include +#include +#include +#include +#include +#define USER_CANCEL -1 + +// LoadingDialog 改造来源:https://blog.csdn.net/weixin_42105886/article/details/114665272 +class LoadingDialog : public QDialog +{ + Q_OBJECT +public: + explicit LoadingDialog(QWidget* parent = nullptr); + ~LoadingDialog(); + // 设置提示文本 + void setTipsText(QString strTipsText); + // 设置是否显示取消等待按钮 + void setCanCancel(bool bCanCancel); + // 移动到指定窗口中间显示 + void moveToCenter(QWidget* pParent); + +protected: + void paintEvent(QPaintEvent* event) override; + +private: + void initUi(); + +Q_SIGNALS: + void cancelWaiting(); + +private slots: + void cancelBtnClicked(); + +private: + QFrame* m_pCenterFrame; + QLabel* m_pMovieLabel; + QMovie* m_pLoadingMovie; + QLabel* m_pTipsLabel; + QPushButton* m_pCancelBtn; +}; +#endif // LOADINGDIALOG_H \ No newline at end of file diff --git a/Note/version.md b/Note/version.md index 86ae040..4a57532 100644 --- a/Note/version.md +++ b/Note/version.md @@ -16,7 +16,7 @@ | 23 | 问题 | 作为接收端如果对方断联要处理这个情况。 | | 0.2.2 | | | 22 | 功能 | 配置可以切组。 | | 0.2.2 | | | 21 | 功能 | 可以传输文件夹。 | | 0.2.2 | | -| 20 | 功能 | 对照传输可以打开本地文件夹。 | | 0.2.2 | | +| 20 | 功能 | 对照传输可以打开本地文件夹。 | | 0.2.2 | 0.2.3 | | 19 | 问题 | 客户之间心跳包,被动/主动失联后断开,清除相关内容,终止相关任务。 | | 0.2.2 | 0.2.3 | | 18 | 问题 | 断开后重连貌似没有发送心跳包。 | | 0.1 | 0.2 | | 17 | 问题 | 服务端主动踢出的客户端,主动重连假链接,不可用无反应。 | | 0.1 | 0.2 | diff --git a/Protocol/Protocol.h b/Protocol/Protocol.h index d52d8f5..cc03af2 100644 --- a/Protocol/Protocol.h +++ b/Protocol/Protocol.h @@ -36,9 +36,16 @@ enum FrameBufferType : uint16_t { FBT_CLI_FILE_BUFFER, FBT_CLI_TRANS_DONE, FBT_CLI_TRANS_FAILED, - FBT_CLI_FILE_INFO + FBT_CLI_FILE_INFO, + FBT_MSGINFO_ASK, + FBT_MSGINFO_ANSWER }; +// 字符串标识 +#define STRMSG_CHECK_FILE_EXIST "checkFileExist" +#define STRMSG_FILE_EXIST "fileExist" +#define STRMSG_FILE_NOT_EXIST "fileNotExist" + struct FrameBuffer { QByteArray data; QString fid; diff --git a/Res/frelay.qrc b/Res/frelay.qrc index 727aff0..3e706d6 100644 --- a/Res/frelay.qrc +++ b/Res/frelay.qrc @@ -2,5 +2,6 @@ main.ico server.ico + loading.gif diff --git a/Res/loading.gif b/Res/loading.gif new file mode 100644 index 0000000..b343823 Binary files /dev/null and b/Res/loading.gif differ diff --git a/Struct/InfoMsg.h b/Struct/InfoMsg.h index 90f094f..d530eca 100644 --- a/Struct/InfoMsg.h +++ b/Struct/InfoMsg.h @@ -9,23 +9,34 @@ struct InfoMsg { qint32 mark{}; + QString command; QString msg; QString fromPath; QString toPath; quint64 size{}; quint32 permissions{}; + QVector list; void serialize(QDataStream& data) const { - data << mark << msg << fromPath << toPath << size << permissions; + data << mark << command << msg << fromPath << toPath << size << permissions; + data << list.size(); + for (const auto& item : list) { + data << item; + } } void deserialize(QDataStream& data) { - data >> mark >> msg >> fromPath >> toPath >> size >> permissions; + data >> mark >> command >> msg >> fromPath >> toPath >> size >> permissions; + qint32 listSize; + data >> listSize; + list.resize(listSize); + for (auto& item : list) { + data >> item; + } } }; - QDataStream& operator<<(QDataStream& data, const InfoMsg& info); QDataStream& operator>>(QDataStream& data, InfoMsg& info);