在Qt中正确实现自定义QWidget

时间:2013-11-23 15:48:51

标签: c++ qt

我是Qt / GUI编程的新手,我正在尝试为简单的Tic Tac Toe游戏创建UI。我创建了两个自定义QWidget类。我的主要窗口类(GameWindow)扩展了基础QWidget,而我的其他类XOSpace扩展了QFrameXOSpace有两个目的:将棋盘划分为空格(每个都有HLineVLine形状),并且是在正确的位置绘制X和O的起点董事会(一旦我弄清楚如何使用Qt画家和绘画事件)。我的问题是,当我将XOSpace添加到GameWindow时,它们不会显示。但是当我从基类中添加QFrame个对象作为测试时,它们显示正常。如何扩展QFrame(或任何窗口小部件类),并确保它的功能与Qt中的基类相同?我需要重新实现的功能吗?还有什么吗?

class XOSpace : public QFrame {
    Q_OBJECT

private:
    XO xo ; //enum representing whether this space holds an X, O, or blank

public:
    explicit XOSpace(QWidget *parent = 0) ;
    explicit XOSpace(QWidget *parent, int size, QFrame::Shape) ;
    ~XOSpace();
    void setXO(XO) ;
};

XOSpace::XOSpace(QWidget *parent) : QFrame(parent) {
    this->xo = XO::blank ;
    this->setGeometry(QRect());
    this->setFrameShape(QFrame::HLine) ;
    this->setFrameShadow(QFrame::Sunken) ;
    this->setMinimumWidth(96) ;
    this->setLineWidth(1) ;
    this->show();
}

XOSpace::XOSpace(QWidget* parent, int size, QFrame::Shape shape) : QFrame(parent) {
    this->xo = XO::blank ;
    this->setGeometry(QRect());
    this->setFrameShape(shape);
    this->setFrameShadow(QFrame::Sunken);
    this->setMinimumWidth(size) ;
    this->setLineWidth(1) ;
    this->show() ;
}

QSize XOSpace::sizeHint() const {
    return this->size();
}

void XOSpace::setXO(XO xo) {
    this->xo = xo ;
}

XOSpace::~XOSpace() {
   ;
}



namespace Ui {
    class GameWindow;
}

class GameWindow : public QWidget {
    Q_OBJECT

private:
    Ui::GameWindow *ui ;

    //these don't display:
    vector<XOSpace*>* hSpaces ;

    //these do:
    QFrame* vLineOne ;
    /* declare 7 more
    like this */

public:
    explicit GameWindow(QWidget *parent = 0);
    ~GameWindow();
    friend class XOSpace ;
};

GameWindow::GameWindow(QWidget *parent) : QWidget(parent),    
        ui(new Ui::GameWindow) {

    ui->setupUi(this);
    this->setWindowTitle("Hello world!");

    QGridLayout *mainLayout = new QGridLayout() ;
    mainLayout->setColumnMinimumWidth(0, 25); 
    mainLayout->setColumnMinimumWidth(6, 25);

    this->setLayout(mainLayout) ;

    QPushButton* button = new QPushButton("Play") ;
    button->setFixedWidth(100);
    mainLayout->addWidget(button, 2, 3) ;

    QGridLayout* secondaryLayout = new QGridLayout() ;
    mainLayout->addLayout(secondaryLayout, 1, 1, 1, 5); 

    QGroupBox* gBox = new QGroupBox() ;
    secondaryLayout->addWidget(gBox, 0, 0);

    QGridLayout* boardLayout = new QGridLayout() ;
    gBox->setLayout(boardLayout);

    hSpaces = new vector<XOSpace*>() ;


    vLineOne = new QFrame() ;
    vLineOne->setGeometry(QRect());
    vLineOne->setFrameShape(QFrame::VLine);
    vLineOne->setFrameShadow(QFrame::Sunken);
    vLineOne->setMinimumHeight(96) ;
    /*repeat for vLines 2-4
     */

    vLineFive = new QFrame() ;
    vLineFive->setGeometry(QRect());
    vLineFive->setFrameShape(QFrame::VLine);
    vLineFive->setFrameShadow(QFrame::Sunken);
    vLineFive->setMinimumHeight(48);
    /*repeat for vLines 6-8
     */

    for(vector<XOSpace*>::size_type i = 0 ; i < 6 ; i++) {
       hSpaces->push_back(new XOSpace(this, 96,  
           QFrame::HLine));
    }

    //horizontal spaces: (don’t display properly)
    boardLayout->addWidget(hSpaces->at(0), 0, 0,  
        Qt::AlignBottom);
    boardLayout->addWidget(hSpaces->at(1), 0, 2, 
        Qt::AlignBottom);
    boardLayout->addWidget(hSpaces->at(2), 0, 4, 
        Qt::AlignBottom);
    boardLayout->addWidget(hSpaces->at(3), 3, 0, Qt::AlignTop);
    boardLayout->addWidget(hSpaces->at(4), 3, 2, Qt::AlignTop);
    boardLayout->addWidget(hSpaces->at(5), 3, 4, Qt::AlignTop);

    //vertical spaces:  (display OK)
    boardLayout->addWidget(vLineOne, 0, 1) ;
    boardLayout->addWidget(vLineFive, 1, 1) ;
    boardLayout->addWidget(vLineTwo, 0, 3) ;
    boardLayout->addWidget(vLineSix, 1, 3) ;
    boardLayout->addWidget(vLineThree, 2, 1) ;
    boardLayout->addWidget(vLineSeven, 3, 1) ;
    boardLayout->addWidget(vLineFour, 2, 3) ;
    boardLayout->addWidget(vLineEight, 3, 3) ;


    mainLayout->setRowStretch(0, 1);
    //set rows and columns stretch

    mainLayout->setVerticalSpacing(0) ;
    //set spacing etc.

}

