Qt网络五子棋-客户端

客户端:

1.客户端布局组件

      左右两边,比例为1:3,左边为聊天框TextBrowser和TextEdit,列表选择框按键和3个label,分别用于计时,显示玩家信息和显示图片,布局方式为竖直VBoxLayout,比例为3:1:1:1:1:3;右边为30*30的棋盘布局方式为网格GridLayout,整体为HBoxLayout。

 widget.h

#ifndef WIDGET_H
#define WIDGET_H
#include <QtGui/QWidget>
#include <QTextBrowser>
#include <QTextEdit>
#include <QListWidget>
#include <QPushButton>
#include <QLabel>
#include <QButtonGroup>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QPaintEvent>
#include <QImage>
#include <QPainter>
#include <QMessageBox>
#include <QTimerEvent>
#include <QTimer>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include "chessindividual.h"
#include "mylabel.h"
#include "thread.h"

#include <QDebug>

class MyTextEdit;
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
    Thread *th;//添加线程成员
private:
    //聊天信息显示
    QTextBrowser *chatBrowser;
    //聊天输入
    MyTextEdit *messageSend;
    //棋子个数列表
    QListWidget *chessNumber;
    //开始按钮
    QPushButton *startBtn;
    //计算时间
    QLabel *timeLabel;
    //玩家提示信息
    QLabel *playerLable;
    //玩家图片
    MyLabel *imageLabel;
    //右边的棋盘部分
    ChessIndividual *chessArea;
    //计时器
    QTimer *timer;
    //时间
    int sec;
    int min;
    //黑白子的标记
    int flag;

    //闪烁的flag
    int blinkflag;
    //上次落子的位置
    int lastTimePos;
    //timer的id
    int timerID;
    //闪烁 是否为第一个落子的标志
    int count;

    //自己在服务器上的fd 区别玩家和观看者
    //为了区分玩家和观看者,在主线程中设置了MyfdonServer默认值为2,
    //该客户端在服务器上的fd,如果是邀请者,邀请被对方同意,自己必然是玩家,
    //如果是被邀请者,同意对方的邀请,自己必然是玩家。根据这两个条件设置MyfdonServer,修改默认值。
    //从而区分开玩家的客户端和观看者的客户端,并做相应的操作限制。

     int myfdOnServer;
    //如果发送了#Agree信号的必然是玩家
    //如果发送了#command 信号,并且收到#Agree信号的必然是玩家;
    //如果发送了#command 信号又收到了#disAgree信号必然不是玩家
protected:
    void timerEvent(QTimerEvent *);
public slots:
    //聊天框的处理:向服务器发送聊天信息
    void sendMsgToServer(QString);
    //start按钮的处理:向服务器发送信息#Command:
    void sendStartMsgToServer();
    //接受到了开始的邀请命令,开启对话框
    void JoinOrNot(int,int);
    //接收到了拒绝游戏的命令
    void refuse();
    //刷新计时器
    void flushTime();
    //开始游戏
    void startGame(int);
    //向服务器发送坐标信息
    void sendMyposition(int);
    //收到坐标信号在相应的位置画图
    void getPos_and_paintImg(int);
    //收到赢家的fd
    void getWinnerInfo(int);

};
//添加MyTextEdit类,继承于QTextEdit类。
//重写该类的KeyPressEvent方法,
//使每次在TextEdit中按下回车键并有内容输入的时候发送信号,
//把内容以QString类型的信号(sendChatMsg(QString))方式发给主线程。
//主线程接受的槽函数拿到信息并把它写给服务器。

class MyTextEdit: public QTextEdit{
    Q_OBJECT//信号和槽的类都是必须的宏名
public:
    MyTextEdit(QWidget *parent = 0);
protected:
    //重写键盘处理函数
    void keyPressEvent(QKeyEvent *e);
signals:
    void sendChatMsg(QString);
//public slots:


};
#endif // WIDGET_H

2.重写MyTextEdit类的KeyPressEvent方法,使每次在TextEdit中按下回车键并有内容输入的时候发送信号,把内容以QString类型的信号方式发给主线程。主线程接受的槽函数拿到信息并把它写给服务器。

MyTextEdit.cpp

#include "widget.h"
#include <QKeyEvent>

/*MyTextEdit类继承了QTextEdit类,并重写keyPressEvent方法,
然后聊天的textEdit实例化为他的对象,就有了keyPressEvent的方法
当ENTER键按下的时候发送信号sendChatMeg(Qstring) ,
chatBrowser绑定该信号执行槽函数append()*/

