PDA

View Full Version : Random Sudoku board generator



laurynas2003
10th April 2020, 10:19
Hello ,

So i am trying to generate a sudoku board that would be solvable, with 20 random digits in random positions , but when I run the code numbers appear in different places then they should be , for example
my function generates y = 8 x = 8 value = 5 , but on my board that tile is empty and 5 appears in other position. Could someone tell me how to fix this?

Thank you :)



// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
class QSignalMapper;
class QLineEdit;
QT_END_NAMESPACE



class SudokuWidget : public QWidget
{
Q_OBJECT;

public:
SudokuWidget( QWidget *parent );


private slots:
// void onMapped( int rowColId );
// void on_pushButton_clicked();

private:
// QSignalMapper *mapper;
QVector < QLineEdit *> tiles;

QVector <QPair <int, int>> random_pos_value;
void random_position(int digits, QVector < QPair <int, int> > &pos_value);

bool is_legal_row(int value, int y, int x, QVector< QVector < QLineEdit* >> &tiles1);
bool is_legal_colum(int value, int y, int x, QVector< QVector < QLineEdit* >> &tiles1);

int box_position(int y, int x);
bool is_legal_box(int value, int y,int x, QVector< QVector < QLineEdit* >> &tiles1);
};


#endif // WIDGET_H

// widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QGridLayout>
#include <QFrame>
#include <QLineEdit>
#include <QIntValidator>
#include <QMessageBox>
#include <QPushButton>
#include <QRandomGenerator>
#include <QDateTime>
#include <QSet>
#include <iostream>


SudokuWidget::SudokuWidget(QWidget * parent)
: QWidget(parent) {

QIntValidator *pValidator = new QIntValidator( this );
pValidator->setRange( 1, 9 );

int square_counter = 0;
random_position(20, random_pos_value);


QGridLayout *mainLayout = new QGridLayout(this);
mainLayout->setSpacing(0);

for (int mr = 0; mr < 3; mr++) {
for(int mc = 0; mc < 3; mc++) {

QFrame *widget = new QFrame;
widget->setFrameStyle(QFrame::Plain);
widget->setFrameShape(QFrame::Box);

QGridLayout *gridLayout = new QGridLayout(widget);
gridLayout->setSpacing(0);
gridLayout->setMargin(0);

for(int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {

QLineEdit *tile = new QLineEdit("");
square_counter++;

tile->setMaxLength(1);
tile->setFixedSize(30,30);
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-style: solid; border-color: black black black black; }");
tile->setAlignment(Qt::AlignCenter);
tile->setFrame(QFrame::Box);
tile->setValidator(pValidator);

tiles.push_back( tile );

gridLayout->addWidget(tile, r, c, 1, 1, Qt::AlignCenter);
}
}
mainLayout->addWidget(widget, mr, mc, 1, 1, Qt::AlignCenter);
}
}
for (int i = 0;i < 80; i++){
for (int j = 0;j < 20; j++){
if (random_pos_value[j].first == i){
tiles[i]->setText(QString::number(random_pos_value[j].second));
tiles[i]->setReadOnly(true);
}
}
}

setLayout(mainLayout);
}


bool SudokuWidget::is_legal_row(int value,int y, int x, QVector< QVector < QLineEdit* >> &tiles1)
{
int count = 0;
for (int i = 0;i < 9; i++){
if (QString::number(value) == tiles1[y][i]->text()){
count++;
}
}
if (count == 1){
return true;
}
return false;
}

bool SudokuWidget::is_legal_colum(int value,int y, int x, QVector<QVector<QLineEdit *> > &tiles1)
{
int count = 0;
for (int i = 0;i < 9; i++){
if (QString::number(value) == tiles1[i][x]->text()){
count++;
}
}
if (count == 1){
return true;
}
return false;
}

