在Qt中正确实现自定义QWidget [英] Correctly implementing a custom QWidget in Qt

查看:197
本文介绍了在Qt中正确实现自定义QWidget的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是全新的Qt / GUI编程,我试图创建一个简单的Tic Tac Toe游戏的UI。我创建了两个自定义的 QWidget 类。我的主窗口类( GameWindow )扩展了基础 QWidget ,和我的其他类 XOSpace extends QFrame XOSpace 用于两个目的:将板划分为空格(每个将有一个 HLine VLine shape),并且是在板子上的正确位置绘制X和O的起点(一旦我能知道如何使用Qt绘图器和绘制事件)。我的问题是,当我添加 XOSpace GameWindow 他们不显示。但是当我从基类中添加 QFrame 对象作为测试时,它们显示正常。如何扩展 QFrame (或任何窗口小部件类),并仍然确保它的功能与基类Qt相同?有什么功能需要重新实现吗?还有什么?

I'm completely new to Qt/GUI programming and I'm trying to create the UI for a simple Tic Tac Toe game. I created two custom QWidget classes. My main window class (GameWindow) extends the base QWidget, and my other class XOSpace extends QFrame. XOSpace serves two purposes: dividing up the board into spaces (each one will have a HLine or VLine shape), and being a starting point for drawing X's and O's in the correct spot on the board (as soon as I can figure out how to use Qt painters and paint events). My problem is that when I add the XOSpaces to GameWindow they don't display. But when I added QFrame objects from the base class as a test, they displayed fine. How do I extend QFrame (or any widget class), and still make sure it will function the same as the base classes in Qt? Are there any functions I need to reimplement? Anything else?

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 s应该绘制为水平线段,构成tic tac脚趾板上的两条水平线。这里是我的应用程序现在的样子:


The XOSpaces are supposed to be drawn as horizontal line segments that will make up the two horizontal lines on a tic tac toe board. Here's what my application looks like now:

推荐答案

这里有很多小小的建议。你在Qt中做的第一个GUI或两个可能会很难做。使用布局,像你已经开始做会帮助相当多。

Here is a lot of little pieces of advice. The first GUI or two that you make in Qt will probably be pretty hard to do. Using layouts, like you have started doing will help quite a bit.

这里是我给出的第一对夫妇的建议:

So here are the first couple recommendations I would give:

不要调用 show xospace 构造函数中。将它们添加到布局,使其成为父母的工作来显示它。所以在你的主要当你调用 gameWindow-> show(); 它处理所有的嵌套元素的显示。

Don't call show in your xospace constructors. Adding them to a layout, makes it the parent's job to show it. So in your main when you call gameWindow->show(); it handles all the showing of nested elements.

QLabel QFrame 的子类。将有问题的元素更改为 QLabel 类型,然后将 setText(X)添加到其构造函数

QLabel is a subclass of QFrame. Change the elements in question to be of type QLabel and then add in setText("X") to their constructor or somewhere to make sure you can see your elements.

如果您没有使用UI表单,我会遗漏它。

If you aren't using a the UI form, I would leave it out.

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

Based on your game layout, this is what your variables look like:

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

不要依赖于这些垂直和水平线的大小来拉伸和尺寸限制,使用将位于板上的元素,如在下面标记有X的点:

Instead of relying on the size of these vertical and horizontal lines for your stretching and size constraints, why not use the elements that will sit on the board, like in spots marked with an X below:

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

这将允许你采取两个并跨越网格。

This would allow you to take two and span them across the grid.

boardLayout->addWidget(vLines.at(0), 0, 1, 5, 1) ;
boardLayout->addWidget(vLines.at(1), 0, 3, 5, 1) ;
mainLayout->setRowStretch(1, 1);

然后像上面提到的,你可以使用 QList QVector 删除这么多复制和粘贴代码。

Then like I hint above, you could use a QList or a QVector to remove so much copy and paste code.

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);
}

setGeometry()在不使用布局时很有用。并且将几何设置为QRect(),可能等效于它具有的默认构造函数。

Also setGeometry() is useful if you aren't using layouts. And setting the geometry to QRect(), is probably equivalent to the default constructor it has.

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

And when you start putting object trees together, you don't have to worry as much about how they clean up:

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

编辑:

这是我如何布局板:

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 项目的位置和访问,你这样做:

Then if you just let QGridLayout manage your item's location and access, you do something like this:

void GameWindow::setXO(QString val, int r, int c)
{
    // upper left xo location is 0,0
    // lower right xo location is 2,2
    // we map to skip the frame locations
    if(r > 2 || r < 0 || c < 0 || c > 2)
    {
        qDebug() << "Error in setXO" << r << c;
        return;
    }

    QLabel * xo = qobject_cast<QLabel*>(board->itemAtPosition(r*2, c*2)->widget());
    if(xo != 0)
    {
        xo->setText(val);
    }
}

希望有帮助。

这篇关于在Qt中正确实现自定义QWidget的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