MyTextEdit::MyTextEdit(QWidget *parent):QTextEdit(parent)
        {

//    w = (Widget *)parent;
}

void MyTextEdit::keyPressEvent(QKeyEvent *e){
    if(e->key() == Qt::Key_Return){
        //截除开始和结尾处的空白字符,过去掉
        if(toPlainText().trimmed() == "")
            return;
        //把自己中的内容以信号的方式发送出去
       emit sendChatMsg(this->toPlainText());
//       toPlainText().toStdString().c_str();
//      将QString转换为const char *
       this->clear();
    }else{
        QTextEdit::keyPressEvent(e);
    }
}

3.添加棋盘类chessindividual,继承于QWidget,在该类型中创建900个按钮ChessBtn和一个按钮组BtnGroup,并声明网格布局。再把所有的按钮添加到网格布局和按钮组中,之后可以用btnGroup的ButtonPressed信号可以传递一个整形的按钮id,可以用该id来确定出点击的棋盘的坐标。

chessindividual.h


#ifndef CHESSINDIVIDUAL_H
#define CHESSINDIVIDUAL_H
#include <QWidget>
#include <QPushButton>
#include <QStackedLayout>
#include <QButtonGroup>
#include <QPixmap>



class ChessIndividual : public QWidget
{
    Q_OBJECT
public:
    explicit ChessIndividual(QWidget *parent = 0);
    //按键组
    QButtonGroup *BtnGroup;
    //900个按键
    QPushButton *chessBtn[30][30];


};

#endif // CHESSINDIVIDUAL_H
棋盘实现文件

chessindividual.cpp

#include "chessindividual.h"

ChessIndividual::ChessIndividual(QWidget *parent) :
    QWidget(parent)
{
    QGridLayout *glayout = new QGridLayout;

    glayout->setSpacing(0);
    glayout->setMargin(5);
    //右半边的900按钮
    //添加按键组
     BtnGroup = new QButtonGroup(this);
    int i,j;
    for(i=0;i<30;i++){
        for(j=0;j<30;j++){
            chessBtn[i][j]=  new QPushButton(this);
            //通过判断这个checkable的属性,来确定这个键是不是已经设置了图标,即:是不是已落子
            //它的默认值为false,现在把它改为true
            //用CSS修改了按钮的样子,用每个按钮的边画出虚线网格
            chessBtn[i][j]->setCheckable(true);
            chessBtn[i][j]->setStyleSheet("border-radius:15px;border-top:1px dashed #000000;border-left:1px dashed #000000;background-color:#F5F5DC;");
            if(i == 29){
                chessBtn[i][j]->setStyleSheet("border-radius:15px;border-bottom:1px dashed #000000;border-top:1px dashed #000000;border-left:1px dashed #000000;background-color:#F5F5DC;");
            }else if(j == 29){
                chessBtn[i][j]->setStyleSheet("border-radius:15px;border-right:1px dashed #000000;border-top:1px dashed #000000;border-left:1px dashed #000000;background-color:#F5F5DC;");
            }

            BtnGroup->addButton(chessBtn[i][j],i*30+j);
            chessBtn[i][j]->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);
            chessBtn[i][j]->setCursor(Qt::PointingHandCursor);
            glayout->addWidget(chessBtn[i][j],i,j);

        }
    }
    chessBtn[29][29]->setStyleSheet("border-radius:15px;border:1px dashed #000000;background-color:#F5F5DC;");
    setLayout(glayout);
}

4.添加myLabel类,继承于QLabel类。声明两个Qpixmap,创建角色标记falg,表示自己和对方的图标。再重写该类的paintEvent方法,使图片在客户端的窗口任意变化的时候能铺满整个label组件。

myLabel,h

#ifndef MYLABEL_H
#define MYLABEL_H
#include <QPaintEvent>
#include <QPainter>
#include <QLabel>

class MyLabel : public QLabel
{
    Q_OBJECT
public:
    explicit MyLabel(QWidget *parent = 0);
    QPixmap map1,map2;

    int flag;//角色标记
signals:

public slots:
    private:

protected:
    void paintEvent(QPaintEvent *);

};

#endif // MYLABEL_H

mylabel.cpp