void SudokuWidget::random_position(int digits, QVector <QPair <int, int> > &random_pos_value)
{

QVector < QVector < QLineEdit* > > random_boards(81);
for (int y1 = 0;y1 < 9; y1++){
for (int x1 = 0;x1 < 9; x1++){
QLineEdit *line_edit = new QLineEdit("0");
random_boards[y1].push_back(line_edit);
}
}
qsrand(QDateTime::currentMSecsSinceEpoch() / 1000);
int count = 0;
int value;
while (true){
int position = (qrand() % 80) + 0;
int y = position / 9;
int x = position % 9;
if (random_boards[y][x]->text() == "0"){
while (true){
value = (qrand() % 9) + 1;
random_boards[y][x]->setText(QString::number(value));
if (is_legal_box(value, y, x, random_boards) && is_legal_colum(value, y, x, random_boards) ){
break;
}
random_boards[y][x]->setText("0");
}
QPair <int, int> values = {position,value};
random_pos_value.push_back(values);
count++;
if (count == digits){
break;
}
}
}
}


int SudokuWidget::box_position(int y, int x)
{
return 3*(y/3) + (x/3);
}

bool SudokuWidget::is_legal_box(int value, int y1, int x1, QVector<QVector<QLineEdit *> > &tiles1)
{
//box
int count = 0;
int square_position = box_position(y1, x1);
QVector <QString> values;
for (int y = (square_position / 3) * 3; y < ((square_position / 3) * 3) + 3;y++){
for (int x = (square_position % 3) * 3; x < ((square_position % 3) * 3) + 3;x++){
if (tiles1[y][x]->text() == QString::number(value)){
count++;
}
}

}
if (count == 1){
return true;
}
return false;
}

d_stranz
10th April 2020, 17:36
for (int i = 0;i < 80; i++){
for (int j = 0;j < 20; j++){
if (random_pos_value[j].first == i){
tiles[i]->setText(QString::number(random_pos_value[j].second));
tiles[i]->setReadOnly(true);
}
}
}


Think about this loop. The innermost part of it runs 3200 times. Is that what you want to do?

laurynas2003
10th April 2020, 19:46
for (int i = 0;i < 20; i++){
tiles[random_pos_value[i].first]->setText(QString::number(random_pos_value[i].second));
QMessageBox::about(this,"????????", "(" + QString::number(random_pos_value[i].first / 9) + ", " + QString::number(random_pos_value[i].first % 9) + ")" + " = " +
QString::number(random_pos_value[i].second));
tiles[i]->setReadOnly(true);
}


I change it to this code , but still doesnt work properly. With MessageBox i check where the tile value should be.

Added after 59 minutes:



if (is_legal_box(value, y, x, random_boards) && is_legal_colum(value, y, x, random_boards ) && is_legal_row(value, y, x, random_boards) ){ // i forgot to add is_legal_row
break;
}



Ohh I forgot to add is_legal function to random_position function, but still doesn't work . I think my random_position function is fine, because I checked with QMessageBox every coordinate that it gave and wrote it on paper Sudoku board, and that board was fine.

d_stranz
10th April 2020, 23:06
tiles[i]->setReadOnly(true);

So if you have 81 tiles on your board, and you are setting 20 of them to fixed numbers, and you are using the variable "i" to count from 0 to 19, which tiles do you think this code is setting to read-only?

Hint: Why don't you write a little function:



int SudokuWidget::mapRowAndColumnToTileIndex( int row, int column ) const
{
int tileIndex = -1; // ALWAYS initialize; -1 means an invalid row or column was passed

if ( row >= 0 && row < 9 && column >= 0 && column < 9 )
{
// it's a valid row / column location
// add code here to compute index into the tiles array from row and column
}
return tileIndex;
}


and use that function every time you need to get a tile from a row and column. You could even write the reverse mapping function



bool SudokuWidget::mapTileIndexToRowAndColumn( int tileIndex, int & row, int & column ) const
{
bool bValid = false; // set to true if the index is within range
if ( tileIndex >= 0 && tileIndex < tiles.size() )
{
bValid = true;

// add code here to compute row and column from index
}
return bValid;
}


