How Should I Go About Displaying A Card?
-
Before I go on, here is my previous post about my program: http://qt-project.org/forums/viewthread/22213/#104697
So, now that I have successfully(atleast I think) implemented my different classes with signals and slots, I have come at a crossroads at implementing printing my cards. Following is my code, including how I normally printed my cards using a console when I wrote the program in C++;
function of both Player.cpp and Dealer.cpp
@void Player::printCard(int Card)
{
rank = ( Card % 13);
if(rank==0)
cout << 'A';
else if(rank < 9)
cout << (rank + 1);
else if(rank == 9)
cout << 'T';
else if(rank == 10)
cout << 'J';
else if(rank == 11)
cout << 'Q';
else
cout << 'K';suit = ( Card / 13 ); if(suit==0) cout << 'D' << endl; else if(suit==1) cout << 'C' << endl; else if(suit==2) cout << 'S' << endl; else cout << 'H' << endl;
}@
As you can see, when I would run this as a console application, I would do two different operations to determine the suit and rank of the card. The problem I am at now is how would I implement this same operation using the signals and slots of Qt? Would I have to make two different operations, once emitting signal and one with a slot? I'm pretty confused on how I would approach this problem. Thank you for your help and time.
-
Regarding the previous discussion, you should now have a GameModel class that implements the game logic plus a GUI Dialog class that implements the graphical user interface. Now I think it would be the job of the GameModel to report which card is to be shown, and then the GUI will react on this info and show the card :-)
So, for example, your GameModel class could have a Signal cardChanged(char c) which is emitted whenever the card has changed. In your code above, which I assume is called each time a new/different card needs to be shown (inside GameModel), you would replace "cout << 'X' << endl" with a "emit cardChanged('X')".
In the GUI class you could have a Slot updateCard(char c) which will handles the update and actually show the card. Assuming that you have a QLable to show the card, the code may look something like:
@void MyDialog::updateCard(char c)
{
qDebug("Going to display card %c", c);switch(c) { case 'A': ui->myLabel->setPixmap(QPixmap("images/card_A.png")); break; /* .... */ default: qFatal("Unknown card encountered!"); }
}@
And don't forget the connect the GameModel class' Signal to the proper Slot of the GUI class...
-
In both cases, console and gui, you should refactor the ifs and the switch to something simpler to modify later, like a lookup table:
@const char Cards[] = "A23456789TJQK";
const char Suits[] = "DCSH";void Player::printCard(int Card)
{
assert(Card >= 0 && Card < 52); // if the card is incorrect, crash
// You'll only have to change the following line if you decide
// to change the way you print the card
cout << Cards[Card] << Cards[Cards/13] << endl;
}
@And with that you can also construct the image filename to avoid using a switch or another list:
@QString imageFilename = "images/card_%1%2.png";
imageFilename = imageFilename.arg(QChar(Suits[Card/13]), QChar(Cards[Card]));
// Which will give something like "images/card_S3.png"
ui->myLabel->setPixmap(QPixmap(imageFilename));@ -
Mulder: The problem I have with this implementation is that I'm sending out only one signal for one thing. I'll try to word as best I can:
You can see from my function that what I would do is take the card being passed in and divide it and then take the modulus to determine both the rank and suit. It was two different operations. So when I try to go do this in Qt, the problem arises because for the first part of the function, which I take the modulus, that helps me determine what the rank is. Here is my question:
You advise me to replace the "cout << 'X' << endl;" with a signal. I understand that. But which one? I have a modulus and a divide operator, both which serve different purposes. This was the problem that I was facing. For example, what if I was emitting a "eightCardSignal()" but when I go to print it out, I wanted to print out an 8 of hearts but ended up displaying an 8 of diamonds? Would one signal be for the rank, connect to my Gui Class slot, which will then determine the suit so then I can know which card to print, or another way? Sorry if my wording sucked :(.
I tried to post my code but it made the post too long and I can't submit it. Sorry. If it help, here is the header files for both GameModel and GuiDialog.
GameModel.h
@#ifndef GAMEMODEL_H
#define GAMEMODEL_H#include <QObject>
#include <QtGui>
#include "Dealer.h"
#include "Player.h"class GameModel : public QObject
{
Q_OBJECTpublic slots:
void bet();signals:
void cardsChanged();
void scoreChanged();
void endHand();
void playerWinner();
void dealerWinner();
void newTie();
void playerThirdCard();
void dealerThirdCard();};
#endif // GAMEMODEL_H@
GuiDialog.h
@
#ifndef GUIDIALOG_H
#define GUIDIALOG_H#include <QObject>
#include <QtGui>
#include <QDialog>
#include "Dealer.h"
#include "Player.h"
#include "GameModel.h"namespace Ui
{
class GuiDialog;
}class GuiDialog: public QDialog
{
Q_OBJECTpublic:
GuiDialog(QWidget *, GameModel *);
public slots:
void cardChange();
void newScore();
void endGame();
void playerWon();
void dealerWon();
void tie();
void updatePlayerThirdCard();
void updateDealerThirdCard();
~GuiDialog();
private:
GameModel *model;
Ui::GuiDialog *ui;};
#endif // GUIDIALOG_H@
Here is how a piece of my code looks like in the GameModel class
@ //Initiates a brand new deck
newDeck.Shuffle();//Sets the players hand to 2 cards drawn from the deck and prints the hand //using the printHand function newPlayer.setHand(newDeck.getNextCard(), newDeck.getNextCard()); playerCardCount++; playerCardCount++; newPlayer.printHand(playerCardCount); //Sets the dealers hand to 2 cards drawn from the deck and prints the hand //using the printHand function newDealer.setHand(newDeck.getNextCard(), newDeck.getNextCard()); dealerCardCount++; dealerCardCount++; newDealer.printHand(dealerCardCount); //Emit a signal that the hands have changed emit cardsChanged();@
EDIT: [quote]So, for example, your GameModel class could have a Signal cardChanged(char c) which is emitted whenever the card has changed. In your code above, which I assume is called each time a new/different card needs to be shown (inside GameModel), you would replace “cout << ‘X’ << endl” with a “emit cardChanged(‘X’)”[/quote]
This function is not part of GameModel, but rather my Player and Dealer class because both players will have different hands, meaning both players must have their own printHand functions. Also, I'm going to change the name of this function to "determineHand" because now that I'm implementing it into a GUI, its not longer printing my hands but rather determining what the hands are to be printed in my updateCards slot inside my GuiDialog class.
-
First of all, I would use enum's here:
@class MyGameModel
{
public:
enum Suit
{
Suit_Clubs = 1,
Suit_Spades = 2,
Suit_Hearts = 3,
Suit_Diamonds = 4
};
enum Card
{
Card_Seven = 7,
Card_Eight = 8,
Card_Nine = 9,
Card_Ten = 10,
Card_Jack = 11,
Card_Queen = 12,
Card_King = 13,
Card_Ace = 14
}signals:
cardChanged(int suite, int card);private:
determineHand(void);
};@Then you would emit the signal like this:
@MyGameModel::determineHand(void)
{
/* Calculate which card is to be shown here... then emit the signal! *//* for example: */ emit cardChanged(Suit_Spades, Card_Ace); /* ... */
}@
Note that we don't need a separate signal for each type of card to be shown, just one signal that indicates that the card has changed. The "suit" and the "card" are simply passed in the signal, as parameters.
In the GUI Dialog you will handle the signal and display the proper card:
@void MyGuiDialog::updateCard(int suite, int card)
{
switch(suite)
{
case MyGameModel::Suit_Clubs:
switch(card)
{
case MyGameModel::Card_Seven:
ui->myLabel->setPixmap(QPixmap("images/card_clubs_seven.png"));
break;
case MyGameModel::Card_Eight:
ui->myLabel->setPixmap(QPixmap("images/card_clubs_eight.png"));
break;
/* ... */
}
break;case MyGameModel::Suit_Spades: switch(card) { /* ... */ } break; /* ... */ }
}@
In case you need to deal with two players, each player could have its own instance/object of the GameModel (or better PlayerModel) class. Then the GUI Dialog needs two Slots to handle the updates.
@//Constructor
MyGuiDialog::MyGuiDialog(PlayerModel *player1, PlayerModel player2)
{
connect(player1, SIGNAL(handChanged(int,int)), this, SLOT(updateFirstPlayer(int,int)));
connect(player2, SIGNAL(handChanged(int,int)), this, SLOT(updateSecndPlayer(int,int)));
}@
@//Main function
int main(/ ... */)
{
//Create player models
PlayerModel *player1 = new PlayerModel();
PlayerModel *player2 = new PlayerModel();//Create application QApplication *app = new QApplication(); //Create GUI dialog MyGuiDialog *gui = new MyGuiDialog(player1, player2); gui->show(); //Give it a go! return app->exec();
}@
-
Awesome. It makes a lot more sense, thank you once again. I didn't get a chance to respond/work on my project yesterday because of having to work on another project for a different class but I will definitely attempt to implement the functions now following your advice. Thank you for your help once again.
-
Alright so here is what I did. I have to go to work in an hour so this all I could work on this afternoon; ill be working some more on it tonight and tomorrow. Please give me advice on whether im doing it correct or incorrectly.
GameModel.h
@#ifndef GAMEMODEL_H
#define GAMEMODEL_H#include <QObject>
#include <QtGui>
#include "Dealer.h"
#include "Player.h"class GameModel : public QObject
{
Q_OBJECTpublic:
enum Suit
{
suitDiamonds=0,
suitClubs=1,
suitSpades=2,
suitHearts=3
};enum Rank { cardAce=0, cardTwo=2, cardThree=3, cardFour=4, cardFive=5, cardSix=6, cardSeven=7, cardEight=8, cardNine=9, cardTen=10, cardJack=11, cardQueen=12, cardKing=13 }; Suit currentSuit; Rank currentRank;
public slots:
void bet();signals:
void cardsChanged(int suite, int rank);
void scoreChanged();
void endHand();
void playerWinner();
void dealerWinner();
void newTie();
void playerThirdCard();
void dealerThirdCard();private:
determineHand(void);
determineCard(int);};@
A snippet of my GameModel.cpp code, as well as the functions determineHand and determineCard
@newPlayer.setHand(newDeck.getNextCard(), newDeck.getNextCard());
playerCardCount++;
playerCardCount++;
determineHand(newPlayer.getHand(), playerCardCount);GameModel::determineCard(int Card)
{
rank = ( Card % 13);
if(rank==0)
currentRank=cardAce;
else if(rank < 9)
currentRank=(rank + 1);
else if(rank == 9)
currentRank=cardTen;
else if(rank == 10)
currentRank=cardJack;
else if(rank == 11)
currentRank=cardQueen;
else
currentRank=cardKing;suit = ( Card / 13 ); if(suit==0) currentSuit=suitDiamonds; else if(suit==1) currentSuit=suitClubs; else if(suit==2) currentSuit=suitSpades; else currentSuit=suitHearts; emit cardChanged(suit,rank);
}
GameModel::determineHand(int playerHand,int cardCount)
{
for(int i=0;i<cardCount;i++)
{
determineCard(playerHand[i]);
}
}int Player::getHand()
{
return playerHand; /This is an array, not sure if I did this correctly. It's an array containing players Hand/
}@GuiDialog::updateCard
@switch(suit)
{
case MyGameModel::suitDiamonds :
switch(rank)
{
case MyGameModel::cardAce:
ui->playerCardOne->setPixMap(QPixMap("cards/dace.jpg"));
break;
case MyGameModel::cardTwo:
ui->playerCardOne->setPixMap(QPixMap("cards/d2.jpg"));
break;
case MyGameModel::cardThree:
ui->playerCardOne->setPixMap(QPixMap("cards/d3.jpg"));
break;
case MyGameModel::cardFour:
ui->playerCardOne->setPixMap(QPixMap("cards/d4.jpg"));
break;@