#include "mylabel.h"
#include <QDebug>
MyLabel::MyLabel(QWidget *parent) :
    QLabel(parent),flag(0)
{
    map1.load(":/mark.jpg");
    map2.load(":/andy.jpg");
}

void MyLabel::paintEvent(QPaintEvent *){
    QPainter painter(this);//指定父控件

    qDebug()<<"in Mylabel paintEvent!";
    //将Map铺满整个组建
    if(flag == 0){
    painter.drawPixmap(0,0,this->width(),this->height(),map1);
    }else{
    painter.drawPixmap(0,0,this->width(),this->height(),map2);
    }

}

5.实现客户端布局


widget.cpp 客户端构造函数

#include "widget.h"


Widget::Widget(QWidget *parent)
    : QWidget(parent),sec(0),min(0),flag(0),myfdOnServer(2),blinkflag(0),count(0)
{
    resize(800,600);
    //左半边所有组件
    chatBrowser = new QTextBrowser(this);
    chatBrowser->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);
    chatBrowser->setStyleSheet("background-image:url(:/guaniu.png);background-position:left bottom;background-attachment:fixed;background-repeat:none;color:#0000FF;font-size:12px;border-radius:3px;padding-bottom:5px;border:1px dashed #008000");
    messageSend =  new MyTextEdit(this);
    messageSend->setStyleSheet("color:#008000;border-radius:5px;border:3px solid #FFFFFF;font-size:14px;");
    messageSend->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);
    chessNumber =  new QListWidget(this);
    chessNumber->setStyleSheet("color:#FFD700;font-size:14px;font-weight:normal;");
    chessNumber->addItem("5 Chess");
    chessNumber->addItem("6 Chess");
    chessNumber->addItem("7 Chess");
    //chessNumber->currentRow();返回当前的列值几子棋?从0开始默认不选的是-1 点了start按钮把这个值传给服务器
    chessNumber->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);
    startBtn = new QPushButton("Start",this);
    startBtn->setFont(QFont("Monospace",18,QFont::Bold,false));
    startBtn->setStyleSheet("*:enabled{background-color:#FFA500;border-radius:6px;color:#F5F5F5;border:3px solid #FFA500;padding:5px 8px;} *:hover{background-color:#FFD700;border-radius:6px;color:#FFFFFF;border:3px solid #FFD700;padding:5px 8px;} *:!enabled{background-color:#A9A9A9;border-radius:6px;color:#FFFFFF;border:3px solid #A9A9A9;padding:5px 8px;}");
    startBtn->setCursor(Qt::PointingHandCursor);
    timeLabel = new  QLabel(this);
    timeLabel->setText("TIME: 00:00");
    timeLabel->setFont(QFont("Monospace",18,QFont::Bold,false));
    timeLabel->setStyleSheet("color:#FFD700;border:1px solid #FFFFFF;border-radius:6px;background-color:#FFFFFF");
    timeLabel->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);
    playerLable = new QLabel(this);
    playerLable->setText("<B style='color:#A9A9A9;font-size:13px;white-space:pre;'>           Attention:</B><p style='color:#FFFF00;text-decoration:underline; text-indent:40px;'>who's turn</p>");
    playerLable->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);
    imageLabel = new MyLabel(this);
    imageLabel->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);


    QHBoxLayout *list_stBtn = new QHBoxLayout;
    list_stBtn->addWidget(chessNumber,2);
    list_stBtn->addWidget(startBtn,1);

    QVBoxLayout *lefthalf = new QVBoxLayout;
    lefthalf->addWidget(chatBrowser,3);
    lefthalf->addWidget(messageSend,1);
    lefthalf->addLayout(list_stBtn,1);
    lefthalf->addWidget(timeLabel,1);
    lefthalf->addWidget(playerLable,1);
    lefthalf->addWidget(imageLabel,3);

    //右半边棋盘的类 900个封装成为一个类 初始化为Widget组件
    chessArea = new ChessIndividual(this);
    QHBoxLayout *mainlayout = new QHBoxLayout;
    mainlayout->addLayout(lefthalf,1);
    mainlayout->addWidget(chessArea,3);
    setLayout(mainlayout);


.....未完

6.添加thread类,继承于QThread类,该类为客户端分离出来的一个子线程,在客户端初始化的时候连接服务器,获取套接字描述符,并实时接收服务器发送回来的数据,执行相应的信号发送操作。

