Вы ввели вручную и назвали 200 виджетов ярлыков ? Пусть никто не называет тебя ленивым. :)
Согласно вашему обновлению, вы знаете, как использовать QLabel::setPixmap()
. вы думаете, , что вам нужно, - это получить указатель QLabel из имени, которое будет сочетать две вещи:
Если вы пойдете по этому пути, вы получите нечто вроде:
QWidget* cellWidget = ui->findChild(TR_position);
QLabel* cellLabel = qobject_cast<QLabel*>(cellWidget);
cellLabel->setPixmap(QPixmap(":/images/missspace.png"));
Но ВНИМАНИЕ! В этом подходе много неправильного.
Хрупко : Что если не будет никакого виджета с таким именем (таинственный сбой)? Или, что еще хуже, что если есть несколько виджетов с таким именем, и этот код блаженно не замечает этого странного условия, которое, вероятно, является ошибкой?
Это плохой ООП : Хотя есть некоторые достойные случаи использования динамического приведения (или «снижения»), это обычно указывает на недостаток в дизайне. Вы знаете, что все QLabels - это QWidgets, но не все QWidget - это QLabels ... так что вызов qobject_cast
может вернуть NULL. Это просто еще одна точка отказа. Иногда вы не можете избежать этого, но на самом деле нет причин, по которым ваша программа должна быть структурирована таким образом.
Это ужасно медленно : Поиск виджета по его имени по сути является наивным рекурсивным поиском. Если вы отложили отдельный фрейм виджета для каждой сетки и выполняете только поиск, Qt придется выполнить 100 сравнений строк, чтобы найти последний элемент (т.е. 50 в среднем случае). Представьте себе очистку сетки с помощью цикла ... теперь вы говорите о 100 * 50 сравнениях строк!
Все эти вещи можно избежать. Так же, как можно использовать циклы для установки изображений на элементах управления по имени, можно использовать циклы для создания виджетов в первую очередь. Вы обычно оставляете область для игровой доски пустой в инструменте дизайна, а затем динамически создаете элементы управления с кодом ... присоединяете их к макету с кодом ... и сохраняете указатели на них в 2D-массиве. (Вы не сможете получить к ним доступ по имени ярлыка, вы будете индексировать их так же, как индексируете свою доску.)
Вы можете создать свой собственный класс, производный от QLabel (например, класс GameCell
), который будет содержать информацию для ячейки вашей доски и связанные с ней методы. Тогда вам не понадобится массив виджетов с ярлыками параллельно массиву, представляющему вашу доску. У вас просто будет один массив объектов, который позаботится об обоих аспектах реализации.
ОБНОВЛЕНИЕ : Поскольку вы задали вопрос в комментариях, вот класс 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 может использовать QGridLayout из следующих GameCells:
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 ярлыков программным способом:
![simple battleship](https://i.stack.imgur.com/eLhaI.png)
С моим кодом, получение GameCell по координате (x, y) не требует в среднем 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 не будет большой проблемой для сетки 10х10, поэтому, если вы хотите просто придерживаться этого, то можете. Если вы собираетесь делать это таким образом, то, по крайней мере, вам не следует размещать их все вручную!