基于腳手架微服務的視頻點播系統-界面布局部分:二.首頁及播放界面布局
- 一.用戶界面布局
- 1.1用戶界面布局分析與實現
- 1.2更新用戶圖像按鈕及邏輯
- 1.3修改按鈕及邏輯
- 1.4上傳視頻對話框實現邏輯
- 1.4.1頁面跳轉邏輯處理
- 1.4.2頁面控件響應處理
- 二.系統界面布局
- 2.1系統管理頁框架
- 2.2審核管理頁
- 2.3角色管理頁
- 2.4審核和角色管理頁面切換
- 2.5視頻審核表項
- 2.6角色管理表項
- 2.7編輯用戶信息頁
- 2.8分頁器實現
- 三.登錄界面布局
- 四.toast窗口
我們先來看下本文結束后需要完成的界面:
1.用戶界面:

2.修改個人信息界面

3.上傳視頻界面

4.系統管理界面-視頻審核與角色管理


5.登錄注冊界面

一.用戶界面布局
1.1用戶界面布局分析與實現
當用戶點擊"我的"頁面切換按鈕時,就會顯示我的頁面。仔細觀察發現,我的頁面整體屬于上下結構布局,從上往下依次為:基本信息區、我的視頻區、視頻信息顯?區,每個視頻信息框可以復用VideoBox。
所以我們可以新增一個ui設計師類,類名為ModifyMyselfDialog然后在其ui界面進行布局如下:
界面控件嵌套關系如下:
和前文一樣,詳細的布局信息以及qss樣式代碼可以在本項目更新完畢之后的最后一篇博客置頂獲取源碼進行參考,這里我們就不羅列了為了避免文章長度冗余。
1.2更新用戶圖像按鈕及邏輯
首先我的界面不會出現關注按鈕,所以這里我們需要先隱藏掉:
/////myselfwidget.cpp-在myselfwidget類中添加一個ui初始化私有成員函數initUi(),在構造函數中調用
void MyselfWidget::initUi()
{ui->attentionBtn->hide();//測試添加視頻for(int i = 0;i < 16;i++){VideoBox* box = new VideoBox();ui->layout->addWidget(box,i /4,i % 4);}
}
然后當用戶鼠標放到頭像位置時會有如下效果,點擊之后會彈出對話框讓用戶選擇圖片去設置頭像:
顯然QPushButton無法解決此問題,此時我們就需要自定義一個類繼承自QPushButton來實現此效果。我們新建一個普通類名為AvatarButton,函數頭文件與cpp文件詳情實現如下:
/////////////avatarbutton.h
#ifndef AVATARBUTTON_H
#define AVATARBUTTON_H#include <QPushButton>
#include <QLabel>class AvatarButton : public QPushButton
{Q_OBJECT
public:explicit AvatarButton(QWidget *parent = nullptr);//重寫鼠標進入與離開事件以顯示或隱藏黑色遮罩void enterEvent(QEnterEvent* event) override;void leaveEvent(QEvent* event) override;//設置是否拿掉遮罩void isHideMask(bool ishide);
private://設置點擊之后的槽函數void onAvatarBtnClicked();
private:QLabel* mask;//黑色遮罩bool maskHide;//設置是否拿掉遮罩默認不拿掉
};#endif // AVATARBUTTON_H////////////avatarbutton.cpp
#include "avatarbutton.h"
#include "util.h"
#include <QFileDialog>AvatarButton::AvatarButton(QWidget *parent): QPushButton{parent},maskHide(true)
{//初始化遮罩mask = new QLabel(this);//黑色遮罩字體顏色為白色mask->setStyleSheet("background-color : rgba(0,0,0,0.5);""color : #FFFFFF;""border-radius : 30px;");mask->setText("修改頭像");mask->setGeometry(0,0,60,60);//因為我們頭像編輯框設置的是60*60mask->setAlignment(Qt::AlignCenter);//設置文本居中mask->hide();//連接信號槽connect(this,&QPushButton::clicked,this,&AvatarButton::onAvatarBtnClicked);
}void AvatarButton::enterEvent(QEnterEvent *event)
{if(maskHide){mask->show();}
}void AvatarButton::leaveEvent(QEvent *event)
{mask->hide();
}void AvatarButton::isHideMask(bool ishide)
{//設置mask遮罩的可見性maskHide = ishide;
}void AvatarButton::onAvatarBtnClicked()
{//參數含義:設置父元素|設置窗口標題|設置起始目錄|設置目標類型文件QString filePath = QFileDialog::getOpenFileName(nullptr,"選擇圖片","C:\\","Image Files (*.jpg *.png)");if(filePath.isEmpty()){LOG() << "取消選擇頭像";return;}//這里因為之后我們需要將數據傳到服務端,所以需要先將該數據轉化為二進制流-新增工具函數QByteArray byteArray = loadFileToByteArray(filePath);if(byteArray.isEmpty()){//說明用戶上傳的文件無法打開或有誤-不再設置頭像return;}//裁剪并設置用戶上傳的頭像this->setIcon(makeCircleIcon(byteArray,30));//上傳圖片數據到服務端//...
}
hover效果的顯示就是一個簡單的widget遮罩很簡單上面代碼已經很詳細了。但是注意到我們這里有loadFileToByteArray與makeCircleIcon函數,為什么呢,因為用戶在上傳頭像時,我們不僅僅需要將頭像文件上傳至服務器,而且它選擇的圖片不一定是60*60大小的,同時我們的圖片還是圓形,所以這里我們就需要對用戶上傳的頭像文件先轉化為二進制流同時對該圖像進行裁剪:
//////////util.h-新增
//將上傳的文件轉化為二進制流
static inline QByteArray loadFileToByteArray(const QString& filePath)
{QFile file(filePath);//以只讀方式打開if(!file.open(QIODevice::ReadOnly)){LOG() << "文件打開失敗";return QByteArray();}QByteArray res = file.readAll();file.close();return res;
}
//將二進制流轉化為圖片并裁剪為圓形作為頭像
static inline QIcon makeCircleIcon(const QByteArray& byteArray, int radius){QPixmap pixmap;pixmap.loadFromData(byteArray);if (pixmap.isNull()) {//說明用戶上傳的不是圖片文件return QIcon();}// 把 pixmap 縮放到指定的 2*radius ??. IgnoreAspectRatio 忽略?寬?;// SmoothTransformation 平滑縮放, 獲得更?的圖?質量, 但是會犧牲?定速度.pixmap = pixmap.scaled(2*radius, 2*radius, Qt::IgnoreAspectRatio,Qt::SmoothTransformation);// 構造繪圖設置,可以理解成畫圖的畫布QPixmap output = QPixmap(pixmap.size());output.fill(Qt::transparent); // 設置透明背景QPainter painter(&output);// 設置抗鋸齒 | 縮放圖片時使用雙線性或更好的濾波算法,產生平滑效果painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);// 創建圓形路徑QPainterPath path;path.addEllipse(0, 0, 2*radius, 2*radius);// 設置裁剪路徑,裁剪路徑的作?是限制繪圖操作的范圍,只有在裁剪路徑內的區域才會被裁剪painter.setClipPath(path);// 繪制圓形圖?painter.drawPixmap(0, 0, pixmap);// 結束繪制:end()內部會釋放與繪圖設備相關的資源,確保繪圖命令都能夠被正確執?painter.end();QIcon icon(output);return icon;
}
打開myselfwidget.ui,選中avatarBtn按鈕,將類型提升為AvatarButton,運?程序就能看到更新圖像按鈕的效果。那么此時就可以在我的界面設置頭像了。
1.3修改按鈕及邏輯
當用戶點擊修改按鈕后,能夠彈出修改對話框,用戶可以對自己的昵稱、密碼信息進?修改。添加?個設計師界?,類名稱設置為ModifyMyselfDialog然后我們在ui界面布局如下:
控件嵌套關系如下:
alt+shift+r即可看到如下預覽界面:
給我的??中的settingBtn按鈕綁定槽函數,然后在槽函數中顯?ModifyMyselfDialog對話框。注意ModifyMyselfDialog需要繼承?QDialog,并設置去掉邊框。
//////////////////////////////// myselfwidget.h
///////////////////////////////////
class MyselfWidget : public QWidget
{Q_OBJECT
private://個人信息修改按鈕被點擊void onModifyMyselfClicked();
private:Ui::MyselfWidget *ui;
};
///////myselfwidget.cpp
void MyselfWidget::initUi()
{connect(ui->settingBtn,&QPushButton::clicked,this,&MyselfWidget::onModifyMyselfClicked);
}void MyselfWidget::onModifyMyselfClicked()
{ModifyMyselfDialog* modify = new ModifyMyselfDialog();modify->exec();//模態顯示delete modify;
}
/////////////////////////modifymyselfdialog.h
#ifndef MODIFYMYSELFDIALOG_H
#define MODIFYMYSELFDIALOG_H#include <QDialog>namespace Ui {
class ModifyMyselfDialog;
}class ModifyMyselfDialog : public QDialog
{Q_OBJECTpublic:explicit ModifyMyselfDialog(QWidget *parent = nullptr);~ModifyMyselfDialog();
private:void onSubmitBtnClicked();void showPasswordDlg();
private:Ui::ModifyMyselfDialog *ui;
};#endif // MODIFYMYSELFDIALOG_H///////////////modifymyselfdialog.cpp
#include "modifymyselfdialog.h"
#include "ui_modifymyselfdialog.h"
#include "util.h"
#include "newpassworddialog.h"ModifyMyselfDialog::ModifyMyselfDialog(QWidget *parent): QDialog(parent), ui(new Ui::ModifyMyselfDialog)
{ui->setupUi(this);//默認隱藏修改密碼后的widgetui->passwordWidget->hide();//設置背景透明 | 無邊框setWindowFlag(Qt::FramelessWindowHint);setAttribute(Qt::WA_TranslucentBackground);//連接信號槽connect(ui->cancelBtn,&QPushButton::clicked,this,&ModifyMyselfDialog::close);connect(ui->submitBtn,&QPushButton::clicked,this,&ModifyMyselfDialog::onSubmitBtnClicked);connect(ui->passwordBtn, &QPushButton::clicked, this,&ModifyMyselfDialog::showPasswordDlg);connect(ui->changePasswordBtn, &QPushButton::clicked, this,&ModifyMyselfDialog::showPasswordDlg);
}ModifyMyselfDialog::~ModifyMyselfDialog()
{delete ui;
}void ModifyMyselfDialog::onSubmitBtnClicked()
{LOG() << "用戶信息已修改";//上傳修改信息到服務端close();
}void ModifyMyselfDialog::showPasswordDlg()
{NewPasswordDialog* newPassWordDialog = new NewPasswordDialog();newPassWordDialog->exec();const QString currentPassword = newPassWordDialog->getNewPassWord();if(currentPassword.isEmpty()){LOG() << "密碼修改已取消";}else{LOG() << "新密碼已設置" << currentPassword;ui->passwordWidget->show();ui->passwordBtn->hide();}delete newPassWordDialog;
}
因為彈出修改個?資料對話框之后,點擊設置密碼按鈕,會彈出設計密碼對話框,讓??完成密碼修改。這里我們需要新建一個ui設計師類名為NewPasswordDialog,在其ui文件中布局如下:
控件的嵌套關系如下:
當用戶點擊修改密碼時,就能顯?出修改密碼對話框。上面的showPasswordDlg就是用來控制其顯示的函數。但是??在修改密碼時,密碼是有限制的:密碼由數字、?寫英?字?、?寫英?字?、特殊字符組成,至少包含兩種類型,?度要求8-16位字符。因此當用戶前后兩次密碼輸?完成之后,需要驗證是否滿足密碼限制,以及前后兩次輸?的密碼是否相等。所以我們可以這樣設計NewPasswordDialog類:
///////////////newpassworddialog.h
#ifndef NEWPASSWORDDIALOG_H
#define NEWPASSWORDDIALOG_H#include <QDialog>namespace Ui {
class NewPasswordDialog;
}class NewPasswordDialog : public QDialog
{Q_OBJECTpublic:explicit NewPasswordDialog(QWidget *parent = nullptr);const QString& getNewPassWord();~NewPasswordDialog();private:void onSubmitBtnClicked();void onEditingFinished();bool checkPasswordEdit();
private:Ui::NewPasswordDialog *ui;QString newPassWord;
};#endif // NEWPASSWORDDIALOG_H
///////////////newpassworddialog.cpp
#include "newpassworddialog.h"
#include "ui_newpassworddialog.h"NewPasswordDialog::NewPasswordDialog(QWidget *parent): QDialog(parent), ui(new Ui::NewPasswordDialog)
{ui->setupUi(this);//設置背景透明 | 無邊框setWindowFlag(Qt::FramelessWindowHint);setAttribute(Qt::WA_TranslucentBackground);//連接信號槽connect(ui->cancelBtn,&QPushButton::clicked,this,&NewPasswordDialog::close);connect(ui->submitBtn,&QPushButton::clicked,this,&NewPasswordDialog::onSubmitBtnClicked);connect(ui->passwordEdit1,&QLineEdit::editingFinished,this,&NewPasswordDialog::onEditingFinished);connect(ui->passwordEdit2,&QLineEdit::editingFinished,this,&NewPasswordDialog::onEditingFinished);
}const QString &NewPasswordDialog::getNewPassWord()
{return newPassWord;
}NewPasswordDialog::~NewPasswordDialog()
{delete ui;
}void NewPasswordDialog::onSubmitBtnClicked()
{if(!checkPasswordEdit())return;newPassWord = ui->passwordEdit1->text();close();
}bool NewPasswordDialog::checkPasswordEdit()
{//核驗密碼的正確性//1.檢驗輸入密碼是否為空if(ui->passwordEdit1->text().isEmpty()){ui->messageLabel->setText("設置的新密碼不能為空!");return false;}if(ui->passwordEdit2->text().isEmpty()){ui->messageLabel->setText("兩次輸入的密碼不一致!");return false;}//2.檢驗輸入的密碼長度以及是否至少包含兩種字符if(ui->passwordEdit1->text().size() < 8 || ui->passwordEdit2->text().size() > 16){ui->messageLabel->setText("密碼長度不合法!");return false;}QVector<bool> kinds(4,false);int kindsNum = 0;for(auto& c : ui->passwordEdit1->text()){if(QChar(c).isDigit()){kindsNum += kinds[0] ? 0 : 1;kinds[0] = true;}else if(QChar(c).isUpper()){kindsNum += kinds[1] ? 0 : 1;kinds[1] = true;}else if(QChar(c).isLower()){kindsNum += kinds[2] ? 0 : 1;kinds[2] = true;}else if(QChar(c).isPunct()){kindsNum += kinds[3] ? 0 : 1;kinds[3] = true;}else{ui->messageLabel->setText("密碼中含有非法字符!");return false;}}if(kindsNum < 2){ui->messageLabel->setText("密碼中必須包含兩種及以上字符!");return false;}//3.檢驗兩次輸入密碼是否一致if(ui->passwordEdit1->text() != ui->passwordEdit2->text()){ui->messageLabel->setText("兩次輸入的密碼不一致!");return false;}//到這里說明密碼合法,清空錯誤信息ui->messageLabel->setText("");return true;
}void NewPasswordDialog::onEditingFinished()
{checkPasswordEdit();
}
1.4上傳視頻對話框實現邏輯
視頻信息對話框中元素整體為上下結構,因為界?中元素較多,頁面中顯示不下,因此界?中所有元
素都處于QScrollArea中,界?結構?致如下:
那么接下來我們就新建一個ui設計師類名為UploadVideoPage,然后在其ui界面中進行布局:
控件嵌套關系如下:
布局完畢并設置完樣式后,預覽下就能看到如下畫面:
1.4.1頁面跳轉邏輯處理
由于上傳視頻頁面和首頁、我的頁面在相同位置顯示,因此需要再LimePlayer的stackedWidget中添加?個新頁面,objectName修改為uploadVideo,然后將其類型提升為UploadVideoPage。 當用戶點擊我的頁面中上傳視頻按鈕,并且選擇了需要上傳的視頻時,我的頁面會發送?個jumpToMyPage(ButtonType buttontype)信號,由LimePlayer處理該信號將頁面切換到上傳視頻頁面,當用戶填好視頻信息點擊提交按鈕時再跳回我的頁面:
///////////////uploadvideopage.h
#ifndef UPLOADVIDEOPAGE_H
#define UPLOADVIDEOPAGE_H#include <QWidget>
#include "pageswitchbutton.h"namespace Ui {
class UploadVideoPage;
}class UploadVideoPage : public QWidget
{Q_OBJECTpublic:explicit UploadVideoPage(QWidget *parent = nullptr);//視頻信息提交完畢后跳轉到我的頁面void onCommitBtnClicked();~UploadVideoPage();signals:void jumpToMyPage(ButtonType buttontype);
};#endif // UPLOADVIDEOPAGE_H
///////////////////uploadvideopage.cpp
UploadVideoPage::UploadVideoPage(QWidget *parent): QWidget(parent), ui(new Ui::UploadVideoPage)
{ui->setupUi(this);//當用戶提交完之后跳轉到我的頁面connect(ui->commitBtn,&QPushButton::clicked,this,&UploadVideoPage::onCommitBtnClicked);
}void UploadVideoPage::onCommitBtnClicked()
{if(ui->videoTitle->text().isEmpty()){//如果標題和簡介為空不允許提交//Toast::showToast("標題不可以為空T_T~~~");-后面設置return;}LOG() << "視頻詳細信息已填好,準備上傳服務端";emit jumpToMyPage(MyPageBtn);
}//記得在構造函數處連接信號槽/////////////////limeplayer.cpp
void MyselfWidget::initUi()
{connect(ui->uploadVideoBtn,&QPushButton::clicked,this,&MyselfWidget::onUpLoadVideoBtnClicked);
}void LimePlayer::connectSignalAndSlot()
{//當視頻上傳完畢之后跳轉到我的頁面connect(ui->uploadVideo,&UploadVideoPage::jumpToMyPage,this,&LimePlayer::switchPage);//連接上傳視頻頁面的信號connect(ui->myPage,&MyselfWidget::switchUploadVideoPage,this,&LimePlayer::switchPage);
}
////////////////////////pageswitchbutton.h
//標記按鈕類型
enum ButtonType{HomePageBtn,MyPageBtn,SysPageBtn,UpLoadPageBtn//-新增類型
};
////////////////////////myselfwidget.h
private://上傳視頻按鈕被點擊void onUpLoadVideoBtnClicked();
signals:void switchUploadVideoPage(ButtonType pageIndex);
/////////////////////////myselfwidget.cpp
void MyselfWidget::onUpLoadVideoBtnClicked()
{//彈出打開?件對話框,讓??選擇要上傳的視頻?件QString videoFilePath = QFileDialog::getOpenFileName(nullptr, "上傳視頻","","Videos(*.mp4 *.rmvb *.avi *.mov)");if(!videoFilePath.isEmpty()){// 視頻?小限制,上限為4GQFileInfo fileInfo(videoFilePath);qint64 fileSize = fileInfo.size();qlonglong maxVideoSize = 4294967296;if(fileSize > maxVideoSize){LOG()<<"視頻文件必須小于4G";return;}emit switchUploadVideoPage(UpLoadPageBtn);}
}
1.4.2頁面控件響應處理
標題框和簡介框的?本有字數限制,隨著用戶不斷輸?,要能讓用戶看到還剩余多少字可以輸?。因此需要捕獲標題QLineEdit和簡介QPlainTextEdit的QLineEdit::textChanged信號,在綁定的槽函數中實現剩余字數提示功能。
//////////////////uploadvideopage.h
#ifndef UPLOADVIDEOPAGE_H
#define UPLOADVIDEOPAGE_H#include <QWidget>
#include "pageswitchbutton.h"namespace Ui {
class UploadVideoPage;
}class UploadVideoPage : public QWidget
{Q_OBJECTpublic:void onVideoTitleChanged(const QString& text);void onPlainTextEditChanged();
private:const int maxTitleTextSize = 80;const int maxPlainTextSize = 1000;
};#endif // UPLOADVIDEOPAGE_H/////////////////////////uploadvideopage.cpp
#include "uploadvideopage.h"
#include "ui_uploadvideopage.h"
#include "util.h"
#include "toast.h"
#include <QMessageBox>UploadVideoPage::UploadVideoPage(QWidget *parent): QWidget(parent), ui(new Ui::UploadVideoPage)
{//限制二者的輸入字數ui->videoTitle->setMaxLength(maxTitleTextSize);connect(ui->videoTitle,&QLineEdit::textChanged,this,&UploadVideoPage::onVideoTitleChanged);connect(ui->plainTextEdit,&QPlainTextEdit::textChanged,this,&UploadVideoPage::onPlainTextEditChanged);
}void UploadVideoPage::onVideoTitleChanged(const QString& text)
{//同步改變后綴文本int Size = text.size();ui->leftWord->setText(QString::number(Size) + "/" + QString::number(maxTitleTextSize));
}void UploadVideoPage::onPlainTextEditChanged()
{//同步改變后綴文本QString currentText = ui->plainTextEdit->toPlainText();int currentLength = currentText.size();// 如果當前字符數超過限制if (currentLength > maxPlainTextSize) {// 阻塞信號,防止在setPlainText時再次觸發textChanged信號導致遞歸ui->plainTextEdit->blockSignals(true);// 截取前maxPlainTextSize個字符QString newText = currentText.left(maxPlainTextSize);ui->plainTextEdit->setPlainText(newText);//設置光標位置到文本末尾QTextCursor cursor = ui->plainTextEdit->textCursor();cursor.movePosition(QTextCursor::End);ui->plainTextEdit->setTextCursor(cursor);// 解阻塞信號ui->plainTextEdit->blockSignals(false);}//同步改變后綴文本currentLength = std::min(currentLength,maxPlainTextSize);ui->briefLeftWord->setText(QString::number(currentLength) + "/" + QString::number(maxPlainTextSize));
}
視頻封?圖默認使用視頻首幀,如果用戶想要更換視頻??封?圖時,可以點擊更改封?圖按鈕,從磁盤選擇本地圖?作為視頻封?圖,注意圖?寬??為4:3:
//////////////////////uploadvideopage.h
#ifndef UPLOADVIDEOPAGE_H
#define UPLOADVIDEOPAGE_H#include <QWidget>
#include "pageswitchbutton.h"namespace Ui {
class UploadVideoPage;
}class UploadVideoPage : public QWidget
{Q_OBJECTpublic:explicit UploadVideoPage(QWidget *parent = nullptr);
private://這里我們將所有的槽函數設置為私有//當標題與簡介文本改變時同步字數顯示void onVideoTitleChanged(const QString& text);void onPlainTextEditChanged();//視頻信息提交完畢后跳轉到我的頁面void onCommitBtnClicked();//當用戶想要自己設置視頻封面時選取視頻封面并設置void onChangeBtnClicked();
};#endif // UPLOADVIDEOPAGE_H////////////////uploadvideopage.cpp
#include "uploadvideopage.h"
#include "ui_uploadvideopage.h"
#include "util.h"
#include "toast.h"
#include <QMessageBox>UploadVideoPage::UploadVideoPage(QWidget *parent): QWidget(parent), ui(new Ui::UploadVideoPage)
{ui->setupUi(this);//相應用戶修改視頻封面信息的操作connect(ui->changeButton,&QPushButton::clicked,this,&UploadVideoPage::onChangeBtnClicked);
}void UploadVideoPage::onChangeBtnClicked()
{QString imagePath = QFileDialog::getOpenFileName(nullptr,"選取視頻封面","","Images (*.png *.xpm *.jpg)");if(!imagePath.isEmpty()){//設置視頻封面圖片并對圖片進行裁剪QPixmap image(imagePath);//忽略原圖像寬高比 | 設置為平滑縮放-scaled返回的是一個新的pixmap對象,而不會修改原圖像image = image.scaled(ui->imageLabel->size(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation);ui->imageLabel->setPixmap(image);//如果不立即重繪會導致圖片顯示是沒有縮放的狀態repaint();}
}
標簽和分類后序在進行處理。
二.系統界面布局
當點擊主頁中系統管理頁面切換按鈕時,應切換到系統管理??。系統管理??持兩個頁面:審核管理和??管理。審核管理頁面中系統管理員對??上傳的視頻進?審核、上架和下架處理,??管理頁面主要是新增、編輯管理員信息,啟?和禁?管理員賬號等。
仔細觀察我們剛開始給出的系統管理頁面的顯示,頁面中僅僅的數據展示操作區不同外,其余基本是?樣的,因此在界?設計時可以考慮將公共部分提取出來作為基類,然后讓審核管理和??管理??去繼承該基類可以重復代碼冗余。但是qt designer設計的ui界?本?不?持繼承,想要達到繼承復用減少代碼冗余,審核管理和??管理操作和展?部分可以?純代碼方式實現。
2.1系統管理頁框架
新建?個qt設計師界面,命名為AdminWidget,然后我們在其ui設計師界面進行布局如下:
控件嵌套關系如下:
審核管理和??管理頁面實現完后,將container中checkTable和roleTable的類型替換掉,系統管理頁面就完成了。(他倆初始是個QWidget)。
2.2審核管理頁
審核頁面整體為上下結構,從上往下依次為編輯選擇按鈕區,視頻信息顯示區和分頁器區域,編輯選擇按鈕區域中用戶可以進?條件查詢等,視頻信息展示區主要展?各視頻信息,每條展示視頻信息的控件需要用戶?定義;如果?個視頻比較多頁面展示不下時,可以通過分頁器向前向后翻頁或快速定位指定區域,因此分頁器也需要用戶?定義。
那我們先來實現審核管理頁吧,添加?個設計師界?,命名為CheckTable,然后在ui界面進行布局如下:
其控件嵌套關系如下:
設置完成后點擊adminwidget.ui,將checkTable的類型提升為CheckTable。運行程序之后就有最開始展示的效果了(當然此時界面中沒有任何條目)。
在審核管理頁面,管理員通過用戶id查看用戶上傳的視頻,也可以根據視頻狀態查看該狀態下的視頻。用戶id只能是大于0的整數,因此需要對用戶ID編輯框進行條件限制。當然當??點擊重置和查看按鈕后,按鈕也需要響應對應的操作。
/////////////////checktable.h
#ifndef CHECKTABLE_H
#define CHECKTABLE_H#include <QWidget>namespace Ui {
class CheckTable;
}class CheckTable : public QWidget
{Q_OBJECTpublic:explicit CheckTable(QWidget *parent = nullptr);~CheckTable();private:void onResetBtnClicked();void onQueryBtnClicked();private:Ui::CheckTable *ui;
};#endif // CHECKTABLE_H
//////////////////////////checktable.cpp
#include "checktable.h"
#include "ui_checktable.h"
#include "util.h"
#include "checktableitem.h"
#include "paginator.h"
#include <QRegularExpressionValidator>
#include <QRegularExpression>CheckTable::CheckTable(QWidget *parent): QWidget(parent), ui(new Ui::CheckTable)
{ui->setupUi(this);ui->videoStatus->addItem("全部分類");ui->videoStatus->addItem("待審核");ui->videoStatus->addItem("審核通過");ui->videoStatus->addItem("審核駁回");ui->videoStatus->addItem("已下架");ui->videoStatus->addItem("轉碼中");ui->videoStatus->setCurrentIndex(0);//設置正則表達式以限制用戶Id的輸入QRegularExpression regExp("^[0-9a-f]{4}-[0-9a-f]{8}-[0-9a-f]{4}$");QRegularExpressionValidator* validator = new QRegularExpressionValidator(regExp,this);//設置驗證器ui->userIdEdit->setValidator(validator);//連接重置按鈕與查詢按鈕的信號槽connect(ui->resetBtn,&QPushButton::clicked,this,&CheckTable::onResetBtnClicked);connect(ui->queryBtn,&QPushButton::clicked,this,&CheckTable::onQueryBtnClicked);
}CheckTable::~CheckTable()
{delete ui;
}void CheckTable::onResetBtnClicked()
{ui->userIdEdit->setText("");ui->videoStatus->setCurrentIndex(0);LOG() << "重置按鈕被點擊";
}void CheckTable::onQueryBtnClicked()
{LOG() << "查詢按鈕被點擊";
}
2.3角色管理頁
??管理頁面和審核頁面布局形式幾乎相同,直接參考審核??布局基本可以完成角色頁面布局,添加?個設計師界?,命名為RoleTable然后在其ui界面中進行布局:
其控件嵌套關系如下:
設置完成之后,打開adminWidget.ui,將roleTable的類型提升為RoleTable。在??管理頁面,管理員通過用戶?機號查看用戶信息,因此?機號編輯框需要進行輸入限制。此外管理員也可以根據用戶狀態查看該狀態下的所有用戶信息,因此用戶狀態的初始數據需要提前設置好。此外重置和審核按鈕點擊時,也需要處理相應的邏輯:
///////////////////////roletable.h
#ifndef ROLETABLE_H
#define ROLETABLE_H#include <QWidget>namespace Ui {
class RoleTable;
}class RoleTable : public QWidget
{Q_OBJECTpublic:explicit RoleTable(QWidget *parent = nullptr);void updateRoleTable();~RoleTable();private:void onResetBtnClicked();void onQueryBtnClicked();void onInsertBtnClicked();private:Ui::RoleTable *ui;
};#endif // ROLETABLE_H
/////////////////////////roletable.cpp
#include "roletable.h"
#include "ui_roletable.h"
#include "util.h"
#include "roletableitem.h"
#include "edituserdialog.h"
#include "paginator.h"
#include <QRegularExpressionValidator>
#include <QRegularExpression>RoleTable::RoleTable(QWidget *parent): QWidget(parent), ui(new Ui::RoleTable)
{ui->setupUi(this);ui->userStatus->addItem("全部分類");ui->userStatus->addItem("啟?");ui->userStatus->addItem("停?");ui->userStatus->setCurrentIndex(0);//設置正則表達式以限制手機號的輸入QRegularExpression regExp("^1\\d{10}$");QRegularExpressionValidator* validator = new QRegularExpressionValidator(regExp,this);//設置驗證器ui->phone->setValidator(validator);//連接重置按鈕與查詢按鈕與新增按鈕的信號槽connect(ui->resetBtn,&QPushButton::clicked,this,&RoleTable::onResetBtnClicked);connect(ui->queryBtn,&QPushButton::clicked,this,&RoleTable::onQueryBtnClicked);
}RoleTable::~RoleTable()
{delete ui;
}void RoleTable::onResetBtnClicked()
{ui->phone->setText("");ui->userStatus->setCurrentIndex(0);LOG() << "審核界面重置按鈕被按下";
}void RoleTable::onQueryBtnClicked()
{LOG() << "審核界面查詢按鈕被按下";
}
2.4審核和角色管理頁面切換
當用戶點擊審核管理和角色管理按鈕時,頁面需要在審核管理頁和角色管理頁之間切換。實現原理?常簡單,就直接切換stackedWidget界面即可。
//////////adminwidget.h
#ifndef ADMINWIDGET_H
#define ADMINWIDGET_H#include <QWidget>namespace Ui {
class AdminWidget;
}class AdminWidget : public QWidget
{Q_OBJECTpublic:explicit AdminWidget(QWidget *parent = nullptr);~AdminWidget();
private:void onCheckBtnClicked();void onRoleBtnClicked();
private:Ui::AdminWidget *ui;const QString selectedStyle = "background-color: #FFFFFF;""font-size: 14px;""color: #3ECEFF;""border:none;""border-bottom: 2px solid #3ECEFF;""font-weight:bold;";const QString unSelectedStyle = "background-color: #FFFFFF;""font-size: 14px;""color: #666666;""border:none;""border-bottom: 2px solid #F5F6F8;";
};#endif // ADMINWIDGET_H/////////////////adminwidget.cpp
#include "adminwidget.h"
#include "ui_adminwidget.h"AdminWidget::AdminWidget(QWidget *parent): QWidget(parent), ui(new Ui::AdminWidget)
{ui->setupUi(this);//連接checkBtn與roleBtn的點擊信號槽函數connect(ui->checkBtn,&QPushButton::clicked,this,&AdminWidget::onCheckBtnClicked);connect(ui->roleBtn,&QPushButton::clicked,this,&AdminWidget::onRoleBtnClicked);
}AdminWidget::~AdminWidget()
{delete ui;
}void AdminWidget::onCheckBtnClicked()
{//切換頁面ui->stackedWidget->setCurrentIndex(0);//更改按鈕樣式ui->checkBtn->setStyleSheet(selectedStyle);ui->roleBtn->setStyleSheet(unSelectedStyle);
}void AdminWidget::onRoleBtnClicked()
{//切換頁面ui->stackedWidget->setCurrentIndex(1);//更改按鈕樣式ui->roleBtn->setStyleSheet(selectedStyle);ui->checkBtn->setStyleSheet(unSelectedStyle);
}
2.5視頻審核表項
添加?個Qt設計師界?,命名為CheckTableItem然后在其ui界面中進行布局:
控件嵌套關系如下:
此時我們在checktable中添加更新函數就能有文章剛開始展示的效果了:
////////checktable.h
#ifndef CHECKTABLE_H
#define CHECKTABLE_H#include <QWidget>namespace Ui {
class CheckTable;
}class CheckTable : public QWidget
{void updateCheckTable();
};#endif // CHECKTABLE_H///////////////////checktable.cpp
#include "checktable.h"
#include "ui_checktable.h"
#include "util.h"
#include "checktableitem.h"
#include "paginator.h"
#include <QRegularExpressionValidator>
#include <QRegularExpression>CheckTable::CheckTable(QWidget *parent): QWidget(parent), ui(new Ui::CheckTable)
{//更新checkTable頁面updateCheckTable();
}void CheckTable::updateCheckTable()
{//測試代碼for(int i = 0;i < 20;i++){CheckTableItem* item = new CheckTableItem();ui->layout->addWidget(item);}
}
2.6角色管理表項
添加?個Qt設計師界面,命名為RoleTableItem,然后在其ui文件中進行布局如下:
控件嵌套關系如下:
然后我們像checkTable那樣給他設置幾個到界面中就能有開頭展示的效果了:
/////roletableitem.h
#ifndef ROLETABLEITEM_H
#define ROLETABLEITEM_H#include <QWidget>namespace Ui {
class RoleTableItem;
}class RoleTableItem : public QWidget
{Q_OBJECTpublic:explicit RoleTableItem(QWidget *parent = nullptr,int serialNumber = 0);void updateSerialNumber(int serialNumber);~RoleTableItem();
private:Ui::RoleTableItem *ui;
};#endif // ROLETABLEITEM_H///////roletableitem.cpp
#include "roletableitem.h"
#include "ui_roletableitem.h"
#include "edituserdialog.h"RoleTableItem::RoleTableItem(QWidget *parent,int serialNumber): QWidget(parent), ui(new Ui::RoleTableItem)
{ui->setupUi(this);//初始化序號updateSerialNumber(serialNumber);
}void RoleTableItem::updateSerialNumber(int serialNumber)
{ui->idLabel->setText(QString::number(serialNumber));
}RoleTableItem::~RoleTableItem()
{delete ui;
}
/////////////////roletable.h
#ifndef ROLETABLE_H
#define ROLETABLE_H#include <QWidget>class RoleTable : public QWidget
{void updateRoleTable();
};#endif // ROLETABLE_H/////////////////roletable.cpp
#include "roletable.h"
#include "ui_roletable.h"
#include "util.h"
#include "roletableitem.h"
#include "edituserdialog.h"
#include "paginator.h"
#include <QRegularExpressionValidator>
#include <QRegularExpression>RoleTable::RoleTable(QWidget *parent): QWidget(parent), ui(new Ui::RoleTable)
{ui->setupUi(this);//初始化管理員界面updateRoleTable();
}void RoleTable::updateRoleTable()
{//測試for(int i = 0;i < 10;i++){RoleTableItem* item = new RoleTableItem(nullptr,i + 1);ui->layout->addWidget(item);}
}
2.7編輯用戶信息頁
在角色管理界?中,當管理員點擊新增按鈕時,需要彈出新增管理員信息窗口,完成新增加管理員信息的輸入;當點擊編輯按鈕時,需要彈出編輯管理員信息窗口,完成對所選管理員信息的編輯。但仔細觀察,這兩個頁面實際是完全相同的。
注意,新增和編輯用戶信息窗口并不是上面白色窗口區域,而是整個窗口區域,窗口上覆蓋?個遮罩層,用戶編輯信息區域在遮罩層上,這樣編輯信息區域能更清晰突顯出來。該窗口也采用絕對定位的?式進行布局。這里我們添加?個Qt設計師界?,命名為EditUserDialog,然后在其ui文件中進行布局:
EditUserDialog創建是?個模態對話框,并且能夠在外部設置創建新增對話框,還是編輯對話框。同時我們設置正則表達式限制輸入框中的輸入
///////////////////edituserdialog.h
#ifndef EDITUSERDIALOG_H
#define EDITUSERDIALOG_H#include <QDialog>namespace Ui {
class EditUserDialog;
}class EditUserDialog : public QDialog
{Q_OBJECTpublic:explicit EditUserDialog(QWidget *parent = nullptr);void setTitle(const QString& title);~EditUserDialog();private:void onSubmitBtnClicked();void onCancelBtnClicked();private:Ui::EditUserDialog *ui;
};#endif // EDITUSERDIALOG_H///////////////edituserdialog.cpp
#include "edituserdialog.h"
#include "ui_edituserdialog.h"
#include "util.h"
#include "limeplayer.h"
#include <QRegularExpressionValidator>
#include <QRegularExpression>EditUserDialog::EditUserDialog(QWidget *parent): QDialog(parent), ui(new Ui::EditUserDialog)
{ui->setupUi(this);//移除標題欄并設置背景透明setWindowFlag(Qt::FramelessWindowHint);setAttribute(Qt::WA_TranslucentBackground);//綁定提交與取消的槽函數connect(ui->submitBtn,&QPushButton::clicked,this,&EditUserDialog::onSubmitBtnClicked);connect(ui->cancelBtn,&QPushButton::clicked,this,&EditUserDialog::onCancelBtnClicked);//設置正則表達式以限制手機號的輸入QRegularExpression regExp("^1\\d{10}$");QRegularExpressionValidator* validator = new QRegularExpressionValidator(regExp,this);//為手機號位置設置正則表達式加以輸入限制ui->phoneEdit->setValidator(validator);//為選擇框添加成員ui->roleComboBox->addItem("管理員");ui->roleComboBox->setCurrentIndex(0);//當簡介中輸入文字時同步文字消息框connect(ui->commentTextEdit,&QPlainTextEdit::textChanged,this,[=](){QString text = ui->commentTextEdit->toPlainText();if(text.size() > 10){//防止setText時再觸發textChanged信號導致遞歸調用ui->commentTextEdit->blockSignals(true);//截取前10個字符text = text.left(10);ui->commentTextEdit->setPlainText(text);// 設置光標位置到末尾QTextCursor cursor = ui->commentTextEdit->textCursor();cursor.movePosition(QTextCursor::End);ui->commentTextEdit->setTextCursor(cursor);ui->commentTextEdit->blockSignals(false);}ui->wordContent->setText(QString::number(text.size()) + "/10");});
}void EditUserDialog::setTitle(const QString &title)
{ui->titleLabel->setText(title);
}EditUserDialog::~EditUserDialog()
{delete ui;
}void EditUserDialog::onSubmitBtnClicked()
{LOG() << "角色管理頁面用戶信息已提交";close();
}void EditUserDialog::onCancelBtnClicked()
{LOG() << "角色管理頁面用戶信息取消提交";close();
}
在RoleTable中給新增按鈕添加槽函數,在槽函數中顯示EditUserDialog窗口;同理,在RoleTableItem中給編輯按鈕添加槽函數,當編輯按鈕點擊的時候顯?EditUserDialog窗口。
////////roletable.h
#ifndef ROLETABLE_H
#define ROLETABLE_H#include <QWidget>namespace Ui {
class RoleTable;
}class RoleTable : public QWidget
{
private:void onInsertBtnClicked();
};#endif // ROLETABLE_H
////////roletable.cpp
#include "roletable.h"
#include "ui_roletable.h"
#include "util.h"
#include "roletableitem.h"
#include "edituserdialog.h"
#include "paginator.h"
#include <QRegularExpressionValidator>
#include <QRegularExpression>RoleTable::RoleTable(QWidget *parent): QWidget(parent), ui(new Ui::RoleTable)
{ui->setupUi(this);connect(ui->insertBtn,&QPushButton::clicked,this,&RoleTable::onInsertBtnClicked);
}void RoleTable::onInsertBtnClicked()
{EditUserDialog* dialog = new EditUserDialog();dialog->setTitle("新增后臺用戶");dialog->exec();delete dialog;
}
/////roletableitem與上面設置基本一致這里不再給出代碼
但是此時有一個問題雖然窗口能夠顯示出來了,但是位置不對,為了更方便之后的獲取主窗口的位置,同時確保同一個程序中只有一個limePlayer實例,這里我們將limePlayer設置為單例模式:
/////limeplayer.h
#ifndef LIMEPLAYER_H
#define LIMEPLAYER_H#include <QWidget>
#include "pageswitchbutton.h"QT_BEGIN_NAMESPACE
namespace Ui {
class LimePlayer;
}
QT_END_NAMESPACEclass LimePlayer : public QWidget
{Q_OBJECTpublic:static LimePlayer* getInstance();~LimePlayer();
private://將函數設置為單例模式LimePlayer(QWidget *parent = nullptr);
private:static LimePlayer* limePlayer;
};
#endif // LIMEPLAYER_H//////////limeplayer.cpp
LimePlayer* LimePlayer::limePlayer = nullptr;LimePlayer* LimePlayer::getInstance()
{if(limePlayer == nullptr){limePlayer = new LimePlayer();}return limePlayer;
}/////main.cpp
#include "limeplayer.h"
#include "startpage.h"#include <QApplication>
#include <QStyleFactory>int main(int argc, char *argv[])
{// 禁?窗?按照分辨率百分?縮放,必須套放在程序第?句QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor);//讓它不跟隨系統設置的深色模式qputenv("QT_QPA_PLATFORM","windows:darkmode=0");QApplication a(argc, argv);// 設置應用程序圖標a.setWindowIcon(QIcon(":/images/limePlayer.ico"));//顯示啟動窗口StartPage startPage;startPage.startUp();startPage.exec();LimePlayer* w = LimePlayer::getInstance();w->show();return a.exec();
}
此時我們在edituserdialog.cpp設置其初始位置即可:
/////edituserdialog.cpp
EditUserDialog::EditUserDialog(QWidget *parent): QDialog(parent), ui(new Ui::EditUserDialog)
{ui->setupUi(this);//設置初始位置this->move(LimePlayer::getInstance()->mapToGlobal(QPoint(25,25)));
}
這時侯位置顯示就沒問題了。
2.8分頁器實現
當數據量比較?時,如果將數據?次性加載出來會非常耗時,而且也不?便用戶查看,因此都是分頁顯示,用戶通過分頁器可以快速定位到指定頁面查看數據:
假設:page為當前顯示頁,pageCount為總頁數,而默認顯示的pageButton數為7個,要實現上述分頁器需求,則:
① 當點擊"<“按鈕時,顯示前一頁,當點擊”>“按鈕時顯示最后一頁
② 當在跳轉?框中輸入數字后按回?鍵可直接跳轉?指定頁,數字在1至最大頁數之間;若輸入超過最大頁數則跳轉到最后頁
③ 如果總頁數小于7?,中間部分按鈕上顯示1~7頁數,不出現折疊按鈕,page按鈕為激活按鈕
④ 如果總頁數?于7?,當前顯顯示頁為1-5時,前5個按鈕展示1~5頁,第6按鈕為折疊按鈕顯示
為”…",最后?個按紐顯示總頁數,page按鈕為激活按鈕
⑤ 如果總頁數?于7頁,當前顯?頁為pageCount-4 ~ pageCount時(pageCount為總?數),第?個按鈕顯示第1頁,其余按鈕顯示pageCount-3 ~ pageCount頁,第2個按鈕為折疊按鈕顯示…,page按紐為折疊按鈕
⑥ 如果總頁數?于7頁,當前顯???于5,小于pageCount-4時,第1個按鈕和第7個按鈕固定展示第?頁和最后?頁,中間五個按鈕?次為page-2、page-1,page,page+1,page+2,并且將第2個按
鈕和第6個按鈕為折疊按鈕顯?…,最中間按鈕(即第三個按鈕)為激活按鈕。
弄清分頁器的邏輯之后,我們便可以著手開始分頁器的設計了:
由于分頁器上每個按鈕有特定樣式,并且有些折疊顯示,有些顯示頁數,有些處于點擊選中狀態,因此將QPushButton進?簡單封裝。添加?個C++頭?件和源?件,類名為PageButton,繼承自QPushButton。具體實現如下:
//////pagebutton.h
#ifndef PAGEBUTTON_H
#define PAGEBUTTON_H#include <QPushButton>class PageButton : public QPushButton
{Q_OBJECT
public:explicit PageButton(QWidget *parent = nullptr,int page = 1);//獲取按鈕對應的頁面序號int getPage() const;void setPage(int page);//設置與獲取按鈕的激活狀態bool getIsActive() const;void setActive(bool isActive);//設置與獲取按鈕的折疊狀態bool getIsFoldBtn() const;void setFold(bool isFold);private:int page;//按鈕對應的頁面序號bool isActiveBtn;//是否為選中狀態bool isFoldBtn;//是否為折疊按鈕
};#endif // PAGEBUTTON_H//////////////pagebutton.cpp
#include "pagebutton.h"PageButton::PageButton(QWidget *parent,int page): QPushButton{parent},isActiveBtn(false),isFoldBtn(false)
{this->page = page;// 設置按鈕的圖標尺? 和 按鈕??this->setIconSize(QSize(12, 12));this->setFixedSize(QSize(32, 32));//設置按鈕對應的頁面setPage(this->page);//設置按鈕的激活與折疊狀態setFold(isFoldBtn);setActive(isActiveBtn);
}int PageButton::getPage() const
{return page;
}void PageButton::setPage(int page)
{this->page = page;setText(QString::number(page));
}bool PageButton::getIsActive() const
{return isActiveBtn;
}void PageButton::setActive(bool isActive)
{this->isActiveBtn = isActive;if(isActive){//選中狀態setStyleSheet("color : #FFFFFF;""background-color : #3ECEFF;""border : 1px solid #3ECEFF;""border-radius : 2px");}else{//非選中狀態setStyleSheet("color : #000000;""background-color : #FFFFFF;""border : 1px solid #D9D9D9;""border-radius : 2px");}
}bool PageButton::getIsFoldBtn() const
{return isFoldBtn;
}void PageButton::setFold(bool isFold)
{this->isFoldBtn = isFold;if(isFold){setText("...");}else{setText(QString::number(page));}
}
然后我們添加一個Paginator設計師類,這里我們不借助ui界面進行布局了,換一種方式嘗試我們直接在代碼中進行控件的創建與布局,同時基于我們上面對分頁器邏輯的分析,我們可以這樣實現Paginator類:
///////paginator.h
#ifndef PAGINATOR_H
#define PAGINATOR_H#include <QWidget>
#include <QPushButton>
#include <QLineEdit>
#include "pagebutton.h"namespace Ui {
class Paginator;
}class Paginator : public QWidget
{Q_OBJECTpublic:explicit Paginator(QWidget *parent = nullptr,int pageCount = 7,int btnNumber = 7);~Paginator();
private:void initUi();void initSignalsAndSlots();//初始化所有的信號槽函數void onPageBtnClicked();//頁面按鈕被按下void onPrevBtnClicked();//前一頁按鈕被按下void onNextBtnClicked();//后一頁按鈕被按下void onPageEditEdited();//跳轉頁編輯框輸入返回void jumpToPage(int page);//設置跳往的頁數//跳轉頁數的三種情況void jumpToPageCase1(int page);void jumpToPageCase2(int page);void jumpToPageCase3(int page);
private:Ui::Paginator *ui;//當前選中的頁面int currentPage;//默認顯示的按鈕數-除去<與>按鈕int defaultBtnNum;//總頁數int pageCount;//跳轉頁編輯框QLineEdit* pageEdit;//當前選中頁面的<與>對應的按鈕QPushButton* prevBtn;QPushButton* nextBtn;//存儲除<與>的所有pageBtn按鈕QList<PageButton*> pageButtons;
};#endif // PAGINATOR_H////////paginator.cpp
#include "paginator.h"
#include "ui_paginator.h"
#include <QHBoxLayout>
#include <QLabel>Paginator::Paginator(QWidget *parent,int pageCount,int btnNumber): QWidget(parent), ui(new Ui::Paginator),pageCount(pageCount)
{ui->setupUi(this);//設置defaultBtnNum必須在7-12之間btnNumber = std::min(12,btnNumber);btnNumber = std::max(7,btnNumber);defaultBtnNum = btnNumber;//初始化分頁器界面initUi();//綁定信號槽函數initSignalsAndSlots();
}void Paginator::initUi()
{//添加水平布局器QHBoxLayout* layout = new QHBoxLayout(this);layout->setContentsMargins(0,0,3,0);//左 上 右 下:順時針layout->setSpacing(4);layout->addStretch();//水平彈簧//設置固定寬高setFixedSize(1270,32);//設置<與>按鈕prevBtn = new QPushButton();prevBtn->setFixedSize(32,32);prevBtn->setIconSize(QSize(12,12));prevBtn->setIcon(QIcon(":/images/admin/arrow-left.png"));nextBtn = new QPushButton();nextBtn->setFixedSize(32,32);nextBtn->setIconSize(QSize(12,12));nextBtn->setIcon(QIcon(":/images/admin/arrow-right.png"));layout->addWidget(prevBtn);//1號按鈕默認為激活狀態PageButton* btn0 = new PageButton(nullptr,1);pageButtons.append(btn0);btn0->setActive(true);layout->addWidget(btn0);currentPage = 1;//設置當前頁面if(pageCount <= defaultBtnNum){//按照pageCount設置總按鈕數for(int i = 1;i < pageCount;i++){PageButton* btn = new PageButton(nullptr,i + 1);pageButtons.append(btn);layout->addWidget(btn);}}else{//需要設置折疊按鈕-按照defaultBtnNumber設置總按鈕數for(int i = 1;i < defaultBtnNum;i++){PageButton* btn = new PageButton();if(i == defaultBtnNum - 2){btn->setPage(i + 1);btn->setFold(true);}else if(i == defaultBtnNum - 1){btn->setPage(pageCount);}else{btn->setPage(i + 1);}pageButtons.append(btn);layout->addWidget(btn);}}layout->addWidget(nextBtn);//添加編輯框和提示labelQLabel* hintLabel1 = new QLabel("跳轉至");layout->addWidget(hintLabel1);pageEdit = new QLineEdit();pageEdit->setFixedSize(QSize(48, 32));pageEdit->setAlignment(Qt::AlignCenter);//文字居中pageEdit->setStyleSheet("QLineEdit{""background-color: #FFFFFF; ""border: 1px solid #D9D9D9; ""border-radius: 4px;}");layout->addWidget(pageEdit);QLabel* hintLabel2 = new QLabel("頁");layout->addWidget(hintLabel2);
}void Paginator::initSignalsAndSlots()
{//綁定prevBtn與nextBtn的槽函數connect(prevBtn,&QPushButton::clicked,this,&Paginator::onPrevBtnClicked);connect(nextBtn,&QPushButton::clicked,this,&Paginator::onNextBtnClicked);//綁定pageButton對應的槽函數for(int i = 0;i < defaultBtnNum;i++){connect(pageButtons[i],&PageButton::clicked,this,&Paginator::onPageBtnClicked);}//綁定編輯框輸入完畢的槽函數connect(pageEdit,&QLineEdit::returnPressed,this,&Paginator::onPageEditEdited);
}Paginator::~Paginator()
{delete ui;
}void Paginator::onPageBtnClicked()
{//從sender也就是激活該函數的控件處獲取設置頁int page = static_cast<PageButton*>(sender())->getPage();jumpToPage(page);
}void Paginator::onPrevBtnClicked()
{if(currentPage - 1 < 1){return;}jumpToPage(currentPage - 1);
}void Paginator::onNextBtnClicked()
{if(currentPage + 1 > pageCount){return;}jumpToPage(currentPage + 1);
}void Paginator::onPageEditEdited()
{int jumpPage = pageEdit->text().toInt();if(jumpPage < 1)jumpPage = 1;else if(jumpPage > pageCount)jumpPage = pageCount;jumpToPage(jumpPage);pageEdit->setText("");
}void Paginator::jumpToPage(int page)
{currentPage = page;//再次驗證page是否合法if(page < 1 || page > pageCount){return;}//當沒有折疊按鈕時if(pageCount <= defaultBtnNum){for(int i = 0;i < pageCount;i++){if(page - 1 == i)pageButtons[i]->setActive(true);elsepageButtons[i]->setActive(false);}}else{//有折疊按鈕時分為三種情況if(page >= 1 && page <= defaultBtnNum - 2){jumpToPageCase1(page);}else if(page > defaultBtnNum - 2 && page <= pageCount - defaultBtnNum + 2){jumpToPageCase2(page);}else{jumpToPageCase3(page);}}
}void Paginator::jumpToPageCase1(int page)
{for(int i = 0;i < defaultBtnNum;i++){//設置按鈕對應的頁號-同時設置折疊狀態if(i == defaultBtnNum - 2){pageButtons[i]->setPage(i + 1);pageButtons[i]->setFold(true);}else if(i == defaultBtnNum - 1){pageButtons[i]->setPage(pageCount);}else{pageButtons[i]->setPage(i + 1);}//設置激活狀態if(page == pageButtons[i]->getPage()){pageButtons[i]->setActive(true);}else{pageButtons[i]->setActive(false);}}
}void Paginator::jumpToPageCase2(int page)
{//清除對應1與pageCount頁的按鈕的活躍狀態pageButtons[0]->setPage(1);pageButtons[0]->setActive(false);pageButtons[defaultBtnNum - 1]->setPage(pageCount);pageButtons[defaultBtnNum - 1]->setActive(false);//剩余可設置按鈕數的一半-向上取整int residueBtn = (defaultBtnNum - 1) / 2;//(defaultBtnNum - 2 + 1)/2//設置中間部分for(int i = 1;i < defaultBtnNum - 1;i++){//設置按鈕對應的頁號if(i <= residueBtn){pageButtons[i]->setPage(page - (residueBtn - i));}else{pageButtons[i]->setPage(page + i - residueBtn);}//設置激活狀態if(page == pageButtons[i]->getPage()){pageButtons[i]->setActive(true);}else{pageButtons[i]->setActive(false);}//設置折疊狀態if(i == 1 || i == defaultBtnNum - 2){pageButtons[i]->setFold(true);}}
}void Paginator::jumpToPageCase3(int page)
{for(int i = 0;i < defaultBtnNum;i++){//設置按鈕對應的頁號-同時設置折疊狀態if(i == 0){pageButtons[i]->setPage(1);}else if(i == 1){pageButtons[i]->setPage(pageCount - defaultBtnNum + i + 1);pageButtons[i]->setFold(true);}else{pageButtons[i]->setPage(pageCount - defaultBtnNum + i + 1);}//設置激活狀態if(page == pageButtons[i]->getPage()){pageButtons[i]->setActive(true);}else{pageButtons[i]->setActive(false);}}
}
在審核頁面和角色管理頁面都有分頁器,在這兩個窗口中添加并顯示分?器。
///////checktable.cpp
CheckTable::CheckTable(QWidget *parent): QWidget(parent), ui(new Ui::CheckTable)
{//設置分頁器的顯示Paginator* paginator = new Paginator(ui->PaginatorArea,36,9);paginator->move(0,15);paginator->show();
}
/////roletable.cpp中添加方式和上面一樣
三.登錄界面布局
剛開始用戶為臨時用戶,臨時用戶操作有限,只能觀看視頻等,如果要發送彈幕、視頻點贊等操作,必須先要登錄進系統中,因此需要添加?個登錄頁面。?持兩種登錄?式:密碼登錄 和 短信登錄。那我們可以新建一個設計師類類名為Login然后在其ui文件中進行布局:
界面中控件的嵌套關系如下:
當然因為登錄窗口是一個彈窗,我們可以去讓他繼承自QDialog,但是我們這里換一種寫法,同時設置輸入限制和密碼登錄與短信登錄界面切換的邏輯,實現如下:
////////login.h
#ifndef LOGIN_H
#define LOGIN_H#include <QWidget>
#include <QRegularExpressionValidator>namespace Ui {
class Login;
}class Login : public QWidget
{Q_OBJECTpublic:explicit Login(QWidget *parent = nullptr);~Login();private:void initSignalsAndSlots();void onPasswordBtnClicked();void onMessageBtnClicked();
private:Ui::Login *ui;QRegularExpressionValidator* authcodeValidator;//驗證碼一欄的驗證器
};#endif // LOGIN_H////////login.cpp
#include "login.h"
#include "ui_login.h"
#include "limeplayer.h"
#include <QGraphicsDropShadowEffect>
#include <QRegularExpression>Login::Login(QWidget *parent): QWidget(parent), ui(new Ui::Login)
{ui->setupUi(this);//移動窗口位置this->move(LimePlayer::getInstance()->mapToGlobal(QPoint(0,0)));//設置為模態對話框并拿掉標題欄setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);//設置Dialog與修改繼承類為QDialog效果一樣setAttribute(Qt::WA_ShowModal,true);//設置窗口close時自動析構setAttribute(Qt::WA_DeleteOnClose,true);// 窗?加上陰影效果setAttribute(Qt::WA_TranslucentBackground); // 陰影效果必須要窗?透明QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect(this);shadowEffect->setColor(Qt::black);shadowEffect->setBlurRadius(25);shadowEffect->setOffset(0);ui->background->setGraphicsEffect(shadowEffect);//設置正則表達式以限制手機號的輸入QRegularExpression regExp("^1\\d{10}$");QRegularExpressionValidator* validator = new QRegularExpressionValidator(regExp,this);//為手機號位置設置正則表達式加以輸入限制ui->accountEdit->setValidator(validator);//設置驗證碼位置的驗證器QRegularExpression regExp1("^\\d{6}$");authcodeValidator = new QRegularExpressionValidator(regExp1,this);ui->passwordEdit->setValidator(authcodeValidator);//連接所有信號槽initSignalsAndSlots();
}void Login::initSignalsAndSlots()
{connect(ui->minBtn,&QPushButton::clicked,this,&Login::showMinimized);connect(ui->quitBtn,&QPushButton::clicked,this,&Login::close);connect(ui->passwordBtn,&QPushButton::clicked,this,&Login::onPasswordBtnClicked);connect(ui->messageBtn,&QPushButton::clicked,this,&Login::onMessageBtnClicked);
}Login::~Login()
{delete ui;
}void Login::onPasswordBtnClicked()
{ui->loginOrRegister->hide();ui->authcodeBtn->hide();ui->passwordLabel->setText("密碼");ui->passwordEdit->setText("");ui->passwordEdit->setPlaceholderText("請輸入密碼");ui->passwordBtn->setStyleSheet("#passwordBtn{""font-size : 22px;""color : #3ECEFE;""font-weight:bold;""border : none;""border-bottom : 6px solid #3ECEFE;""}");ui->messageBtn->setStyleSheet("#messageBtn{""font-size : 22px;""color : #222222;""border : none;""border-bottom: 2px solid #B5ECFF;""}");ui->passwordEdit->setValidator(nullptr);
}void Login::onMessageBtnClicked()
{ui->loginOrRegister->show();ui->authcodeBtn->show();ui->passwordLabel->setText("驗證碼");ui->passwordEdit->setText("");ui->passwordEdit->setPlaceholderText("請輸入驗證碼");ui->messageBtn->setStyleSheet("#messageBtn{""font-size : 22px;""color : #3ECEFE;""font-weight:bold;""border : none;""border-bottom : 6px solid #3ECEFE;""}");ui->passwordBtn->setStyleSheet("#passwordBtn{""font-size : 22px;""color : #222222;""border : none;""border-bottom: 2px solid #B5ECFF;""}");ui->passwordEdit->setValidator(authcodeValidator);
}
這里為了測試,我們為playerPage界面的點贊按鈕設置點擊后彈出此對話框:
//////playerpage.h
#ifndef PLAYERPAGE_H
#define PLAYERPAGE_H#include <QWidget>
#include <QGraphicsOpacityEffect>
#include <QPropertyAnimation>
#include <QTimer>
#include "volume.h"
#include "playspeed.h"namespace Ui {
class PlayerPage;
}class PlayerPage : public QWidget
{private slots:void onLikeImageBtnClicked();//點贊按鈕被點擊
};#endif // PLAYERPAGE_H
//////playerpage.cpp
void PlayerPage::initPlayer()
{//測試-顯示登錄窗口connect(ui->likeImageBtn,&QPushButton::clicked,this,&PlayerPage::onLikeImageBtnClicked);
}void PlayerPage::onLikeImageBtnClicked()
{Login* login = new Login();//Toast::showToast("登錄之后才可以點贊哦~",login);-下面實現login->show();
}
在視頻播放界面點擊點贊按鈕就有如下效果了:
四.toast窗口
Toast提示是?種用來進行關鍵信息提示的彈出式消息對話框,?般只顯示幾秒鐘,然后?動消失不會干擾用戶的正常操作。通常用于通知用戶某些操作結果、錯誤或狀態提示的彈出式消息框。最初在 Android 系統中被廣泛使用,后來也被許多其他平臺和應用程序借鑒。 界面上有些操作需要用戶登錄之后才有權限,比如發送彈幕、點贊、修改??圖像等,在用戶沒有登錄時進行這些操作,應該給用戶Toast提示。
我們對上面的onLikeImageBtnClicked函數中的Toast::showToast(“登錄之后才可以點贊哦~”,login);取消注釋后點擊點贊按鈕會先顯示toast窗口然后再彈出登錄界面,顯示效果如下:
這里我們新建一個普通c++類類名為Toast,實現toast窗口的代碼如下:
/////////toast.h
#ifndef TOAST_H
#define TOAST_H#include <QWidget>
#include <QPropertyAnimation>class Toast : public QWidget
{Q_OBJECT
public://靜態的去顯示消息提示窗口static void showToast(const QString& text,QWidget *widget);static void showToast(const QString& text);
private://提示窗先顯示后顯示窗口Toast(const QString& text,QWidget *widget);//僅僅顯示提示窗口Toast(const QString& text);//初始化提示窗口的uivoid initUi(const QString& text);
private://窗口消失時的動畫QPropertyAnimation* windowHide;
};#endif // TOAST_H//////toast.cpp
#include "toast.h"
#include <QTimer>
#include <QGraphicsOpacityEffect>
#include <QHBoxLayout>
#include <QLabel>
#include <QGuiApplication>
#include <QScreen>Toast::Toast(const QString &text, QWidget *widget)
{initUi(text);//設置定時器QTimer* timer = new QTimer(this);connect(timer,&QTimer::timeout,this,[=](){this->close();timer->stop();this->deleteLater();if(widget){widget->show();}});windowHide->start();timer->start(3000);
}Toast::Toast(const QString &text)
{initUi(text);//設置定時器QTimer* timer = new QTimer(this);connect(timer,&QTimer::timeout,this,[=](){this->close();timer->stop();this->deleteLater();//切記釋放toast窗口});windowHide->start();timer->start(3000);
}void Toast::showToast(const QString &text, QWidget *widget)
{Toast* toast = new Toast(text,widget);toast->show();
}void Toast::showToast(const QString &text)
{Toast* toast = new Toast(text);toast->show();
}void Toast::initUi(const QString &text)
{//拿掉窗口的標題欄與設置背景透明-設置窗口固定寬高setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);setAttribute(Qt::WA_TranslucentBackground);setFixedSize(500,60);//初始化動畫效果QGraphicsOpacityEffect* toastOpacity = new QGraphicsOpacityEffect(this);toastOpacity->setOpacity(1.0); // 初始完全不透明this->setGraphicsEffect(toastOpacity); // 將效果應用到控件windowHide = new QPropertyAnimation(toastOpacity, "opacity",this);//設置透明效果并掛到對象樹上windowHide->setDuration(3000); // 動畫持續3000毫秒(3秒)windowHide->setStartValue(1.0); // 起始值:完全不透明windowHide->setEndValue(0.0); // 結束值:完全透明windowHide->setEasingCurve(QEasingCurve::InOutQuad); // 使用平滑的緩動曲線//添加背景QWidget* toastBg = new QWidget(this);toastBg->setFixedSize(500,60);toastBg->setStyleSheet("background-color : rgba(106,106,106,0.9);""border-radius : 5px");//添加布局器QHBoxLayout* layout = new QHBoxLayout(toastBg);layout->setContentsMargins(0,5,0,5);//添加文本提示框QLabel* label = new QLabel(toastBg);label->setWordWrap(true);//設置自動換行label->setAlignment(Qt::AlignCenter);//設置文本居中label->setText(text);label->setStyleSheet("color : #FFFFFF;""font-family :""幼圓,""Microsoft YaHei,""sans-serif;");//添加label到背景框中layout->addWidget(label);//調整toast窗口的顯示位置// 獲取主屏幕的可用幾何區域(排除任務欄等)QScreen *screen = QGuiApplication::primaryScreen();QRect screenGeometry = screen->geometry();int x = (screenGeometry.width() - 500) / 2;int y = screenGeometry.height() - 50 - 60;this->move(x,y);
}