Application stops responding when using QMap to store objects - c++

A friend of mine and I are trying to make a game in C++ using Qt. We want to store a few QGraphicsTextItem in a QMap to access them during runtime. I've pasted the relevant parts of our code here, and our problem is that the program stops responding.
Game.cpp
int players = 6;
QGraphicsRectItem * overviewBox = new QGraphicsRectItem();
overviewBox->setRect(0, 0, 782, 686);
scene->addItem(overviewBox);
for(int i = 1; i <= players; i++) {
Container * ovContainer = new Container(overviewBox);
ovContainer->Overview(i, faceNo);
ovContainer->setPos(0, 0 + 110 * (i - 1));
info->textBoxMap[i-1] = ovContainer->textBox->playerText; // Program stops responding here
}
GameInfo.h
#ifndef GAMEINFO_H
#define GAMEINFO_H
#include "TextBox.h"
#include <QMap>
class GameInfo {
public:
GameInfo();
QMap<int, QGraphicsTextItem *> textBoxMap;
};
#endif // GAMEINFO_H
None of us have much experience using C++ or Qt, and we would appreciate any help.

Unless you are missing some code in your code snippet, then your QMap is not being used correctly. I think you have not allocated (inserted) any QMap items yet? - therefore you are accessing an element that is out of range (i.e. does not exist yet).
To add items into the QMap you can use insert(), like this (taken from Qt page):
QMap<int, QString> map;
map.insert(1, "one");
map.insert(5, "five");
map.insert(10, "ten");
Then to read your values back out:
QString str = map[1];
//or
QString str2 = map.value(5);
You don't need to iterate using a for loop but for your code you could do:
for(int i = 1; i <= players; i++)
{
:
:
info->textBoxMap.insert(i, ovContainer->textBox->playerText);
}
note
If you want to insert items with the same key you will need to use insertMulti(...), otherwise you will just overwrite the value of the key, e.g.:
QMap<int, QString> map;
map.insert(1, "test1");
map.insert(1, "test2");
Here, map[1] will return "test2". But I don't think this is what you want since your players are all going to be unique indexes I assume... but its worth pointing out that insert() with the same index just over-writes the value.

Related

C++ Read Access Violation error was thrown, but I'm not sure why. Tile Slider Puzzle Game

