Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Resize window when replacing widget
Forum Updated to NodeBB v4.3 + New Features

Resize window when replacing widget

Scheduled Pinned Locked Moved Unsolved General and Desktop
6 Posts 4 Posters 400 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    Smeeth
    wrote on last edited by
    #1

    I created a Minesweeper application that allows the user to start a game with different numbers of rows, columns, and bombs.

    The first game looks great, and the window fits around game board properly.
    47b4c1da-4c54-46e8-9328-04db7f374967-image.png

    However, when I make the board smaller, the columns get spaced out to fill the space of the window and the rows push to the bottom of the screen.
    b18d15f3-61ac-4f90-b023-9fbf97c0c947-image.png

    How can I make the window resize to the size it should be so the board is not stretched out? Is there anyway to fit to layout without setting the explicit pixel size of the window?

    Here is my window code, and the method where the panel is replaced.

    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent),
          clicksCountLbl(new QLabel),
          layout(new QGridLayout)
    {
        QAction *gamesAction = menuBar()->addAction("Game");
        connect(gamesAction, &QAction::triggered, this, &MainWindow::openGame);
    
        QWidget *centralWidget = new QWidget;
    
        int rows = 10;
        int cols = 10;
        numBombs = 8;
        uncoveredSquares = rows*cols - numBombs;
    
        panel = new MinesweeperPanel(10, 10, 8, this);
        connect(panel, &MinesweeperPanel::bombClicked, this, [=](void){endGame(false);});
        connect(panel, &MinesweeperPanel::click, this, &MainWindow::click);
        connect(panel, &MinesweeperPanel::uncovered, this, &MainWindow::uncovered);
    
        layout->addWidget(clicksCountLbl, 0, 1);
        layout->addWidget(panel, 1, 0, 1, 2);
    
        centralWidget->setLayout(layout);
    
        this->setCentralWidget(centralWidget);
    }
    
    void MainWindow::openGame(){
        GameDialog dialog;
    
        dialog.exec();
    
        layout->removeWidget(panel);
    
        delete panel;
    
        panel = new MinesweeperPanel(dialog.height(), dialog.width(), dialog.mines());
    
        layout->addWidget(panel, 1, 0, 1, 2);
        
        this->update();
    }
    
    
    JonBJ 1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      You should also show the implementation of your MinesweeperPanel class.

      From the looks of it, you should make its size fixed based on its content.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      1
      • S Offline
        S Offline
        Smeeth
        wrote on last edited by
        #3

        Panel class is rather long. I'll post it below.

        I figured there may not be a way to fit to contents. I guess I will just have to do a bit of math to figure it out.

        #include "minesweeperpanel.h"
        #include <QGridLayout>
        #include <QRandomGenerator>
        #include <QDebug>
        #include <QMessageBox>
        #include <QApplication>
        
        int MinesweeperPanel::boxDimensions = 30;
        
        MinesweeperPanel::MinesweeperPanel(int rows, int cols, int numBombs, QWidget *parent) :
            QWidget(parent),
            rows(rows),
            cols(cols),
            numBombs(numBombs)
        {
        
            setStyleSheet("QPushButton{min-width:" + QString::number(boxDimensions) + "px; \
                                        max-width:" + QString::number(boxDimensions) + "px; \
                                        min-height:" + QString::number(boxDimensions) + "px; \
                                        max-height:" + QString::number(boxDimensions) + "px; \
                                        background-color:rgb(185,185,185);}");
            QGridLayout *layout = new QGridLayout;
            layout->setSpacing(0);
        
            //allocate dynamic arrays of size rows
            board = new MinesweeperButton**[cols];
            bombs = new bool*[cols];
            flags = new bool*[cols];
            revealed = new bool*[cols];
        
            //set up board and initailize bombs
            for (int i = 0; i < rows; i++) {
                //allocate row
                board[i] = new MinesweeperButton*[cols];
                bombs[i] = new bool[cols];
                flags[i] = new bool[cols];
                revealed[i] = new bool[cols];
                for (int j = 0; j < cols; j++){
                    //create button
                    board[i][j] = new MinesweeperButton;
                    board[i][j]->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
                    board[i][j]->setIcon(QIcon("://images/facingDown.png"));
                    board[i][j]->setIconSize(QSize(boxDimensions, boxDimensions));
                    connect(board[i][j], &QPushButton::clicked, this, [=](void){buttonClicked(i,j);});
                    connect(board[i][j], &MinesweeperButton::rightClicked, this, [=](void){buttonRightClicked(i,j);});
                    layout->addWidget(board[i][j], i, j);
                    //initialize all bomb spots to false
                    bombs[i][j] = false;
                    flags[i][j] = false;
                    revealed[i][j] = false;
                }
                // each i-th pointer is now pointing to dynamic array (size rows) of actual int values
            }
        
            for (int i=0; i<numBombs; i++){
                int x = static_cast<int>(QRandomGenerator::global()->generateDouble() * (rows-1));
                int y = static_cast<int>(QRandomGenerator::global()->generateDouble() * (cols-1));
                bombs[x][y] = true;
            }
        
            this->setLayout(layout);
        }
        
        MinesweeperPanel::~MinesweeperPanel()
        {
            for (int i=0; i<rows; i++){
                for (int j=0;j<cols; j++){
                    delete board[i][j];
                }
            }
            delete this->layout();
        }
        
        /**
         * @brief MinesweeperPanel::buttonClicked
         * Checks for bomb, then starts flood fill from given coordinate
         * @param row
         * @param col
         */
        void MinesweeperPanel::buttonClicked(int row, int col){
        
            if(revealed[row][col])
                return;
        
            emit click(row, col);
        
            if (bombs[row][col]){ //bomb clicked
                board[row][col]->setStyleSheet("background-color:red;");
                board[row][col]->setIcon(QIcon("://images/bomb.png"));
                emit bombClicked();
            }
        
            floodFill(row, col);
        }
        
        /**
         * @brief MinesweeperPanel::buttonRightClicked
         * Toggles flag on given coordinates
         * @param row
         * @param col
         */
        void MinesweeperPanel::buttonRightClicked(int row, int col){
        
            if(revealed[row][col])
                return;
        
            flags[row][col] = !flags[row][col]; //flip flag
        
            emit flagged(row, col, flags[row][col]);
        
            if (flags[row][col]){
                board[row][col]->setIcon(QIcon("://images/flagged.png"));
            } else{
                board[row][col]->setIcon(QIcon("://images/facingDown.png"));
            }
        
        }
        
        /**
         * @brief MinesweeperPanel::isWithinBounds
         * @param x
         * @param y
         * @return square is within bounds of grid
         */
        bool MinesweeperPanel::isWithinBounds(int x, int y){
            return x>=0 && x<rows && y>=0 && y<cols;
        }
        
        /**
         * @brief MinesweeperPanel::floodFill
         * Recursive method that reveals board starting from given coordinates,
         * then checks all neighbors if square has no neighboring bombs.
         * @param x
         * @param y
         */
        void MinesweeperPanel::floodFill(int x, int y){
        
             if (!isWithinBounds(x,y) //if out of bounds
                    || bombs[x][y] //or found bomb
                    || revealed[x][y]) //or already visited this square
                return;
        
            revealed[x][y] = true;
        
            int neighbors = neighboringBombs(x, y);
            board[x][y]->setIcon(QIcon("://images/" + QString::number(neighbors) + ".png"));
        
            emit uncovered(x,y);
        
            if (neighbors == 0){
                //search neighbors
                floodFill(x-1, y); //left
                floodFill(x+1, y); //right
                floodFill(x-1, y-1); //left up
                floodFill(x-1, y+1); //left down
                floodFill(x+1, y-1); //right up
                floodFill(x+1, y+1); //right down
                floodFill(x, y+1); //up
                floodFill(x, y-1); //down
            }
        }
        
        /**
         * @brief MinesweeperPanel::isBomb
         * @param x
         * @param y
         * @return square contains bomb
         */
        bool MinesweeperPanel::isBomb(int x, int y){
            return !isWithinBounds(x,y) //within bounds
                    && bombs[x][y]; //is bomb
        }
        
        /**
         * @brief MinesweeperPanel::neighboringBombs
         * @param x
         * @param y
         * @return number of neighboring squares that are bombs
         */
        int MinesweeperPanel::neighboringBombs(int x, int y){
            int bombNeighbors=0;
            for (int i=-1; i<=1; i++){
                for (int j=-1; j<=1; j++){
                    if (isWithinBounds(x+i, y+j) && //is within bounds
                            !(i==0 && j==0) && //don't check current square
                            bombs[x+i][y+j]) //is bomb
                      bombNeighbors++;
                }
            }
        
            return bombNeighbors;
        }
        
        /**
         * @brief MinesweeperPanel::revealBoard
         * Uncover all squares in the board
         */
        void MinesweeperPanel::revealBoard(){
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++){
                    if (!revealed[i][j]){
                        if (bombs[i][j])
                            board[i][j]->setIcon(QIcon("://images/bomb.png")); //reveal bomb
                        else
                            board[i][j]->setIcon(QIcon("://images/" + QString::number(neighboringBombs(i, j)) + ".png")); //reveal # neighbors
                    }
                }
            }
        }
        
        
        1 Reply Last reply
        0
        • VRoninV Offline
          VRoninV Offline
          VRonin
          wrote on last edited by
          #4

          OMG that's a grid of buttons?! that should be implemented with QTableView/QTableWidget

          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
          ~Napoleon Bonaparte

          On a crusade to banish setIndexWidget() from the holy land of Qt

          JonBJ 1 Reply Last reply
          0
          • VRoninV VRonin

            OMG that's a grid of buttons?! that should be implemented with QTableView/QTableWidget

            JonBJ Online
            JonBJ Online
            JonB
            wrote on last edited by
            #5

            @VRonin
            ... I can hear the hooves of QStyledItemDelegate thundering toward us... ;)

            1 Reply Last reply
            0
            • S Smeeth

              I created a Minesweeper application that allows the user to start a game with different numbers of rows, columns, and bombs.

              The first game looks great, and the window fits around game board properly.
              47b4c1da-4c54-46e8-9328-04db7f374967-image.png

              However, when I make the board smaller, the columns get spaced out to fill the space of the window and the rows push to the bottom of the screen.
              b18d15f3-61ac-4f90-b023-9fbf97c0c947-image.png

              How can I make the window resize to the size it should be so the board is not stretched out? Is there anyway to fit to layout without setting the explicit pixel size of the window?

              Here is my window code, and the method where the panel is replaced.

              MainWindow::MainWindow(QWidget *parent)
                  : QMainWindow(parent),
                    clicksCountLbl(new QLabel),
                    layout(new QGridLayout)
              {
                  QAction *gamesAction = menuBar()->addAction("Game");
                  connect(gamesAction, &QAction::triggered, this, &MainWindow::openGame);
              
                  QWidget *centralWidget = new QWidget;
              
                  int rows = 10;
                  int cols = 10;
                  numBombs = 8;
                  uncoveredSquares = rows*cols - numBombs;
              
                  panel = new MinesweeperPanel(10, 10, 8, this);
                  connect(panel, &MinesweeperPanel::bombClicked, this, [=](void){endGame(false);});
                  connect(panel, &MinesweeperPanel::click, this, &MainWindow::click);
                  connect(panel, &MinesweeperPanel::uncovered, this, &MainWindow::uncovered);
              
                  layout->addWidget(clicksCountLbl, 0, 1);
                  layout->addWidget(panel, 1, 0, 1, 2);
              
                  centralWidget->setLayout(layout);
              
                  this->setCentralWidget(centralWidget);
              }
              
              void MainWindow::openGame(){
                  GameDialog dialog;
              
                  dialog.exec();
              
                  layout->removeWidget(panel);
              
                  delete panel;
              
                  panel = new MinesweeperPanel(dialog.height(), dialog.width(), dialog.mines());
              
                  layout->addWidget(panel, 1, 0, 1, 2);
                  
                  this->update();
              }
              
              
              JonBJ Online
              JonBJ Online
              JonB
              wrote on last edited by JonB
              #6

              @Smeeth
              If there isn't an automatic size policy you can apply to the main window to shrink to contents (I don't know), what about mainWindow->adjustSize() (or resize(minimumSizeHint()))? You may have to call it on a QTimer after the board's dimensions have been changed.

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved