c++小學期大作業攻略(三)用戶系統


Update

  at 2019/07/22 14:16

  發現一個大坑,我們后期是打算用QSS統一堆樣式進行美化的,於是我把之前對QLabel進行的setAlignment全部去掉了,打算統一丟進Qss里面,一下子使程序簡潔了很多。但是發現QLabel在QSS里面不支持text-align,也找不到其他將其設置為居中的接口。暫時的解決方案是新建一個QLabelCenter類,完全從QLabel繼承,但是構造函數里面加上setAlignment。

  附一段加載qss的代碼:

void Translation::LoadQss(const QString sRoad){
    QFile fQss(sRoad);
    fQss.open(QFile::ReadOnly);
    setStyleSheet(fQss.readAll());
    fQss.close();
}

  sRoad是qss文件的路徑。

  

  


 

  感覺自己花了兩天時間完成了一下午的工作量,基本上都是單純地堆代碼,沒有什么技術性比較強的活,所以這篇巨水無比。我現在主要的參考資料已經變成官方的手冊了:

  https://doc.qt.io/qt-5/classes.html

  目前正在一步一步朝着GMA的樣式前進。

  

  用戶的存儲是在全局的位置放了std::vector<User> g_Users,下面是遍歷這個vector的一種比較優雅的做法:

  std::vector<User>::iterator itUserTemp;
  for (itUserTemp = g_Users.begin(); itUserTemp != g_Users.end(); itUserTemp++) {
    //itUserTemp->GetNickname()....   }

 

  

  

 1. Register

  

  這里的樣式結構非常簡單,畫面整體是豎向排列的,里面再套一個橫向排列用來放提示和輸入框。文字輸入用的是QLineEdit,下拉列表選項用的是QComboBox。

  按照我的寫法,Register類中需要存儲一下被輸入的QLineEdit的指針,還有Register的父元素MainWindow的指針,響應的槽函數放在Register中,這樣響應函數可以調用MainWindow的切換界面函數。

 

  ^_^

QWidget* Register::ShowInformation(bool Show) {
    QWidget* pwBox = new QWidget();
    QVBoxLayout* pvLayout = new QVBoxLayout(pwBox);

    //CreateLineInput是預先寫好的一個函數,返回一個QWidget,里面橫向放了一個QLabel和一個QLineEdit
    //m_pleNickname會變成一個指向QLineEdit的指針
    pvLayout->addWidget(CreateLineInput(STR_TIP_NICKNAME, m_pleNickname, Show));
    m_pleNickname->setMaxLength(20);

    pvLayout->addWidget(CreateLineInput(STR_TIP_PASSWORD, m_plePassword, Show));
    m_plePassword->setEchoMode(QLineEdit::Password);
    m_plePassword->setMaxLength(40);

    pvLayout->addWidget(CreateLineInput(STR_TIP_PASSWORDCON, m_plePasswordCon, Show));
    m_plePasswordCon->setEchoMode(QLineEdit::Password);
    m_plePasswordCon->setMaxLength(40);

    pvLayout->addWidget(CreateLineInput(STR_TIP_AGE, m_pleAge, Show));

    //CreateGenderInput跟CreateLineInput類似
    pvLayout->addWidget(CreateGenderInput(STR_TIP_AGE, m_pleGender, Show));

    return pwBox;
}
void Register::Init() {
    QVBoxLayout *pvlLayout = new QVBoxLayout(this);//整體豎向排版

    QLabel* plMainTitle = new QLabel(this);
    plMainTitle->setText(STR_MAINTITLE_REGISTER);//主標題
    plMainTitle->setAlignment(Qt::AlignCenter);
    pvlLayout->addWidget(plMainTitle, INT_SIZERATE_MAINTITAL);//主標題加入整體的豎向排版

    pvlLayout->addWidget(ShowInformation(), INT_SIZERATE_INFORMATION);//調用函數,獲得一個QWidget,加進主排版



    //按鈕部分
    QWidget* pwButton = new QWidget(this);
    QHBoxLayout* phlButton = new QHBoxLayout(pwButton);
    QPushButton* ppbRegister = new QPushButton(pwButton);
    ppbRegister->setText(STR_BUTTON_REGISTER);
    phlButton->addWidget(ppbRegister, INT_SIZERATE_BUTTON_REGISTER);


    pvlLayout->addWidget(pwButton, INT_SIZERATE_BUTTON);



    connect(ppbRegister, SIGNAL(clicked(bool)), this, SLOT(RegisterRequest()));
}
View Code

 

 

 

 

2. Login

  

  和Register非常相似,用一個User* g_pCurrentUser來指向當前的已登錄用戶。

  

  

  ^_^

QWidget* Login::ShowInformation(bool Show) {
    QWidget* pwBox = new QWidget();
    QVBoxLayout* pvLayout = new QVBoxLayout(pwBox);

    //CreateLineInput是寫好的一個創建輸入提示和輸入框的函數
    pvLayout->addWidget(CreateLineInput(STR_TIP_NICKNAME, m_pleNickname, Show));
    pvLayout->addWidget(CreateLineInput(STR_TIP_PASSWORD, m_plePassword, Show));
    m_plePassword->setEchoMode(QLineEdit::Password);
    return pwBox;
}
void Login::Init() {
    //豎直布局
    QVBoxLayout *pvlLayout = new QVBoxLayout(this);

    //加入主標題
    pvlLayout->addWidget(new QLabelCenter(STR_MAINTITLE_LOGIN), INT_SIZERATE_MAINTITAL);

    //加入輸信息模塊
    pvlLayout->addWidget(ShowInformation(), INT_SIZERATE_INFORMATION);


    //加入按鈕模塊
    QPushButton* ppbLogin = new QPushButton(STR_BUTTON_LOGIN);
    connect(ppbLogin, SIGNAL(clicked(bool)), this, SLOT(LoginRequest()));
    pvlLayout->addWidget(ppbLogin, INT_SIZERATE_BUTTON_LOGIN);

    QPushButton* ppbRegister = new QPushButton(STR_BUTTON_REGISTER);
    connect(ppbRegister, SIGNAL(clicked(bool)), this, SLOT(RegisterRequest()));
    pvlLayout->addWidget(ppbRegister, INT_SIZERATE_BUTTON_REGISTER);
}
View Code

 

3. Database

  我們需要在程序開啟時從數據庫加載表里的用戶,程序關閉時把相關數據存入數據庫。

  我專門寫了一個Database類,初始化時傳入地址、賬號、密碼之類的初始信息,當然為了方便你也可以把數據庫寫成全局的函數,初始信息寫死。

  每次存數據先把數據庫里的表清空:TRUNCATE Users,然后再逐個用戶INSERT INTO Users。取數據直接SELECT即可。

  后來聽說直接清空之后存數據給的分會低,已有的數據只進行update效率比較高,mark起來以后改

 

 

  ^_^

//存數據
void Database::SaveData(std::vector<User>& Users) {
    MYSQL MysqlTemp;
    MYSQL* pSock;
    mysql_init(&MysqlTemp);
    if ((pSock = mysql_real_connect(&MysqlTemp, HOST, USERNAME, PASSWORD, DB, PORT, UNIX_SOCKET, CLIENT_FLAG)) == nullptr) {
#ifdef DEBUG_MOD
        qDebug() << "Fail to connect mysql.\n";
        qDebug() << mysql_error(&MysqlTemp) << endl;
#endif
        return;
    }

    std::string sQuery;
    if (mysql_query(&MysqlTemp, "TRUNCATE Users") != 0) {
#ifdef DEBUG_MOD
        qDebug() << "Fail to query : " << QString::fromStdString(sQuery) << endl;
#endif
    }

    std::vector<User>::iterator itUserTemp;
#ifdef DEBUG_MOD
    qDebug() << "Save : " << Users.size() << endl;
#endif
    for (itUserTemp = Users.begin(); itUserTemp != Users.end(); itUserTemp++) {
        //設置好語句
        sQuery = "INSERT INTO Users (ID, Nickname, Password, Introduction, Certificate, Gender, Age, Score) VALUES (";
        sQuery += std::to_string(itUserTemp->GetId()) + ", ";
        sQuery += "'" + itUserTemp->GetNickname() + "', ";
        sQuery += "'" + itUserTemp->GetPassword() + "', ";
        sQuery += "'" + itUserTemp->GetIntroduction() + "', ";
        sQuery += "'" + itUserTemp->GetCertificate() + "', ";
        sQuery += std::to_string(int(itUserTemp->GetGender())) + ", ";
        sQuery += std::to_string(itUserTemp->GetAge()) + ", ";
        sQuery += std::to_string(itUserTemp->GetScore()) + ")";

        if (mysql_query(&MysqlTemp, sQuery.data()) != 0) {
#ifdef DEBUG_MOD
            qDebug() << "Fail to query : " << QString::fromStdString(sQuery) << endl;
#endif
        }
    }
    mysql_close(pSock);
}

//取數據
void Database::LoadData(std::vector<User>& Users) {
    MYSQL MysqlTemp;
    MYSQL* pSock;
    mysql_init(&MysqlTemp);
    if ((pSock = mysql_real_connect(&MysqlTemp, HOST, USERNAME, PASSWORD, DB, PORT, UNIX_SOCKET, CLIENT_FLAG)) == nullptr) {
#ifdef DEBUG_MOD
        qDebug() << "Fail to connect mysql.\n";
        qDebug() << mysql_error(&MysqlTemp) << endl;
#endif
        return;
    }
    std::string sQuery = "SELECT ID, Nickname, Password, Introduction, Certificate, Gender, Age, Score FROM Users";
    if (mysql_query(&MysqlTemp, sQuery.data()) != 0){
#ifdef DEBUG_MOD
        qDebug() << "Fail to query : " << QString::fromStdString(sQuery) << endl;
#endif
        return;
    }
    MYSQL_RES * result;
    MYSQL_ROW row;
    if ((result = mysql_store_result(&MysqlTemp)) == NULL) {
#ifdef DEBUG_MOD
        qDebug() << "Fail to store result!" << endl;
#endif
        return;
    }

    Users.clear();
    //逐行取數據
    while ((row = mysql_fetch_row(result)) != NULL)    {
        User UserTemp(atoi(row[0]), std::string(row[1]), std::string(row[2]), std::string(row[3]), 
                      std::string(row[4]), Gender(atoi(row[5])), atoi(row[6]), atoi(row[7]));

        Users.push_back(UserTemp);
    }
#ifdef DEBUG_MOD
    qDebug() << "Load : " << Users.size() << endl;
#endif
    mysql_close(pSock);
}
View Code

 

 

 4. Leaderboard

  

 

  反正只是一個大作業而已就不打算做什么翻頁的功能了,直接一個滾動條滑到底好了。

  首先是一個豎直的布局,有一個MainTitle(QLabel)和一個Leaderboard(QScrollArea)。QScrollArea里面先套了一個QWidget,在這個QWidget里面放豎直布局,豎直布局里面每行是一個水平布局,存放個人信息。值得注意的是QScrollArea需要執行一下setWidgetResizable(true),否則會有顯示錯誤……

  

  ^_^

namespace QtLeaderboard {
    QWidget* CreateTips() {
        QWidget* pwTips = new QWidget();
        QHBoxLayout* phlTipsLayout = new QHBoxLayout(pwTips);
        phlTipsLayout->addWidget(new QLabel(SET_TIP_HEAD), INT_SIZERATE_HEAD);

        phlTipsLayout->addWidget(new QLabel(SET_TIP_NICKNAME), INT_SIZERATE_NICKNAME);

        phlTipsLayout->addWidget(new QLabel(SET_TIP_INTRO), INT_SIZERATE_INTRO);

        phlTipsLayout->addWidget(new QLabel(SET_TIP_SCORE), INT_SIZERATE_SCORE);

        return pwTips;
    }


    QWidget* CreatePersonal(std::vector<User>::iterator itUserTemp) {
        QWidget* pPersonal = new QWidget();
        pPersonal->setFixedHeight(INT_PERSONAL_HEIGHT);

        QHBoxLayout* phlPersonalLayout = new QHBoxLayout(pPersonal);


        QLabel* plHead = new QLabel(pPersonal);

        {
            QImage Head(QString::fromStdString(itUserTemp->GetHead()));
            Head = Head.scaled(INT_HEAD_WIDTH, INT_HEAD_HEIGHT);
            plHead->setPixmap(QPixmap::fromImage(Head));
            plHead->setAlignment(Qt::AlignCenter);
        }

        phlPersonalLayout->addWidget(plHead, INT_SIZERATE_HEAD);

        phlPersonalLayout->addWidget(new QLabel(QString::fromStdString(itUserTemp->GetNickname())), INT_SIZERATE_NICKNAME);

        phlPersonalLayout->addWidget(new QLabel(QString::fromStdString(itUserTemp->GetIntroduction())), INT_SIZERATE_INTRO);

        phlPersonalLayout->addWidget(new QLabel(QString::number(itUserTemp->GetScore())), INT_SIZERATE_SCORE);

        return pPersonal;
    }

    QScrollArea* CreateBoard() {
        QScrollArea* psaBoard = new QScrollArea();
        QWidget* pwBoard = new QWidget(psaBoard);

        pwBoard->setFixedHeight(g_Users.size() * INT_PERSONAL_HEIGHT + INT_TIP_HEIGHT);
        QVBoxLayout* pvlBoardLayout = new QVBoxLayout(pwBoard);

        pvlBoardLayout->addWidget(QtLeaderboard::CreateTips());


        //逐個用戶添加信息
        std::vector<User>::iterator itUserTemp;
        //sort(g_Users.begin(), g_Users.end(), User::SortByScore);
        for (itUserTemp = g_Users.begin(); itUserTemp != g_Users.end(); itUserTemp++) {
            pvlBoardLayout->addWidget(QtLeaderboard::CreatePersonal(itUserTemp));
        }

        psaBoard->setWidget(pwBoard);
        psaBoard->setWidgetResizable(true);

        return psaBoard;
    }
}
void Leaderboard::Init() {
    QVBoxLayout *pvlMainLayout = new QVBoxLayout(this);

    //加入主標題
    pvlMainLayout->addWidget(new QLabel(STR_MAINTITLE_LEADERBOARD), INT_SIZERATE_MAINTITAL);


    //加入排行榜界面
    pvlMainLayout->addWidget(QtLeaderboard::CreateBoard(), INT_SIZERATE_BOARD);
}
View Code

 

 5. Personal

  個人界面……寫得還蠻久

  

  

  亮點是實現了一個上傳頭像的功能吧,但是用Qt的getOpenFileName,QImage自帶load和save,很容易就造出來了。

 

  ^_^

QWidget* Personal::CreateMainInfor() {
    QWidget* pwBox = new QWidget();
    QVBoxLayout* pvMainLayout = new QVBoxLayout(pwBox);

    QLabelCenter* plHead = new QLabelCenter();
    QImage Head = g_pCurrentUser->GetHead();
    Head = Head.scaled(INT_HEAD_WIDTH, INT_HEAD_HEIGHT);
    plHead->setPixmap(QPixmap::fromImage(Head));
    pvMainLayout->addWidget(plHead, INT_SIZERATE_HEAD);

    pvMainLayout->addWidget(new QLabelCenter(QString::fromStdString(g_pCurrentUser->GetNickname())), INT_SIZERATE_NICKNAME);

    pvMainLayout->addWidget(new QLabelCenter(QString::fromStdString(g_pCurrentUser->GetIntroduction())), INT_SIZERATE_INTRO);

    return pwBox;
}
QWidget* Personal::CreateSingleInfor(QString sTip, QString sInfor) {
    QWidget* pwBox = new QWidget();
    QHBoxLayout* phLayout = new QHBoxLayout(pwBox);

    phLayout->addWidget(new QLabelCenter(sTip), INT_SIZERATE_SIN_TIP);
    phLayout->addWidget(new QLabelCenter(sInfor), INT_SIZERATE_SIN_INFOR);
    phLayout->addStretch(INT_SIZERATE_SIN_SPACE);

    return pwBox;
}
QWidget* Personal::CreateInfor() {
    QWidget* pwBox = new QWidget();
    QVBoxLayout* pvLayout = new QVBoxLayout(pwBox);

    pvLayout->addWidget(CreateSingleInfor(STR_TIP_AGE, QString::number(g_pCurrentUser->GetAge())));
    pvLayout->addWidget(CreateSingleInfor(STR_TIP_GENDER, STR_GENDER[g_pCurrentUser->GetGender()]));
    pvLayout->addWidget(CreateSingleInfor(STR_TIP_CERITI, QString::fromStdString(g_pCurrentUser->GetCertificate())));
    pvLayout->addWidget(CreateSingleInfor(STR_TIP_SCORE, QString::number(g_pCurrentUser->GetScore())));
    pvLayout->addWidget(CreateSingleInfor(STR_TIP_BALANCE, QString::number(g_pCurrentUser->GetBalance())));

    return pwBox;
}
QWidget* Personal::CreateSingleModifyPassword() {
    QWidget* pwBox = new QWidget();
    QHBoxLayout* phLayout = new QHBoxLayout(pwBox);


    QWidget* pwTip = new QWidget();
    QVBoxLayout* pvLayoutTip = new QVBoxLayout(pwTip);
    pvLayoutTip->addWidget(new QLabelCenter(STR_TIP_PASSWORD));
    pvLayoutTip->addWidget(new QLabelCenter(STR_TIP_PASSWORDCON));
    phLayout->addWidget(pwTip, INT_SIZERATE_MOD_TIP);

    QWidget* pwEdit = new QWidget();
    QVBoxLayout* pvLayoutEdit = new QVBoxLayout(pwEdit);
    QLineEdit* pleInputPass = new QLineEdit();
    pvLayoutEdit->addWidget(pleInputPass);
    pleInputPass->setEchoMode(QLineEdit::Password);
    QLineEdit* pleInputCon = new QLineEdit();
    pvLayoutEdit->addWidget(pleInputCon);
    pleInputCon->setEchoMode(QLineEdit::Password);
    phLayout->addWidget(pwEdit, INT_SIZERATE_MOD_INPUT);

    QPushButton* ppbButton = new QPushButton(STR_TIP_SIN_MODITY);
    phLayout->addWidget(ppbButton, INT_SIZERATE_MOD_BUTTON);


    connect(ppbButton, &QPushButton::clicked, [=]() {
        if (pleInputPass->text() != pleInputCon->text()) {
            QMessageBox::information(this, STR_MODIFY_FAIL, STR_PASSWORD_UNMATCH);
            return;
        }
        if (g_pCurrentUser->ChangePassword(pleInputPass->text().toStdString()) == true) {
            m_pParentTrans->ChangeContent(new Personal(m_pParentTrans));
        }
        else {
            QMessageBox::information(this, STR_MODIFY_FAIL, STR_MODIFY_FAIL_TEXT);
            return;
        }
    });
    return pwBox;
}
QWidget* Personal::CreateSingleModify(QString sTip, bool(User::*pChange)(std::string)) {
    QWidget* pwBox = new QWidget();
    QHBoxLayout* phLayout = new QHBoxLayout(pwBox);

    phLayout->addWidget(new QLabelCenter(sTip), INT_SIZERATE_MOD_TIP);
    QLineEdit* pleInput = new QLineEdit();
    phLayout->addWidget(pleInput, INT_SIZERATE_MOD_INPUT);
    QPushButton* ppbButton = new QPushButton(STR_TIP_SIN_MODITY);
    phLayout->addWidget(ppbButton, INT_SIZERATE_MOD_BUTTON);
    connect(ppbButton, &QPushButton::clicked, [=]() {
        if ((g_pCurrentUser->*pChange)(pleInput->text().toStdString()) == true) {
            m_pParentTrans->ChangeContent(new Personal(m_pParentTrans));
        }
        else {
            QMessageBox::information(this, STR_MODIFY_FAIL, STR_MODIFY_FAIL_TEXT);
            return;
        }
    });

    return pwBox;
}
QWidget* Personal::CreateSingleModifyGender() {
    QWidget* pwBox = new QWidget();
    QHBoxLayout* phLayout = new QHBoxLayout(pwBox);

    phLayout->addWidget(new QLabelCenter(STR_TIP_GENDER), INT_SIZERATE_MOD_TIP);
    QComboBox* pcbInput = GetInformation::CreateGenderCom();
    phLayout->addWidget(pcbInput, INT_SIZERATE_MOD_INPUT);
    QPushButton* ppbButton = new QPushButton(STR_TIP_SIN_MODITY);
    phLayout->addWidget(ppbButton, INT_SIZERATE_MOD_BUTTON);
    connect(ppbButton, &QPushButton::clicked, [=]() {
        if (g_pCurrentUser->ChangeGender(pcbInput->currentIndex()) == true) {
            m_pParentTrans->ChangeContent(new Personal(m_pParentTrans));
        }
        else {
            QMessageBox::information(this, STR_MODIFY_FAIL, STR_MODIFY_FAIL_TEXT);
            return;
        }
    });


    return pwBox;
}
QScrollArea* Personal::CreateModify() {
    QScrollArea* psaBox = new QScrollArea();

    QWidget* pwBox = new QWidget();
    QVBoxLayout* pvLayout = new QVBoxLayout(pwBox);
    QPushButton* ppbUploadHead = new QPushButton(STR_HEAD_UPLOAD);
    connect(ppbUploadHead, &QPushButton::clicked, [this]() {
        QString filename = QFileDialog::getOpenFileName(this, STR_CHOOSE_IMAGE, "/", STR_IMAGE_FILTER);
        if (filename.isEmpty())
            return;
        else
        {
            QImage Image;
            if (!(Image.load(filename)))
            {
                QMessageBox::information(this, STR_LOAD_FAIL, STR_LOAD_FAIL_TEXT);
                return;
            }
            QString sSavePath = QCoreApplication::applicationDirPath() + STR_HEAD_PATH;
            Image.save(sSavePath + QString::number(g_pCurrentUser->GetId()) + STR_HEAD_TYPE);
            m_pParentTrans->UpdateMenu();
            m_pParentTrans->ChangeContent(new Personal(m_pParentTrans));
        }
    });
    pvLayout->addWidget(ppbUploadHead, INT_SIZERATE_MOD_UPLOAD);


    pvLayout->addWidget(CreateSingleModifyPassword(), INT_SIZERATE_MOD_INTRO);
    pvLayout->addWidget(CreateSingleModify(STR_TIP_AGE, &(User::ChangeAge)), INT_SIZERATE_MOD_AGE);
    pvLayout->addWidget(CreateSingleModify(STR_TIP_INTRO, &(User::ChangeIntro)), INT_SIZERATE_MOD_INTRO);
    pvLayout->addWidget(CreateSingleModify(STR_TIP_CERITI, &(User::ChangeCeriti)), INT_SIZERATE_MOD_CERITI);
    pvLayout->addWidget(CreateSingleModifyGender(), INT_SIZERATE_MOD_GENDER);

    psaBox->setWidget(pwBox);
    psaBox->setWidgetResizable(true);
    return psaBox;
}
QTabWidget* Personal::CreateTab() {
    QTabWidget* pwTabBox = new QTabWidget();

    //標簽頁暫時只有這倆元素
    pwTabBox->addTab(CreateInfor(), STR_TAB_INFOR);
    pwTabBox->addTab(CreateModify(), STR_TAB_MODIFY);

    return pwTabBox;
}

void Personal::Init() {
    if (g_pCurrentUser == NULL) {
        m_pParentTrans->ChangeContent(new Index(m_pParentTrans));
        return;
    }
    QVBoxLayout *pvlMainLayout = new QVBoxLayout(this);

    //主題拆成兩塊,主信息展示和標簽頁
    pvlMainLayout->addWidget(CreateMainInfor(), INT_SIZERATE_INFOR);

    pvlMainLayout->addWidget(CreateTab(), INT_SIZERATE_TAB);
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM