您手动输入并命名200 个标签小部件?不要让任何人说你懒惰。 :)
根据您的更新,您现在知道如何使用QLabel::setPixmap()。你什么think您需要从名称中获取 QLabel 指针,该指针是两件事的组合:
如果你沿着这条路走下去,你最终会得到这样的结果:
QWidget* cellWidget = ui->findChild(TR_position);
QLabel* cellLabel = qobject_cast<QLabel*>(cellWidget);
cellLabel->setPixmap(QPixmap(":/images/missspace.png"));
但要小心!这种方法有很多问题。
它很脆:如果碰巧没有任何具有该名称的小部件怎么办(神秘崩溃)?或者更糟糕的是,如果有怎么办?multiple具有该名称和此代码的小部件幸福地前进,对可能是错误的奇怪情况一无所知?
这是糟糕的 OOP:虽然有一些不错的情况使用动态转换(或“向下转换”),但它通常表明设计中存在缺陷。你知道所有的 QLabel 都是 QWidget,但并非所有的 QWidget 都是 QLabels...所以qobject_cast
调用可能会返回 NULL。这只是又一个失败点。有时您无法避免这种情况,但实际上您的程序没有理由需要以这种方式构建。
速度非常慢:通过名称搜索小部件本质上是一种简单的递归搜索。如果您为每个网格留出一个单独的小部件框架并且仅搜索该框架,则 Qt 将必须进行 100 次字符串比较才能找到最后一个元素(因此平均情况下为 50 次)。想象一下用循环清除网格...现在您正在谈论 100*50 字符串比较!
所有这些事情都是可以避免的。正如可以使用循环按名称设置控件上的图像一样,也可以使用循环首先创建小部件。您基本上会在设计工具中将游戏板区域留空,然后使用代码动态创建控件...使用代码将它们附加到布局...并将指向它们的指针保存在 2D 数组中。 (此时您不会通过标签名称访问它们,您会像为板建立索引一样对它们进行索引。)
您可以创建自己的从 QLabel 派生的类(例如GameCell
类),其中包含您的董事会单元的信息以及与其相关的方法。那么您就不需要与代表您的板的数组并行的标签小部件数组。您只需拥有一组对象来处理实现的两个方面。
UPDATE:既然您在评论中询问了具体细节,这里有一个 GameCell 类:
class GameCell : public QLabel
{
Q_OBJECT
public:
enum State { Undiscovered, Hit, Miss };
GameCell (QWidget *parent = 0) : QLabel (parent),
currentState (Undiscovered)
{
syncBitmap();
}
State getState() const { return currentState; }
void setState(State newState) {
if (currentState != newState) {
currentState = newState;
syncBitmap();
}
}
private:
void syncBitmap() { // you'd use setPixmap instead of setText
switch (currentState) {
case Undiscovered: setText("U"); break;
case Hit: setText("H"); break;
case Miss: setText("M"); break;
}
}
State currentState;
};
这通过表现得像 QWidget 以及维护一部分内部状态来完成双重任务。然后 GameMap 小部件可以使用这些 GameCell 的 QGridLayout:
class GameMap : public QWidget {
Q_OBJECT
public:
static const int Rows = 10;
static const int Columns = 10;
GameMap (QWidget* parent = 0) :
QWidget (parent)
{
layout = new QGridLayout (this);
for (int column = 0; column < Columns; column++) {
for (int row = 0; row < Rows; row++) {
GameCell* cell = new GameCell (this);
cells[column][row] = cell;
layout->addWidget(cell, row, column);
}
}
}
private:
GameCell* cells[Columns][Rows];
QGridLayout* layout;
};
如果您愿意,您可以在设计器中的布局中留下想要用 GameMap 小部件填充的空间。或者您可以继续以编程方式完成整个事情。为了简单起见,我将两个板并排放置,并在对话框的表面上有一个垂直分隔符:
class Game : public QDialog
{
Q_OBJECT
public:
Game (QWidget *parent = 0)
: QDialog(parent)
{
targetMap = new GameMap (this);
fleetMap = new GameMap (this);
verticalSeparator = new QFrame (this);
verticalSeparator->setFrameShape(QFrame::VLine);
verticalSeparator->setFrameShadow(QFrame::Sunken);
layout = new QHBoxLayout (this);
layout->addWidget(targetMap);
layout->addWidget(verticalSeparator);
layout->addWidget(fleetMap);
setLayout(layout);
setWindowTitle(tr("Battleship"));
}
private:
GameMap* targetMap;
QFrame* verticalSeparator;
GameMap* fleetMap;
QHBoxLayout* layout;
};
我不会在这里编写整个游戏或让它看起来很花哨。这只是要点,展示了如何以编程方式获取 200 个标签:
使用我的代码,从 (x,y) 坐标获取 GameCell 不需要平均 50 次字符串比较。由于二维数组的形式化和可预测性,索引cells[x][y]
仅需要一次乘法运算和一次加法运算。没有任何贬低,你可以简单地写:
cells[x][y].setState(GameCell::Miss);
ADDENDUM:为每个网格单元创建一个 QWidget 并不一定是首先要采取的方法。有些人可能认为这是“重量级”。如果你的游戏是在一个大的虚拟瓷砖空间上玩的,那么它可能会太慢。您可能会发现了解一下很有用QGraphicsGridLayout,从长远来看,这可能是更合适的方法:
http://doc.qt.io/qt-5/qtwidgets-graphicsview-basicgraphicslayouts-example.html
然而,使用 QWidgets 对于 10x10 网格来说不会有太大问题,所以如果您想坚持使用 QWidgets 那么您可以。如果您打算这样做,那么至少您不应该手动放置它们!