and use it every time you need to go the other way. The code you need to add is in an answer I gave to one of your other posts, if I remember correctly...

laurynas2003
11th April 2020, 10:51
#include "widget.h"
#include "ui_widget.h"

#include <QGridLayout>
#include <QFrame>
#include <QLineEdit>
#include <QIntValidator>
#include <QMessageBox>
#include <QPushButton>
#include <QRandomGenerator>
#include <QDateTime>
#include <QSet>
#include <iostream>
#include <QSignalMapper>



SudokuWidget::SudokuWidget(QWidget * parent)
: QWidget(parent) {

mapper = new QSignalMapper( this );

for (int y = 0;y < 9; y++){
for (int x =0; x < 9; x++){
game_data[y].push_back(0);
}
}

QIntValidator *pValidator = new QIntValidator( this );
pValidator->setRange( 1, 9 );

int square_counter = 0;

// random_position(20, random_pos_value); // QVector <QPair <int, int>


QGridLayout *mainLayout = new QGridLayout(this);
mainLayout->setSpacing(0);
mainLayout->setAlignment(Qt::AlignCenter);


for (int mr = 0; mr < 3; mr++) {
for(int mc = 0; mc < 3; mc++) {

QFrame *widget = new QFrame;
widget->setFrameStyle(QFrame::Plain);
widget->setFrameShape(QFrame::Box); /

QGridLayout *gridLayout = new QGridLayout(widget);
gridLayout->setSpacing(0);
gridLayout->setMargin(0);

for(int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {

QLineEdit *tile = new QLineEdit(this);

int rowColId = (square_counter / 9) + (square_counter % 9) * 10;
mapper->setMapping( tile, rowColId );
connect(tile, &QLineEdit::editingFinished, mapper, &QSignalMapper::map ); // error - no matching member function for call to connect

tile->setMaxLength(1);
tile->setFixedSize(30,30);
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-style: solid; border-color: black black black black; }");
tile->setAlignment(Qt::AlignCenter);
tile->setFrame(QFrame::Box);
tile->setValidator(pValidator);

tiles.push_back( tile );
square_counter++;
gridLayout->addWidget(tile, r, c, 1, 1, Qt::AlignCenter);
}
}
mainLayout->addWidget(widget, mr, mc, 1, 1, Qt::AlignCenter);
}
}
connect( mapper, SIGNAL( mapped( int ) ), this, SLOT( onMapped( int ) ) );
// for (int i = 0;i < 20; i++){

// tiles[random_pos_value[i].first]->setText(QString::number(random_pos_value[i].second));
// QMessageBox::about(this,"????????", "(" + QString::number(random_pos_value[i].first / 9) + ", " + QString::number(random_pos_value[i].first % 9) + ")" + " = " + QString::number(random_pos_value[i].second));

// tiles[random_pos_value[i].first]->setReadOnly(true);
// }

//setLayout(mainLayout);
}

void SudokuWidget::onMapped( int rowColId )
{
int row = rowColId / 10;
int col = rowColId % 10;

QLineEdit * tile = tiles[ row * 9 + col ];

int square_value = tile->text().split(" ")[0].toInt(); // Retrieve the QString text from the QLineEdit, convert it to an int,

// store it in your game data structure, check to see if it is correct,
game_data[col][row] = square_value;
if (is_legal_box(square_value, col, row, game_data) && is_legal_row(square_value, col, game_data) && is_legal_colum(square_value, row, game_data) ){
QMessageBox::about(this, "?", "Its working"); // whatever your game decides to do.
}






}

bool SudokuWidget::is_legal_row(int value, int y, QVector< QVector < int >> &tiles1)
{
int count = 0;
for (int i = 0;i < 9; i++){
if (value == tiles1[y][i]){
count++;
}
}
if (count == 1){
return true;
}
return false;
}

bool SudokuWidget::is_legal_colum(int value, int x, QVector<QVector< int > > &tiles1)
{
int count = 0;
for (int i = 0;i < 9; i++){
if (value == tiles1[i][x]){
count++;
}
}
if (count == 1){
return true;
}
return false;
}