需要的信号有6个:

    1.//发送接收到的信息信号给主线程
    void thread_copy_message(QString);
    2.//收到了其他玩家开始游戏的邀请
    void thread_gameStart_Invite(int,int);
    3. //接收到其他玩家同意加入游戏的消息
       //现在正式进入游戏
    void agree_ToStrat_Game(int);
    4.//接收到对方的拒绝信息
    void thread_gameDisagree();
    5.//接受到对方的坐标信息
    void thread_position(int);
    6.//接受到赢家的消息
    void thread_winner(int);

thread,h文件

#ifndef THREAD_H
#define THREAD_H

#include <QThread>
#include<QTextCodec>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>//inet_ntop()
#include <unistd.h>



class Thread : public QThread
{
    Q_OBJECT
public:
    Thread();
    int sockfd;//套节字描述符
    //执行客户端连到服务器上的操作返回一个int的sockfd
    int conn_to_server();
//protected:
    void run();
signals:
    //发送接收到的信息信号给主线程
    void thread_copy_message(QString);
    //收到了其他玩家开始游戏的邀请
    void thread_gameStart_Invite(int,int);
    //接收到其他玩家同意加入游戏的消息
    //现在正式进入游戏
    void agree_ToStrat_Game(int);
    //接收到对方的拒绝信息
    void thread_gameDisagree();
    //接受到对方的坐标信息
    void thread_position(int);
    //接受到赢家的消息
    void thread_winner(int);

};

#endif // THREAD_H

thread.cpp文件

#include "thread.h"
#include <QDebug>
#define BUF_SIZE 1024

Thread::Thread()
{
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
}

//客户端链接到服务器上面并返回一个int 的socketfd
int Thread::conn_to_server(){
    int client_socked_fd = socket(AF_INET,SOCK_STREAM,0);
    if(client_socked_fd < 0){

        fprintf(stderr,"client_socked_fd %s\n",strerror(errno));
        exit(1);
    }
    //填写主机的信息
    struct sockaddr_in addr;
    memset(&addr,0,sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);/********************port *********************/
    inet_pton(client_socked_fd,"172.16.3.143",&addr.sin_addr.s_addr);

    //connect
    if(::connect(client_socked_fd,(struct sockaddr*)&addr,sizeof(addr)) < 0){
        fprintf(stderr,"client_socked_fd %s\n",strerror(errno));
        exit(1);
    }
    return client_socked_fd;
}


void Thread::run(){
    /*客户端:1发送聊天信息,2实时接受聊天信息
     客户端需要分离出来一个子线程,专门用来读取数据,
     客户端需要:读取服务器发送到的数据,可以以信号的形式将数据发送给主线程,然后由主线程对相关的组建作出修改!*/
    //建立网络链接,拿到套节字描述符
    sockfd = conn_to_server();
    qDebug()<<"socketfd:"<<sockfd;

    //接受服务器返回来的数据,并以信号的形式发给主线程,实现相关的功能
    int nread;
    char buffer[BUF_SIZE];
    while(1){
        memset(buffer,0,sizeof(buffer));

        nread = read(sockfd,buffer,sizeof(buffer));
        if(nread < 0){
        //error
           fprintf(stderr,"client read from server:%s\b",strerror(errno));
           sleep(1);
           exit(1);
        }else if(nread == 0){
            printf("server connection closed!\n");
            exit(1);

        }else{
            //读到了服务器发送回来的数据,
            //分析

            if(strstr(buffer,"#Agree") != NULL){
                //对方同意了我的请求

                if(strlen(buffer) >= 8){
                    QString agreeBuf(buffer);
                    int myfd = agreeBuf.remove(0,7).toInt();
                    qDebug()<<"received myfd:"<<myfd;
                    emit agree_ToStrat_Game(myfd);
                }else{
                    emit agree_ToStrat_Game(2);
                }

            }else if(strstr(buffer,"#Invite:") != NULL){
                //服务器发给了我开始命令  "#Invite:%d,%d"
                //第一个是对方的fd,第二个是自己的
                QString temp(buffer);
                temp.remove(0,8);//?,?
                QString teammatefd(temp);
                teammatefd.remove(1,2);
                QString myfd(temp);
                myfd.remove(0,2);
                //得到对方和自己的fd
                emit thread_gameStart_Invite(teammatefd.toInt(),myfd.toInt());

            }else if(strstr(buffer,"#disAgree") != NULL){
                //收到了拒绝的消息
                emit thread_gameDisagree();
                qDebug()<<"对方拒绝与我玩游戏";

           }else if(strstr(buffer,"#Pos:") != NULL){
            //收到了坐标信息解析为int型
               QString positon(buffer);
               positon.remove(0,5);
               emit thread_position(positon.toInt());
               //qDebug()<<"收到坐标 x:"<<positon.toInt()/30<<"y:"<<positon.toInt()%30;

           }else if(strstr(buffer,"#Winner") != NULL){
               //服务器发回了赢家的消息
               QString winner(buffer);
               QString x("#");
               int index = winner.indexOf(x);
               winner.remove(index,strlen(winner.toStdString().c_str()));
               qDebug()<< "winner is:"<<winner.toInt();
               emit thread_winner(winner.toInt());

           }else{
                //服务器发给我的是聊天信息
                emit thread_copy_message(buffer);
            }
        }
    }
}