So I'm coding an object oriented Tile Slider Puzzle game, and I feel as though I've coded things correctly, and when I build the project, no errors are thrown. However, when I go to run my code (Visual Studio 2015 IDE) I get a message box saying the .exe file has stopped working. Here are my files thus far:
The following is the TileSlider.h file:
#ifndef TILESLIDER_H
#define TILESLIDER_H
#include <Windows.h>
class TileSlider
{
private:
char** solvedBoard;
char** gameBoard;
//mutator(s)
void setUpBoards(); //keep or copy code to constructor
//other member functions
void printBoard(const HANDLE &consoleOut) const;
void scrambleBoard();
bool isBoardSolved() const;
void makeMove(int move);
public:
TileSlider(); //allocate mem here? maybe call setUpBoards()
~TileSlider(); //deallocate mem here
void playGame();
};
#endif
The following is the TileSlider.cpp file:
#include "TileSlider.h"
using namespace std;
#define SIZE 3 //num of rows and cols to board
// --------------------------------------
// Private Members
// --------------------------------------
// Mutator(s)
void TileSlider::setUpBoards() {
//allocate memory for boards
char** solvedBoard = new char*[SIZE];
char** gameBoard = new char*[SIZE];
for (int i = 0; i < SIZE; i++) {
solvedBoard[i] = new char[SIZE];
gameBoard[i] = new char[SIZE];
}
//fill the boards
char i = 49; // ASCII code for '1'
for (int row = 0; row < SIZE; row++) {
for (int column = 0; column < SIZE; column++) {
gameBoard[row][column] = i;
solvedBoard[row][column] = i;
i++;
}
}
gameBoard[SIZE - 1][SIZE - 1] = 42; // ASCII for '*'
solvedBoard[SIZE - 1][SIZE - 1] = 42;
}
The following is the driver file for my code (TileSliderGame.cpp):
#include "TileSlider.h"
using namespace std;
int main() {
TileSlider* game = new TileSlider();
game->playGame();
delete game;
return 0;
}
To attempt to determine the issue that was occurring, I put a break point to step into where I'm calling playGame() in the driver file (TileSliderGame.cpp). I stepped into that function, and then stepped into where playGame() calls the printBoard(consoleOut) function and I received a Read Access Violation error when I got to this line:
// Other Private Member Functions
void TileSlider::printBoard(const HANDLE &consoleOut) const {
for (int row = 0; row < SIZE; row++) {
for (int column = 0; column < SIZE; column++) {
if (gameBoard[row][column] == 42) { //ASCII code 42 is '*' asterisk
. . .
(The error was thrown at the last line shown above)
Error message:
Exception thrown: read access violation.
this->gameBoard was 0x1110112.
Now, I'm really not sure why I would get a read access violation error within the printBoard() function because it's a private function, and therefore should be able to directly access the private gameBoard variable inside the class. I even tried to see if it would make a difference to create an accessor for the gameBoard, but it didn't (the same error was thrown).
Another note I'd like to make, I started this code in a separate project with an Imperative program design and have got it running as I intend it to. Therefore, I know that the code within my object oriented program regarding how the TileSlider game works is working perfectly fine. I'm just not sure what I may have done wrong when I redesigned the code into an object oriented design.
If what my game is supposed to look like is confusing, the TileSlider gameBoard is a 3x3 2D character array that displays onto the screen like so :
1 2 3
4 5 6
7 8 *
Above is how the gameBoard starts, it is then scrambled and the user then moves the tiles with the "wasd" keys to attempt to win the game. Any tiles moved into their correct position (the positions showed above) are colored green, and any tiles that aren't in their correct position are colored red. The one exception is that the empty tile (the asterisk) is printed in white all of the time.
I don't think that my code is perfect so I'll take any constructive criticism on my code and code design that I can get.
Edit: I removed a large portion of my TileSlider.cpp file code shown above because it was irrelevant to the error I made in my code.
You wrote:
char** solvedBoard = new char*[SIZE];
char** gameBoard = new char*[SIZE];
You probably meant:
solvedBoard = new char*[SIZE];
gameBoard = new char*[SIZE];
The reason is that your declaration of solvedBoard and gameBoard in TileSlider::setUpBoards() effectively hides the TileSlider member variables with the same names, and nothing is assigned to the latter.

I'm using qt to design a program and need help because my code is not DRY [duplicate]

I have a gui form, where multiple text boxes are present. I want to put their values inside an array. One way of doing it is by writing something like this
{array element } = ui->text_1->text();
and repeat it for text_2,text_3 upto n.
What I want is to run a loop and replace number portion of text box name in each cycle.
something like this {array element } = ui->text_{This number getting changed }->text();
How can it be done in qt?
There are two ways of doing this.
When you create the UI, instead of using text1, text2, etc. you create an array of QLineEdits (eg. std::vector<QLineEdit>) and then when you want to retrieve their values then simply iterate over this array
Iterate over the children of the container widget. You can get the list of the children using the following (documentation):
QList<QObject *> list = parentWidget->children();
Another option to those listed would be to create an array using an initializer list. Depending on how big the array is (and how often it changes), this might be workable.
QLineEdit* entries[] = { ui->text_0, ui->text_1, ui=>text_2 };
QStringList answers;
for ( int i = 0; i < 3; ++i )
{
answers += entries[i]->text();
}
here is an expanded version of Matyas' solution:
class MyClass : public QWidget
{
QStringList answers;
void FillAnswersList(QObject *base)
{
QLineEdit *lineEdit = qobject_cast<QLineEdit>(base);
if(lineEdit)answers.append(lineEdit->text());
else
{
foreach(QObject *child, base->children())
FillAnswersList(child);
}
}
};
If it is just the number changing, and always incrementing, there is another possible solution using QObject::findChild, which takes a name as a parameter.
QString name_template("text_%1");
QStringList answers;
for(int i = 0; i < MAX_TEXTS; ++i)
{
QLineEdit *edit = ui->findChild<QLineEdit *>(name_template.arg(i));
answers += edit->text();
}

Generate random coordinates in a rectangle qt

I have a rectangle with length 27.5 and width 3.5 and I am supposed to generate coordinates within this rectangle which are unique i.e., without any duplicates. The number of coordinates that need to be generated are based on the size of a list. Here is what I have done so far using Qt:
struct coordinates_t{
int x;
int y;
};
QVector<coordinates_t> coordinatesList;
QList<QString> listOfDevices;
//populate listOfDevices
for( int i = 0; i < listOfDevices.count(); i++)
{
coordinatesList.pushback({rand() % 51 + (-25), rand() % 11 + (-5)});
}
The problem now is that even though the rand function generates random numbers within the rectangle, it does not however avoid duplicates. Is there someway in which I can avoid the duplicates and produce unique coordinates within the given rectangle.
The normal way of solving this would be to shuffle a predefined set of coordinates. But this isn't practical in this instance. Furthermore, duplicates on one axis are allowed too.
Consider solving this by
Defining bool operator<(const coordinates_t& other) const and bool operator==(const coordinates_t& other) const
Use a std::set<coordinates_t>. This is possible once you've done (1).
Insert random coordinates into the set until the desired size of the set is reached.
Copy that set into the QVector if you still need them to be stored that way. If you want to preserve the insertion order (set will sort the values according to how you define the operators in (1)), then retain the QVector, and only push a point to it if insertion into the set was successful.
Insertion of a duplicate into a set is an O(Log N) no-op. Also note that rand() as a linear congruential generator might throw out undesirable autocorrelation effects that manifest themselves particularly in an x, y plot. Consider using Mersenne Twister instead.
Here is an example that demonstrates the check for duplicates using QVector and QPoint. If you want to use QVector::contains with your own struct, you must implement the operator==().
#include <QCoreApplication>
#include <QVector>
#include <QPoint>
#include <QDateTime>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QVector<QPoint> point_vector;
qsrand(QDateTime::currentMSecsSinceEpoch());
const int max_points = 20;
while(point_vector.size() < max_points)
{
QPoint point(qrand() % 27, qrand() % 4);
if(!point_vector.contains(point))
point_vector.append(point);
}
qDebug() << point_vector;
return a.exec();
}
Just for completeness sake. :)
In my comment I suggested using a QMap but I had a std::set in mind, which in Qt does not have an equivalent. :(
Anyway, I wanted to see if a QMap could do the trick as well and came up with an ... a little bit strange approach.
Putting a QPoint into the QMap does not work since the inserted key needs to have the < operator implemented, which is not the case for QPoint but it for QPair. Thus my choice.
The idea is to (a bit like in thuga's answer) compare the size() of the resulting list to the list of devices and insert until they are equally long. However, since I bound myself to using QMap, here's my version. :)
#include <ctime>
#include <QMap>
#include <QDebug>
#include <QPair>
int main()
{
qsrand(std::time(nullptr));
QMap<QPair<int, int>, QString> coords;
QList<QString> listOfDevices;
//populate listOfDevices
for (int i = 10; i < 30; ++i) {
listOfDevices.append("dev" + QString::number(i));
}
qDebug() << "Number of devices:" << listOfDevices.size();
int demo_counter = 0;
while (coords.size() < listOfDevices.size()) {
coords.insert(
qMakePair(qrand() % 51 + (-25), qrand() % 11 + (-5)),
listOfDevices.at(coords.size())
);
++demo_counter;
}
for (auto it = coords.cbegin(); it != coords.cend(); ++it) {
qDebug() << it.key() << ':' << it.value();
}
qDebug() << "Entries inserted:" << coords.size();
qDebug() << "Tries needed:" << demo_counter;
return 0;
}
This outputs (see how it tried 23 times for adding 20 entries):
Number of devices: 20
QPair(-25,-2) : "dev14"
QPair(-23,3) : "dev28"
QPair(-22,5) : "dev20"
QPair(-20,5) : "dev11"
QPair(-19,-1) : "dev22"
QPair(-19,0) : "dev27"
QPair(0,-2) : "dev29"
QPair(5,-3) : "dev23"
QPair(9,0) : "dev28"
QPair(10,1) : "dev17"
QPair(11,-5) : "dev18"
QPair(11,4) : "dev25"
QPair(16,-2) : "dev12"
QPair(16,4) : "dev26"
QPair(21,-2) : "dev26"
QPair(21,2) : "dev16"
QPair(21,4) : "dev15"
QPair(23,-2) : "dev24"
QPair(25,2) : "dev21"
QPair(25,4) : "dev23"
Entries inserted: 20
Tries needed: 23
Since your requirement is not exactly clear and you might be able to fill in the devices within the while loop as well, there is no way to tell, which solution is better suited but if you really want two different lists, one with devices and one with coords, the solution with the contains() check is to be preferred.

How to iterate QAbstractListModel?

I have a requirement where I have QAbstractListModel which is being updated continuously. The data type of QAbstractListModel is integar type.
I would like to copy the whole data at particular intervals into the vector so that vector is updated continuously and I can use it further.
Any idea how can I iterate QAbstractListModel by its index and copy it into vector.
Quick and dirty way of doing it :
QAbstractListModel m;
QVector<int> v;
const int nbRow = m.rowCount();
v.reserve(nbRow);
for (int i = 0; i < nbRow; ++i)
{
int myInt = m.index(i, 0).data().toInt();
v.append(myInt);
}

How to run a loop using gui object names in qt?

I have a gui form, where multiple text boxes are present. I want to put their values inside an array. One way of doing it is by writing something like this
{array element } = ui->text_1->text();
and repeat it for text_2,text_3 upto n.
What I want is to run a loop and replace number portion of text box name in each cycle.
something like this {array element } = ui->text_{This number getting changed }->text();
How can it be done in qt?
There are two ways of doing this.
When you create the UI, instead of using text1, text2, etc. you create an array of QLineEdits (eg. std::vector<QLineEdit>) and then when you want to retrieve their values then simply iterate over this array
Iterate over the children of the container widget. You can get the list of the children using the following (documentation):
QList<QObject *> list = parentWidget->children();
Another option to those listed would be to create an array using an initializer list. Depending on how big the array is (and how often it changes), this might be workable.
QLineEdit* entries[] = { ui->text_0, ui->text_1, ui=>text_2 };
QStringList answers;
for ( int i = 0; i < 3; ++i )
{
answers += entries[i]->text();
}
here is an expanded version of Matyas' solution:
class MyClass : public QWidget
{
QStringList answers;
void FillAnswersList(QObject *base)
{
QLineEdit *lineEdit = qobject_cast<QLineEdit>(base);
if(lineEdit)answers.append(lineEdit->text());
else
{
foreach(QObject *child, base->children())
FillAnswersList(child);
}
}
};
If it is just the number changing, and always incrementing, there is another possible solution using QObject::findChild, which takes a name as a parameter.
QString name_template("text_%1");
QStringList answers;
for(int i = 0; i < MAX_TEXTS; ++i)
{
QLineEdit *edit = ui->findChild<QLineEdit *>(name_template.arg(i));
answers += edit->text();
}