//void SudokuWidget::random_position(int digits, QVector <QPair <int, int> > &random_pos_value)
//{

// QVector < QVector < QLineEdit* > > random_boards(81);
// for (int y1 = 0;y1 < 9; y1++){
// for (int x1 = 0;x1 < 9; x1++){
// QLineEdit *line_edit = new QLineEdit("0");
// random_boards[y1].push_back(line_edit);
// }
// }
// qsrand(QDateTime::currentMSecsSinceEpoch() / 1000);
// int count = 0;
// int value;
// while (true){
// int position = (qrand() % 80) + 0;
// int y = position / 9;
// int x = position % 9;
// if (random_boards[y][x]->text() == "0"){
// while (true){
// value = (qrand() % 9) + 1;
// random_boards[y][x]->setText(QString::number(value));
// if (is_legal_box(value, y, x, random_boards) && is_legal_colum(value, x, random_boards) && is_legal_row(value, y, random_boards)){
// break;
// }
// random_boards[y][x]->setText("0");
// }
// QPair <int, int> values = {position,value};
// random_pos_value.push_back(values);
// count++;
// if (count == digits){
// break;
// }
// }
// }
//}


int SudokuWidget::box_position(int y, int x)
{
return 3*(y/3) + (x/3);
}

bool SudokuWidget::is_legal_box(int value, int y1, int x1, QVector<QVector< int > > &tiles1)
{
//box
int count = 0;
int square_position = box_position(y1, x1);
QVector <QString> values;
for (int y = (square_position / 3) * 3; y < ((square_position / 3) * 3) + 3;y++){
for (int x = (square_position % 3) * 3; x < ((square_position % 3) * 3) + 3;x++){
if (tiles1[y][x] == value){
count++;
}
}

}
if (count == 1){
return true;
}
return false;
}




So i changed it to this code, but now i am getting an error "no matching member function for call to connect"

d_stranz
11th April 2020, 16:57
So i changed it to this code, but now i am getting an error "no matching member function for call to connect"

It is an obscure error, and I have been burned by it more than once. It is because QSignalMapper::map() is an overloaded function, and the compiler cannot determine which one of the two overloads to use.

There are two solutions:

1 - Use the "old style" form of connect:



connect( tile, SIGNAL( editingFinished() ), mapper, SLOT( map() ) );


2 - Use the new style, with a special template which resolves the overload so it can compile:



connect( tile, &QLineEdit::editingFinished, mapper, QOverload<>::of( &QSignalMapper::map ) ); // C++11

// or

connect( tile, &QLineEdit::editingFinished, mapper, qOverload<>( &QSignalMapper::map ) ); // C++14


Even though the new style is more complex, you should use it instead of the old style if you can, because it allows the connect() arguments to be evaluated at -compile- time.