7.主线程对线程thread的五个信号的槽函数和自己的4个槽函数:

定义:

对线程thread的五个信号的槽函数

    //接受到了开始的邀请命令,开启对话框
    void JoinOrNot(int,int);
    //接收到了拒绝游戏的命令
    void refuse();
    //开始游戏
    void startGame(int);
    //收到坐标信号在相应的位置画图
    void getPos_and_paintImg(int);
    //收到赢家的fd
    void getWinnerInfo(int);

主线程自己的一些槽函数:

//聊天框的处理:向服务器发送聊天信息
    void sendMsgToServer(QString);
    //start按钮的处理:向服务器发送信息#Command:
    void sendStartMsgToServer();
 //刷新计时器
    void flushTime();
//向服务器发送坐标信息
    void sendMyposition(int);

实现:

所有槽函数的实现:

//向服务器发送文字消息槽函数,meg来自与MyTextEdit的自定义信号
void Widget::sendMsgToServer(QString msg){
    //格式化输入,让服务器来判断发送的是聊天信息还是开始游戏的指令
    char message[1024] = {0};
    //发送的信息message
    sprintf(message,"#Chat:%s",msg.toStdString().c_str());
    ssize_t len = strlen(message);
    qDebug()<<"message len :"<<len;
    if(write(th->sockfd,message,len)!=len){
        //发送失败
        fprintf(stderr,"snedtoserver %s\n",strerror(errno));
        exit(1);
    }
}
//开始按键的槽函数,向服务器发送命令
 void Widget::sendStartMsgToServer(){
    char StartMsg[10] = "#Command:";
    ssize_t len = strlen(StartMsg);
    if(write(th->sockfd,StartMsg,len) != len){
        fprintf(stderr,"send Start Command: %s",strerror(errno));
    }
 }

 //是不是开始游戏的槽函数,是否要开始都给对方发送消息
 void Widget::JoinOrNot(int parterfd,int myfd){


     char Content[256] = {0};
     sprintf(Content,"你同意与 玩家%d 玩一局不?",parterfd);
     int ret = QMessageBox::question(this,"有人邀请你加入游戏",Content,\
                           QMessageBox::No,QMessageBox::Yes);
     if(ret == QMessageBox::Yes){
     //我同意了对方的邀请,现在给对方发送同意的消息 -->服务器 --> 对方客户端
     //我的start按键Enable false 计时器start 棋盘可以点击
     //qDebug()<<"I agree the invite";
         myfdOnServer = myfd;
         qDebug()<<"被邀请者:onJoinOrOnt myfdOnServer:"<<myfdOnServer;
         char AgreeMsg[256] = {0};
         sprintf(AgreeMsg,"#Agree:%d",parterfd);
         ssize_t len = strlen(AgreeMsg);
        if(write(th->sockfd,AgreeMsg,strlen(AgreeMsg)) != len){
            fprintf(stderr,"send AgreeMsg to server:%s\n",strerror(errno));
        }    
     }else if(ret == QMessageBox::No){
     //我拒绝了对方的邀请,给对方发送拒绝的消息
         char disAgreeMsg[256] = {0};
         //附带上对方的fd
         sprintf(disAgreeMsg,"#disAgree:%d",parterfd);
         ssize_t len = strlen(disAgreeMsg);
         if(write(th->sockfd,disAgreeMsg,len) != len){
            fprintf(stderr,"send disagree command:%s\n",strerror(errno));
            exit(1);
         }
     }
 }
 //刷新时间计时器
  void Widget::flushTime(){
      sec++;
      if(sec == 60){
        sec=0;
        min++;
      }
      if(min ==60){
      min=0;
      }
      QString timerstr;
      timerstr.sprintf("TIME: %02d:%02d",min,sec);
      timeLabel->setText(timerstr);

  }
  //对方同意了我的邀请,开始游戏
  void Widget::startGame(int myfd){
    //还原时间
      min = 0;
      sec = 0;
    //时间计时器开启
    timer->start();
    //count 清理
    count = 0;
    //开始键不可用
    startBtn->setEnabled(false);
    //停止闪烁
    killTimer(timerID);
    //清空上局棋子
    if(myfd > 3)myfdOnServer = myfd;
    for(int i=0;i<900;i++){
        chessArea->chessBtn[i/30][i%30]->setIcon(QIcon(""));
        chessArea->chessBtn[i/30][i%30]->setCheckable(true);
        //chessArea->chessBtn[i/30][i%30]->setEnabled(true);
    }
  }
  //收到了对方拒绝我邀请的命令
  void Widget::refuse(){
      QMessageBox::information(this,"对不起!","对方拒绝了你的邀请。",\
                                 QMessageBox::Ok);
  }
  //向服务器发送自己的坐标点
  void Widget::sendMyposition(int pos){
     //在发送端切断已设置过的图标的发送,以免被重新设置ICON 如:对手的棋子又被重设为自己的
      if(chessArea->chessBtn[pos/30][pos%30]->isCheckable() == true){
          char strpos[10] = {0};
          sprintf(strpos,"%d#Pos:%d",myfdOnServer,pos);
          qDebug()<<"on SendMyPosition:"<<myfdonserver int="" len="strlen(strpos);" if="" myfdonserver="">= 4){
              if(write(th->sockfd,strpos,len) != len){
                  fprintf(stderr,"send positon msg to server:%s\n",strerror(errno));
                  exit(1);
              }
          }
      }

  }
  //收到坐标点 并开始在相应的位置画图
  void Widget::getPos_and_paintImg(int pos){
      //保存落子上一次的位置
      //本次的闪烁,上一次的设置图标
      count++;
      if(count > 1){
          killTimer(timerID);
      }


      if(count > 1){
          int i = lastTimePos/30;
          int j = lastTimePos%30;

          if(flag == 0){
              //对方的图标 paintingEvent
              imageLabel->flag = 1;
              this->update();
              playerLable->setStyleSheet("*{background-image:url(:/chess2.png);background-position: left top;background-repeat:none; color:#00ff00}");
              chessArea->chessBtn[i][j]->setIcon(QIcon(":/chess2.png"));
              flag = 1;
          }else{
              //我的图标 paintingEvent
              imageLabel->flag = 0;
              this->update();
              playerLable->setStyleSheet("*{background-image:url(:/chess4.png);background-position: left top;background-repeat:none; color:#00ff00}");
              chessArea->chessBtn[i][j]->setIcon(QIcon(":/chess4.png"));
              flag = 0;
          }
      }
      //保留本次的位置
      lastTimePos = pos;
      startTimer(500);

      //设置该按键的可选中属性为false 防止统一个坐标重复设置
      chessArea->chessBtn[pos/30][pos%30]->setCheckable(false);

      }
  //闪烁
  void Widget::timerEvent(QTimerEvent *event){
      timerID = event->timerId();
      if(blinkflag == 0){

          chessArea->chessBtn[lastTimePos/30][lastTimePos%30]\
                  ->setIcon(QIcon(":/chess1.png"));
          blinkflag = 1;
      }else{
          if(flag == 0)
          chessArea->chessBtn[lastTimePos/30][lastTimePos%30]\
                  ->setIcon(QIcon(":/chess2.png"));
          if(flag > 0)
              chessArea->chessBtn[lastTimePos/30][lastTimePos%30]\
                      ->setIcon(QIcon(":/chess4.png"));

          blinkflag = 0;
      }


  }
  void Widget::getWinnerInfo(int winnerFd){
      //按键变亮
      startBtn->setEnabled(true);
      //停止计时器
      timer->stop();

      if(winnerFd == myfdOnServer){
      //自己赢了游戏
          QMessageBox::information(this,"恭喜你!","赢得了本局游戏。",\
                                     QMessageBox::Ok);

      }else{
            if(myfdOnServer == winnerFd-1||myfdOnServer == winnerFd+1){
                //自己输了游戏
                QMessageBox::information(this,"很遗憾!","你输了,再玩一局吧!",\
                                           QMessageBox::Ok);
            }else{
                char winnerInfo[20]={0};
                sprintf(winnerInfo,"本局的赢家是:%d",winnerFd);
                QMessageBox::information(this,"玩家报告!",winnerInfo,\
                                           QMessageBox::Ok);
            }

      }


  }</myfdonserver>