GameWindow::~GameWindow() {
    delete ui;
    if (hSpaces != nullptr) {
        for(vector<XOSpace*>::size_type i = 0 ; i < hSpaces->size() ; i++) {
            if (hSpaces->at(i) != nullptr) {
                delete hSpaces->at(i) ;
            }
        }
        delete hSpaces ;
    }
}

XOSpace应该被绘制为水平线段,它们将组成一个tic tac toe board上的两条水平线。这是我的应用程序现在的样子: enter image description here

1 个答案:

答案 0 :(得分:3)

这里有很多小建议。您在Qt中创建的第一个或第二个GUI可能很难做到。使用布局,就像你已经开始做的那样会有所帮助。

所以这是我给出的第一对建议:

请勿在{{1​​}}构造函数中调用show。将它们添加到布局中,使其成为显示它的父项工作。因此,在您调用xospace时,它会处理所有嵌套元素的显示。

gameWindow->show();QLabel的子类。将相关元素更改为QFrame类型,然后将QLabel添加到其构造函数或某处,以确保您可以看到您的元素。

如果您没有使用UI表单,我会将其删除。

根据您的游戏布局,这就是您的变量的样子:

setText("X")

为什么不使用会在板上的元素,而不是依赖于这些垂直和水平线的大小,就像在下面标有X的点上一样:

//      vL1     vL2
// hs0  vL6 hs1 vL5 hs2
//      vL3     vL4
// hs3  vL7 hs4 vL8 hs5
//      ???     ???

这样你就可以拿两个并跨越网格。

//  X   vL1  X  vL2  X
// hs0  vL6 hs1 vL5 hs2
//  X   vL3  X  vL4  X
// hs3  vL7 hs4 vL8 hs5
//  X   ???  X  ???  X

然后就像我上面提示的那样,您可以使用boardLayout->addWidget(vLines.at(0), 0, 1, 5, 1) ; boardLayout->addWidget(vLines.at(1), 0, 3, 5, 1) ; mainLayout->setRowStretch(1, 1); QList删除这么多的复制和粘贴代码。

QVector

如果您不使用布局,vLines.append(new QFrame); vLines.append(new QFrame); foreach(QFrame * f, vLines) { //f->setGeometry(QRect()); f->setFrameShape(QFrame::VLine); f->setFrameShadow(QFrame::Sunken); f->setMinimumHeight(96); } 也很有用。将几何设置为QRect(),可能等同于它的默认构造函数。

当你开始将对象树放在一起时,你不必担心它们如何清理:

http://qt-project.org/doc/qt-4.8/objecttrees.html

编辑:

以下是我如何布置电路板:

setGeometry()

然后,如果您只是让QGridLayout * board = new QGridLayout(); for(int r = 0; r < 5; r++) { for(int c = 0; c < 5; c++) { if(r % 2 == 1) { if(c % 2 == 1) { // is an intersection // leave it blank? // or add a box? } else { // is a horizontal line QFrame * f = new QFrame(); f->setFrameShape(QFrame::HLine); f->setFrameShadow(QFrame::Sunken); f->setMinimumWidth(96); board->addWidget(f,r, c); } } else { if(c % 2 == 1) { // is a vertical line QFrame * f = new QFrame(); f->setFrameShape(QFrame::VLine); f->setFrameShadow(QFrame::Sunken); f->setMinimumHeight(96); board->addWidget(f, r, c); } else { // is an XO location board->addWidget(new QLabel(), r, c, Qt::AlignCenter); } } } } setLayout(board); 管理商品的位置和访问权限,则可以执行以下操作:

QGridLayout

希望有所帮助。