The old style will always compile, even if the signal or slot are not defined at all and it will not be caught until run time. Even then, all that happens is a warning, and the program will run (incorrectly because the connection won't be made between signal and slot). This makes it really hard to debug a program unless you happen to pay attention to all of the startup warnings and messages that are displayed in the Qt Creator or Visual Studio Output window.

laurynas2003
11th April 2020, 17:11
By the way i just figured out why my first code didn't work.

I thought that my square positions were like that:

0 1 2 3 4 5 6 7 8
9 10 11...

but actually it looks like that:

0 1 2 9 10 11 18 19 20....
3 4 5 12 13 14
6 7 8 15 16 17

d_stranz
11th April 2020, 17:42
but actually it looks like that:

Yes, my mistake, too. It is because your code to create the 3 x 3 grid of tiles is inside another two loops to create the 3 x 3 grid of big squares. It would probably be a good idea to change your grid creating code so the the cell indexes are like the first case (0 1 2 3 4 5 6 7 8...). It will make it much easier to design your game logic.

I would create the outer grid of big squares first, in a 3 x 3 loop. Then outside of that, in a 9 x 9 loop, create the tiles and put them in the right position in the big grids.

You actually don't need the big grid at all, I don't think. Just a 9 x 9 grid is all you really need. Grids within grids just makes everything harder.

laurynas2003
11th April 2020, 19:45
I would create the outer grid of big squares first, in a 3 x 3 loop. Then outside of that, in a 9 x 9 loop, create the tiles and put them in the right position in the big grids.

Maybe you could show me how to make it because I can't figure out how to make that without using another gridlayout ?

d_stranz
11th April 2020, 21:49
// SudokuWidget constructor

QGridLayout * pMainLayout( this );

// Make the 3 x 3 array of grid layouts
QVector< QGridLayout * > bigGrids;
for ( int row = 0; row < 3; row++ )
{
for ( int col = 0; col < 3; col++ )
{
QGridLayout * pInnerGrid = new QGridLayout( this );
pMainLayout->addLayout( pInnerGrid, row, col );
bigGrids.push_back( pInnerGrid );
}
}

// Big grid array corresponds to these positions
// 0 1 2
// 3 4 5
// 6 7 8

// Now make the 9 x 9 arrays of tiles
// These will have indexes
// 0 1 2 3 4 5 6 7 8
// 9 10 11 12 13 14 15 16 17
// 18 19 20 21 22 23 24 25 25
// ...
// 72 73 74 75 76 77 78 79 80

// QVector< QLineEdit * > tiles is a member variable
// Resize the tiles array, set all pointers to null
tiles.resize( 81, nullptr );

for ( int row = 0; row < 9; row++ )
{
for ( int col = 0; col < 9; col++ )
{
// index of grid in bigGrids array
int bigGridIndex = 3 * (row / 3 ) + col / 3; // ex: if row = 5 and col = 4, index = 3 + 1 = 4
QGridLayout * pInnerGrid = bigGrids[ bigGridIndex ];

// row, col coordinates of line edit inside of inner grid
int gridRow = row % 3;
int gridCol = col %3;

QLineEdit * tile = new QLineEdit( this );
pInnerGrid->addWidget( tile, gridRow, gridCol );

// index of tile in tiles array
int tileIndex = row * 9 + col;
tiles[ tileIndex ] = tile;
}
}

setLayout( pMainLayout );


Now you have a SudokuWidget with a grid layout inside it. This grid layout has 9 smaller grid layouts inside it.
In each of these 9 smaller grids, there are 9 QLineEdit tiles.

The tiles are stored in a QVector of QLineEdit pointers. The tiles are numbered as a 9 x 9 grid like above (row = 0 .. 8, col = 0 .. 8).

Helpful functions:



QVector< QLineEdit * > SudokuWidget::tilesForColumn( int col )
{
QVector< QLineEdit * > colTiles;
for ( int row = 0; row < 9; row++ )
{
int tileIndex = col + row * 9;
colTiles.push_back( tiles[ tileIndex ] );
}
return colTiles;
}

QVector< QLineEdit * > SudokuWidget::tilesForRow( int row )
{
QVector< QLineEdit * > rowTiles;
for ( int col = 0; col < 9; col++ )
{
int tileIndex = col + row * 9;
rowTiles.push_back( tiles[ tileIndex ] );
}
return rowTiles;
}


Another function you will probably want is tilesForSquare( int row, int col ), which gives you the 9 tiles in the inner grid that contains row and col.

laurynas2003
12th April 2020, 00:04
Could you tell me how to change outer grid of big squares size?

d_stranz
12th April 2020, 16:34
What do you mean, "change the size"? Make the widget bigger or smaller? Change the number of squares inside it?

Show some of your code for how you are creating your SudokuWidget instance and a screenshot of the result.

laurynas2003
12th April 2020, 16:55
// widget.cpp


#include "widget.h"
#include "ui_widget.h"

#include <QGridLayout>
#include <QFrame>
#include <QLineEdit>
#include <QIntValidator>
#include <QMessageBox>
#include <QPushButton>
#include <QRandomGenerator>
#include <QDateTime>
#include <QSet>
#include <iostream>
#include <QSignalMapper>



SudokuWidget::SudokuWidget(QWidget * parent)
: QWidget(parent) {

QIntValidator *pValidator = new QIntValidator( this ); // user can input only 1 - 9
pValidator->setRange( 1, 9 ); // 1 - 9

QGridLayout *pMainLayout = new QGridLayout( this );
pMainLayout->setSpacing(0);
pMainLayout->setAlignment(Qt::AlignCenter);
QRect size = QRect(0,0,5000,1000);
this->setGeometry(size);





// 3 x 3 array of grid layouts
QVector< QGridLayout * > bigGrids;
for ( int y = 0; y < 3; y++ ){
for ( int x = 0; x < 3; x++ ){
QGridLayout *pInnerGrid = new QGridLayout( this );

pInnerGrid->setAlignment(Qt::AlignHCenter);
pInnerGrid->setSpacing(0);
pInnerGrid->setMargin(0);

pMainLayout->addLayout( pInnerGrid, y, x );
bigGrids.push_back( pInnerGrid );
}
}

tiles.resize(81);
solve_data.resize(81);

for (int y = 0; y < 9; y++){
for (int x = 0; x < 9; x++){
int bigGridIndex = box_position( y, x);
QGridLayout *pInnerGrid = bigGrids[bigGridIndex];

// square coordinates in the box
int grid_y = y % 3;
int grid_x = x % 3;

QLineEdit *tile = new QLineEdit( this );

// tile customization
tile->setValidator(pValidator); // input 1 - 9
tile->setMaxLength(1);
tile->setFixedSize(30,30);
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-style: solid; border-color: gray gray gray gray; }");
tile->setAlignment(Qt::AlignCenter);
tile->setFrame(QFrame::Box);

if (y == 0){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-top: 5px; border-style: solid; border-color: black gray gray gray; }");
}

if (x == 8){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-right: 5px; border-style: solid; border-color: gray black gray gray; }");
}
if (y == 8){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-bottom: 5px; border-style: solid; border-color: gray gray black gray; }");
}
if (x == 0){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-left: 5px; border-style: solid; border-color: gray gray gray black; }");
}
if (x == 0 && y == 0){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-top: 5px; border-left: 5px; border-style: solid; border-color: black gray gray black; }");
}
if (x == 8 && y == 0){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-top: 5px; border-right: 5px; border-style: solid; border-color: black black gray gray; }");
}
if (x == 8 && y == 8){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-bottom: 5px; border-right: 5px; border-style: solid; border-color: gray black black black; }");
}
if (x == 0 && y == 8){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-bottom: 5px; border-left: 5px; border-style: solid; border-color: gray gray black black; }");
}
if (x == 5 || x == 2){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-right: 5px; border-style: solid; border-color: gray black gray gray; }");
}
if (y == 2 || y == 5){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-bottom: 5px; border-style: solid; border-color: gray gray black gray; }");
}
if (x == 2 && y == 8){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-bottom: 5px; border-right: 5px; border-style: solid; border-color: gray black black gray; }");
}
if (x == 5 && y == 8){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-bottom: 5px; border-right: 5px; border-style: solid; border-color: gray black black gray; }");
}
if ( ( x == 2 || x == 5 || x == 8 ) && y == 5){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-bottom: 5px; border-right: 5px; border-style: solid; border-color: gray black black gray; }");
}
if ( ( x == 2 || x == 5 || x == 8 ) && y == 2){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-bottom: 5px; border-right: 5px; border-style: solid; border-color: gray black black gray; }");
}
if (x == 0 && (y == 2 || y == 5)){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-bottom: 5px; border-left: 5px; border-style: solid; border-color: gray gray black black; }");
}
if ( y == 0 && ( x == 2 || x == 5 ) ){
tile->setStyleSheet("QLineEdit{ border-width: 1.5px; border-top: 5px; border-right: 5px; border-style: solid; border-color: black black gray gray; }");
}