8.对widget.cpp中构造函数的添加:信号和槽函数的绑定

 1,线程Thread的信号和自己的槽函数绑定

//线程Thread的对象  收到消息的信号和槽函数的绑定
    th = new Thread;//900:1发送按键组坐标点的信号和槽函数,pos = i*30+j
    connect(chessArea->BtnGroup,SIGNAL(buttonPressed(int)),this,SLOT(sendMyposition(int)));
    //客户端接收到的(read())从服务器发的【文字】消息,并把它追加到chatVrowser中
    connect(th,SIGNAL(thread_copy_message(QString)),chatBrowser,SLOT(append(QString)));
    //客户端收到从服务器发过来的开始游戏的邀请命令
    connect(th,SIGNAL(thread_gameStart_Invite(int,int)),this,SLOT(JoinOrNot(int,int)));
    //客户端收到对方发过来的同意游戏的消息 开始游戏
    connect(th,SIGNAL(agree_ToStrat_Game(int)),this,SLOT(startGame(int)));
    //客户端接收到对方发来的拒绝游戏的消息 弹出拒绝消息
    connect(th,SIGNAL(thread_gameDisagree()),this,SLOT(refuse()));
    //接受到位置的坐标信息并开始画图的信号函数和槽函数绑定
    connect(th,SIGNAL(thread_position(int)),this,SLOT(getPos_and_paintImg(int)));
    //接受到赢家的消息
    connect(th,SIGNAL(thread_winner(int)),this,SLOT(getWinnerInfo(int)));
    th->start();

2.自己的信号和槽函数绑定

connect(messageSend,SIGNAL(sendChatMsg(QString)),this,SLOT(sendMsgToServer(QString)));
    //开始按键的信号和槽函数
    connect(startBtn,SIGNAL(clicked()),this,SLOT(sendStartMsgToServer()));
    //900:1发送按键组坐标点的信号和槽函数,pos = i*30+j
   connect(chessArea->BtnGroup,SIGNAL(buttonPressed(int)),this,SLOT(sendMyposition(int)));
    

   //计时器的初始化 一秒为最小计时单位
   timer = new QTimer(this);
   timer->setInterval(1000);
   //刷新时间 设置timelabel上面的时间
   connect(timer,SIGNAL(timeout()),this,SLOT(flushTime()));

9.其他的一些各问题说明,【已经添加到代码注释中】

  • 为了区分玩家和观看者,在主线程中设置了MyfdonServer默认值为2,该客户端在服务器上的fd,如果是邀请者,邀请被对方同意,自己必然是玩家,如果是被邀请者,同意对方的邀请,自己必然是玩家。根据这两个条件设置MyfdonServer,修改默认值。从而区分开玩家的客户端和观看者的客户端,并做相应的操作限制。
  • 为了实现轮流落子,思路可以在服务器上面设置标志位,每一次落子后修改标志位。也可以把自己的Fd和棋子的坐标同时发送给服务器,服务器解析后判断当前是哪一个客户端的落子操作,从而做转发,若当前和上一次是同一个客户端发的坐标信息,则不作转发。
  • 对于已有棋子的位置重复点击可以重新设置的问题,可以设置chessBtn的Checkable属性,客户端每一次收到了服务器的"#Pos:"位置信息,设置该位置按键的Checkable属性为false,再对客户端发送棋子坐标的地方做判断,若该棋子的Checkable属性为false则不发送。
  • 输赢由服务器做判断,服务器保存一张虚拟的二维表,存储两方落子的位置信息,并标记是谁的棋子,然后判断四个方向的自己的棋子数量是否为5,若有一方到5,就发送"#Winner:"信息。
  • 客户端对于"#Winner:"信息做不同反应,弹出不同的对话框,timer计时器停止,startBtn复原。
  • 最后一颗棋子的闪烁效果:重写Widget的timerEvent,保存timerId,设置blinkflag标志位实现交替闪烁。在设置图标的槽函数中杀掉上次的timerId,设置上次位置的图标,保存本次的图标位置,开始本次的计时器。
  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值