pInnerGrid->addWidget( tile, grid_y, grid_x);

// inedex of tile in tiles array
int tileIndex = y * 9 + x;
tiles [tileIndex] = tile;
}
}

// Generate random digits with solvable solution ( 20 digs )
random_position(20, random_pos_value);
for (int i = 0;i < 20; i++){
tiles[random_pos_value[i].first]->setText(QString::number(random_pos_value[i].second));
tiles[random_pos_value[i].first]->setReadOnly(true);
}
// solve button
Solve = new QPushButton( "Solve", this);
connect( Solve, SIGNAL(released()), this, SLOT(on_Solve_clicked()) );
pMainLayout->addWidget(Solve, 5, 0);

setLayout( pMainLayout );
}
// .......


I want to make my whole widget bigger, it is very small if you compare it to the window, and i attached Screenshot to see how it looks

d_stranz
12th April 2020, 17:32
It looks like you are putting this SudokuWidget inside of a QMainWindow class. Are you doing something like this?



MainWindow::MainWindow( QWidget * parent )
: QMainWindow( parent )
{

// setup ui and all that

SudokuWidget * pSudokuWidget = new SudokuWidget( this );

setCentralWidget( pSudokuWidget );
}


Making your widget the central widget of the main window means that the main window will take control of the size and will grow or shrink the central widget to match the size of the main window's central area.

What might happen is that your QLineEdit tiles might not grow as big as the grid cells they are in. The fonts won't grow either, and you probably want both to happen.

So now you will need to implement resizeEvent() for your SudokuWidget class. the sizeEvent() will give you the new size that the main window will set. You can take that size, divide by 9, and that will give you a rough estimate for the largest size font height in pixels that will fit in a square. You can then use QFont and maybe QFontMetrics to create a new QFont that is as close as possible to the desired height. Set that font on the SudokuWidget instance and the line edits should inherit it (and hopefully resize themselves to fit)

Another complication is that if you make these changes constantly while the user is resizing the main window, it could lead to ugly flashing as the widget constantly adjusts the fonts. In this case, what I usually do is use a QTimer and a resize() slot to control how often the UI gets updated.

Make the QTimer and slot members of your SudokuWidget class. Connect its timeout() signal to your resize() slot. When you first receive a resizeEvent(), start (or restart) it with a timeout of maybe 250 ms. Then, 250 ms after the last resizeEvent(), the timer will fire, and you will enter the resize() slot. Here is where you do the font adjustment to make a larger or smaller font. If 250 ms seems too sluggish, make it smaller until you get a good balance between excessive flashiness and slow response to resizing.



void SudokuWidget::resizeEvent( QResizeEvent * pEvent )
{
// Start (or restart) the timer for another mResizeTimeout waiting period
mpTimer->start( mResizeTimeout ); // Another member variable to make it easy the adjust the response time
}

void SudokuWidget::resize() // slot
{
QSize newSize = size();

// Divide by 9, subtract some for padding, create a new QFont (on the stack) with this new size and face, then use setFont() to install it
}

laurynas2003
12th April 2020, 18:11
It looks like you are putting this SudokuWidget inside of a QMainWindow class. Are you doing something like this?


No, I don't have QMainWindow class, I just have one header file, main.cpp file and widget.cpp

My widget.h and main.cpp code:




// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
class QSignalMapper;
class QLineEdit;
class QPushButton;
QT_END_NAMESPACE




class SudokuWidget : public QWidget
{
Q_OBJECT;

public:
SudokuWidget( QWidget * parent = nullptr );
// ~SudokuWidget();

private slots:
// void onMapped( int rowColId );
void on_Solve_clicked();

private:

// QSignalMapper *mapper;
QVector < QLineEdit *> tiles;
// QVector <QVector < int > > game_data;

QPushButton *Solve;
QVector <QVector < QLineEdit* > > solve_data;
bool Solve_Board( QVector< QVector < QLineEdit* > > &board );
bool empty_cell( QVector< QVector < QLineEdit* > >, int &y, int &x );


QVector <QPair <int, int>> random_pos_value;
void random_position(int digits, QVector < QPair <int, int> > &pos_value);

// int mapRowAndColumnToTileIndex( int row, int column ) const;
//bool mapTileIndexToRowAndColumn( int tileIndex, int & row, int & column ) const;


bool is_legal_row(int value, int y, QVector< QVector < QLineEdit* > > &tiles1);
bool is_legal_colum(int value, int x, QVector< QVector < QLineEdit* > > &tiles1);

int box_position(int y, int x);
bool is_legal_box(int value, int y,int x, QVector <QVector < QLineEdit* > > &tiles1);
};


#endif // WIDGET_H




// main.cpp

#include "widget.h"
#include <QApplication>


int main(int argc, char * argv[])
{
QApplication a(argc,argv);
SudokuWidget *sudokuwidged = new SudokuWidget(nullptr);
sudokuwidged->show();
return a.exec();
}

d_stranz
12th April 2020, 18:21
Then I would suggest you add a MainWindow class as your main top-level widget. It isn't hard - just look at the Qt Application example (https://doc.qt.io/qt-5/qtwidgets-mainwindows-application-example.html) but ignore almost everything in it for now. All you need is the header file that derives MainWindow from QMainWindow and the code in the constructor that creates your SudokuWidget and makes it the central widget (like I showed above). When you are ready to start using menus, toolbars, and statusbars, then you can take a look at some of the other parts of the examples.

In you main() function, you just substitute MainWindow for SudokuWidget and you are good to go, resizing and all.

laurynas2003
12th April 2020, 22:40
So now you will need to implement resizeEvent() for your SudokuWidget class. the sizeEvent() will give you the new size that the main window will set. You can take that size, divide by 9, and that will give you a rough estimate for the largest size font height in pixels that will fit in a square. You can then use QFont and maybe QFontMetrics to create a new QFont that is as close as possible to the desired height. Set that font on the SudokuWidget instance and the line edits should inherit it (and hopefully resize themselves to fit)

Another complication is that if you make these changes constantly while the user is resizing the main window, it could lead to ugly flashing as the widget constantly adjusts the fonts. In this case, what I usually do is use a QTimer and a resize() slot to control how often the UI gets updated.

Make the QTimer and slot members of your SudokuWidget class. Connect its timeout() signal to your resize() slot. When you first receive a resizeEvent(), start (or restart) it with a timeout of maybe 250 ms. Then, 250 ms after the last resizeEvent(), the timer will fire, and you will enter the resize() slot. Here is where you do the font adjustment to make a larger or smaller font. If 250 ms seems too sluggish, make it smaller until you get a good balance between excessive flashiness and slow response to resizing.

Isn't there a simpler way to make it bigger? It looks very complicated to do this way

d_stranz
12th April 2020, 23:26
You do not need to do any of this. All you need to do is make the SudokuWidget the central widget of a MainWindow and the MainWindow class will automatically resize its SudokuWidget child window when the MainWindow is resized.

But like I said, resizing will not change anything except the size of the grid. The font will not get bigger, and the QLineEdits might enlarge to their maximum preferred height and stop growing. So you probably won't be happy with the result.

The simplest thing is just to implement resizeEvent() for the SudokuWidget, and all you need to do there is to calculate a new font size and set that. The QLineEdits should inherit it and grow to accommodate.

The suggestion of the QTimer is only if you don't like the way the resizing looks while you are doing it. If the UI flashing with a repaint ever time your mouse poves doesn't bother you, then leave the resize event simple and just change the font.

laurynas2003
13th April 2020, 08:14
But maybe I can make that you couldn't change mainwindow size , and set it that it would showfullscreen, and make widget bigger and make that user couldn't change mainwindow size, so there wouldn't be resizing problems.