How to access a variable from another class in Qt? - c++

I am trying to implement in Qt a main window which has 2 widgets: one area where I draw some points and one list box where I write all the points with their respective coordinates. And I would like to implement the function "delete point" of a button on the main window, i.e. when I press the button then the point selected from the list box should disappear from my area where I am drawing. So I was thinking of doing this with signals/slots, but when I try to gain access to my list of points from my drawing area it just doesn't find any containing data. This is my code until now:
paintwidget.cpp (my main window):
PaintWidget::PaintWidget(QWidget parent) :
QWidget(parent),
ui(new Ui::PaintWidget)
{
area = new RenderArea(this);
ui->setupUi(this);
connect(ui->displayWidget, SIGNAL(listUpdated(QList)), ui->pointsListWidget,
SLOT(onListUpdated(QList*)));
connect(ui->deletePoints, SIGNAL(clicked()), this, SLOT(deleteItem()));
}
void PaintWidget::deleteItem()
{
area->deletePoint(ui->pointsListWidget->currentItem());
}
renderarea.cpp (my drawing area):
void RenderArea::mousePressEvent(QMouseEvent *e)
{
point = e->pos();
updateList(point);
this->update();
}
void RenderArea::updateList(const QPoint& p)
{
list.append(p);
if (list.count()>1)
lineAdded(p);
emit listUpdated(&list);
}
void RenderArea::paintEvent(QPaintEvent * /* event */)
{
QPainter painter(this);
painter.setPen(QPen(Qt::black,2));
for (int i = 0; i < list.size(); ++i)
painter.drawPoint(list[i]);
if (list.size()>1)
for(int j = 0; j < list.size()-1; ++j)
painter.drawLine(list[j], list[j+1]);
}
void RenderArea::deletePoint(QListWidgetItem *item)
{
bool ok1;
bool ok2;
int index = item->text().indexOf(",");
int x = item->text().left(index).toInt(&ok1, 10);
int y = item->text().mid(index + 1).toInt(&ok2, 10);
for (int i = 0; i < list.size(); ++i)
//find the point with x and y as coordinates and delete it
}
listbox.cpp:
void ListBox::onListUpdated(QList *list)
{
clear();
for (int i = 0; i < list->size(); ++i)
addItem(new QListWidgetItem(QString::number(list->at(i).x()) + ", " +
QString::number(list->at(i).y())));
}
The list from the render area is a QList of QPoints. The problem is that in the FOR-loop the size of the list is 0 so I cannot see any of the points that it should contain. I think that I am failing to initialize it somewhere but I am not sure where.
The points are drawn with QPainter so when I delete the point from the list is there any possibility to delete them from my drawing area also?

I'm suspecting you've got two RenderArea widgets hanging around for some reason.
You're connecting ui->displayWidget's signal, but acting on the area widget for the delete.
Shouldn't you be calling ui->displayWidget->deletePoint or connecting area's signal?
As for the repaint, you should call the widget's update() method to have it repaint itself.

Related

QChartView and QScatterSeries overrdide the label of a QPointF

I have a QChartView which displays some 2D points which are representing each one a specific project I want to label each point with the project name AND NOT with it's x,y coordinates as the default behaviour
Is there any way to achieve override the function that creates or render the labels?
Why this could be difficult to achieve without changing the Qt source code
QXYSeries::setPointLabelsFormat wouldn't be of much help to you. It does indeed allow you to change the format of the labels, but the only variable part of it are the coordinates of the points.
All the drawing is done in the private part of the Qt classes. Here is the whole story:
The labels are drawn in the private part of QXYSeries (painter->drawText(position, pointLabel);):
void QXYSeriesPrivate::drawSeriesPointLabels(QPainter *painter, const QVector<QPointF> &points,
const int offset)
{
if (points.size() == 0)
return;
static const QString xPointTag(QLatin1String("#xPoint"));
static const QString yPointTag(QLatin1String("#yPoint"));
const int labelOffset = offset + 2;
painter->setFont(m_pointLabelsFont);
painter->setPen(QPen(m_pointLabelsColor));
QFontMetrics fm(painter->font());
// m_points is used for the label here as it has the series point information
// points variable passed is used for positioning because it has the coordinates
const int pointCount = qMin(points.size(), m_points.size());
for (int i(0); i < pointCount; i++) {
QString pointLabel = m_pointLabelsFormat;
pointLabel.replace(xPointTag, presenter()->numberToString(m_points.at(i).x()));
pointLabel.replace(yPointTag, presenter()->numberToString(m_points.at(i).y()));
// Position text in relation to the point
int pointLabelWidth = fm.width(pointLabel);
QPointF position(points.at(i));
position.setX(position.x() - pointLabelWidth / 2);
position.setY(position.y() - labelOffset);
painter->drawText(position, pointLabel);
}
}
drawSeriesPointLabels is called from the paint method of ScatterChartItem (this class is not included in the official documentation):
void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
if (m_series->useOpenGL())
return;
QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
painter->save();
painter->setClipRect(clipRect);
if (m_pointLabelsVisible) {
if (m_pointLabelsClipping)
painter->setClipping(true);
else
painter->setClipping(false);
m_series->d_func()->drawSeriesPointLabels(painter, m_points,
m_series->markerSize() / 2
+ m_series->pen().width());
}
painter->restore();
}
The ScatterChartItem in turn is created in the private part of QScatterSeries and can't be substituted with a custom class:
void QScatterSeriesPrivate::initializeGraphics(QGraphicsItem* parent)
{
Q_Q(QScatterSeries);
ScatterChartItem *scatter = new ScatterChartItem(q,parent);
m_item.reset(scatter);
QAbstractSeriesPrivate::initializeGraphics(parent);
}
What you might wanna try
Hide the original labels with setPointLabelsVisible(false); The labels will be drawn separately afterwards.
Subclass QChartView and reimplement the paintEvent, invoking first QChartView::paintEvent and then calling a custom function (lets say drawCustomLabels), which is a modified version of QXYSeriesPrivate::drawSeriesPointLabels. By calling drawCustomLabels pass:
a local painter drawing on the vieport of MyChartView,
the points as returned by QXYSeries::points,
the desired offset.
Here is an example of how the drawCustomLabels might look like:
void MyChartView::drawCustomLabels(QPainter *painter, const QVector<QPointF> &points, const int offset)
{
if (points.count() == 0)
return;
QFontMetrics fm(painter->font());
const int labelOffset = offset + 2;
painter->setFont(m_pointLabelsFont); // Use QXYSeries::pointLabelsFont() to access m_pointLabelsFont
painter->setPen(QPen(m_pointLabelsColor)); // Use QXYSeries::pointLabelsColor() to access m_pointLabelsColor
for (int n(0); n < points.count(); n++) {
QString pointLabel = "..."; // Set the desired label for the n-th point of the series
// Position text in relation to the point
int pointLabelWidth = fm.width(pointLabel);
QPointF position(points.at(n));
position.setX(position.x() - pointLabelWidth / 2);
position.setY(position.y() - labelOffset);
painter->drawText(position, pointLabel);
}
}

QTableWidget Checkbox get state and location

How to get state of all checkbox and get row and column of checked?
Onclick PushButton function.
QTableWidget *t = ui->tableWidget;
t->setRowCount(2);
t->setColumnCount(2);
QStringList tableHeader;
tableHeader<<"item01"<<"item02";
t->setHorizontalHeaderLabels(tableHeader);
for (int i = 0; i < t->rowCount(); i++) {
for (int j = 0; j < t->columnCount(); j++) {
QWidget *pWidget = new QWidget();
QHBoxLayout *pLayout = new QHBoxLayout(pWidget);
QCheckBox *pCheckBox = new QCheckBox();
pLayout->setAlignment(Qt::AlignCenter);
pLayout->setContentsMargins(0,0,0,0);
pLayout->addWidget(pCheckBox);
pWidget->setLayout(pLayout);
t->setCellWidget(i, j, pWidget);
}
}
And when I clicked the button, I need get all selected elements with rows, columns of each.
void Widget::on_pushButton_clicked()
{
// Code here
// For example: Selected ["item01", 2]
}
I simply iterate over all cell widgets:
for (int i = 0; i < t->rowCount(); i++) {
for (int j = 0; j < t->columnCount(); j++) {
QWidget *pWidget = t->cellWidget(i, j);
QCheckBox *checkbox = pWidget->findChild<QCheckBox *>();
if (checkbox && checkbox->isChecked())
qDebug() << t->horizontalHeaderItem(j)->text() << i;
}
}
It's been awhile since I've programmed with Qt, but I believe there is not really good way of doing this. I've done all of these solutions with success.
1) Iterate through all the cell widgets like svlasov's answer says. This has some scalability issues.
2) Create hash maps where the pointer to the button is the key and the indices you want are the values. You can get which button was clicked with QObject::sender().
3) Store the indices you want as properties on the button when you create the buttons (see setProperty() in QObject's documentation). For example,
button->setProperty("x index", x);
In your slot, use QObject::sender() to get the pointer to the button and then call
button->property("x");
I've generally found the third option to be the cleanest and best performing.
Note that these answers also work for QTreeWidgets and QListWidgets.

stop event propagation from QGraphicsItem to QGraphicsScene QT

I have an Qgraphicsscene implemented as a class, then i use QGraphicsScene::mousePressEvent to add a QGraphicsRectItem, this item have also an implementation for QGraphicsRectItem::mousePressEvent, the problem is that the event in the rect item is propagated to the scene and when i click it is added a new rect item, but i want is that the events inside this item do not propagate to the scene, i try event->accept but the event is propagated, how i cant do that? thanks for any help.
here is my qgraphicsscene code:
#include "imageview.h"
ImageView::ImageView(QWidget *parent){
scene = new ImageScene(this);
setScene(scene);
//this->setMouseTracking(true);
this->setInteractive(true);
}
ImageScene::ImageScene(QWidget *parent){
current = NULL;
selection = new QRubberBand(QRubberBand::Rectangle,parent);
selection->setGeometry(QRect(10,10,20,20));
setSceneRect(0,0,500,500);
}
void ImageScene::mousePressEvent(QGraphicsSceneMouseEvent *event){
QGraphicsScene::mousePressEvent(event);
/*IGNORING THIS EVENT FROM QGRAPHICSRECTITEM*/
cout<<"image view"<<endl;
if(this->selectedItems().length() == 0){ /*WORKS BUT IN SOME IMPLEMENTATION IS A PROBLEM (WHEN I DELETE THE ITEM WITH A DELETE BUTTON THE EVENT IS FIRED AND ADD A NEW ITEM .)*/
origin = event->scenePos();
selection->show();
}
}
void ImageScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
if(selection->isVisible() && selection->rect().width() >= 20 && selection->rect().height() >= 20){
QGraphicsScene::mouseReleaseEvent(event);
ResizableRect * rselection = new ResizableRect();
//selection->origin = event->scenePos();
//selection->grabMouse();
cout<<"add"<<endl;
this->addItem(rselection);
rselection->setPos(selection->pos());
rselection->setRect(0,0,selection->rect().width(),selection->rect().height());
}
selection->hide();
}
void ImageScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
QGraphicsScene::mouseMoveEvent(event);
if(selection->isVisible()){
QPoint rorigin(origin.x(),origin.y());
int xdes = event->scenePos().x();
int ydes = event->scenePos().y();
xdes = xdes > 0? xdes:0;
ydes = ydes > 0? ydes:0;
xdes = xdes < this->width()?xdes:this->width();
ydes = ydes < this->height()?ydes:this->height();
QPoint rdest(xdes,ydes);
selection->setGeometry(QRect(rorigin,rdest).normalized());
}
}
Opposite to QWidgets, QGraphicsScene catches events before child items. It is described on Qt documentation.
For correctly work with that, use reimplementation of QGraphicsView instead of QGraphcisScene.
Reimplement mousePressEvent there.
At that moment you can determine item under mouse pointer.
it it is there - you can just call QGraphicsView::mousePressEvent();
It it is not - use your implementation for add new item.
It also allows you to separate behavior of different views.

How to transform a line into a curve with the mouse in Qt?

I am having trouble with a project of mine. I am trying to draw in an render-area a course for the cars (a street) which can contain both straight lines and curves. For that I was thinking of primarily drawing the lines and then with the mouse selecting one line and transforming it into a curve by moving the mouse (a curve which has as peek the point on the line selected by the mouse). Until now I only managed to draw points in the render area and automatically generate lines between these points, but I am not sure about how to transform the line into a curve with the mouse.
My code until now is:
renderarea.cpp:
RenderArea::RenderArea(QWidget *parent)
: QWidget(parent)
{
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
}
void RenderArea::mousePressEvent(QMouseEvent *e)
{
point = e->pos();
updateList(point);
this->update();
}
void RenderArea::updateList(const QPoint& p)
{
Point point;
point.point = p;
list.append(point);
if (list.count()>1)
lineAdded(point);
}
void RenderArea::lineAdded(const Point &p)
{
Line temp;
temp.endPoint = p;
temp.startPoint = list.at(list.count() - 2);
lines.append(temp);
}
void RenderArea::paintEvent(QPaintEvent * /* event */)
{
int i;
QPainter painter(this);
painter.setPen(QPen(Qt::black,2));
for (i = 0; i < list.size(); ++i)
painter.drawPoint(list[i].point);
for (i = 0; i < lines.size(); ++i)
painter.drawLine(lines[i].startPoint.point, lines[i].endPoint.point);
}
Hope you can help me. Thanks in advance.
Have some UI (right click?) that changes the line segment to Besier curve. Then control the shape of the curve by dragging handles (which you will need to provide). Another right click changes the curve back to segment.

Conway's Game of Life - C++ and Qt

I've done all of the layouts and have most of the code written even. But, I'm stuck in two places.
1) I'm not quite sure how to set up the timer. Am I using it correctly in the gridwindow class? And, am I used the timer functions/signals/slots correctly with the other gridwindow functions.
2) In GridWindow's timerFired() function, I'm having trouble checking/creating the vector-vectors. I wrote out in the comments in that function exactly what I am trying to do.
Any help would be much appreciated.
main.cpp
// Main file for running the grid window application.
#include <QApplication>
#include "gridwindow.h"
//#include "timerwindow.h"
#include <stdexcept>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
void Welcome(); // Welcome Function - Prints upon running program; outputs program name, student name/id, class section.
void Rules(); // Rules Function: Prints the rules for Conway's Game of Life.
using namespace std;
// A simple main method to create the window class and then pop it up on the screen.
int main(int argc, char *argv[])
{
Welcome(); // Calls Welcome function to print student/assignment info.
Rules(); // Prints Conway's Game Rules.
QApplication app(argc, argv); // Creates the overall windowed application.
int rows = 25, cols = 35; //The number of rows & columns in the game grid.
GridWindow widget(NULL,rows,cols); // Creates the actual window (for the grid).
widget.show(); // Shows the window on the screen.
return app.exec(); // Goes into visual loop; starts executing GUI.
}
// Welcome Function: Prints my name/id, my class number, the assignment, and the program name.
void Welcome()
{
cout << endl;
cout << "-------------------------------------------------------------------------------------------------" << endl;
cout << "Name/ID - Gabe Audick #7681539807" << endl;
cout << "Class/Assignment - CSCI-102 Disccusion 29915: Homework Assignment #4" << endl;
cout << "-------------------------------------------------------------------------------------------------" << endl << endl;
}
// Rules Function: Prints the rules for Conway's Game of Life.
void Rules()
{
cout << "Welcome to Conway's Game of Life." << endl;
cout << "Game Rules:" << endl;
cout << "\t 1) Any living cell with fewer than two living neighbours dies, as if caused by underpopulation." << endl;
cout << "\t 2) Any live cell with more than three live neighbours dies, as if by overcrowding." << endl;
cout << "\t 3) Any live cell with two or three live neighbours lives on to the next generation." << endl;
cout << "\t 4) Any dead cell with exactly three live neighbours becomes a live cell." << endl << endl;
cout << "Enjoy." << endl << endl;
}
gridcell.h
// A header file for a class representing a single cell in a grid of cells.
#ifndef GRIDCELL_H_
#define GRIDCELL_H_
#include <QPalette>
#include <QColor>
#include <QPushButton>
#include <Qt>
#include <QWidget>
#include <QFrame>
#include <QHBoxLayout>
#include <iostream>
// An enum representing the two different states a cell can have.
enum CellType
{
DEAD, // DEAD = Dead Cell. --> Color = White.
LIVE // LIVE = Living Cell. ---> Color = White.
};
/*
Class: GridCell.
A class representing a single cell in a grid. Each cell is implemented
as a QT QFrame that contains a single QPushButton. The button is sized
so that it takes up the entire frame. Each cell also keeps track of what
type of cell it is based on the CellType enum.
*/
class GridCell : public QFrame
{
Q_OBJECT // Macro allowing us to have signals & slots on this object.
private:
QPushButton* button; // The button inside the cell that gives its clickability.
CellType type; // The type of cell (DEAD or LIVE.)
public slots:
void handleClick(); // Callback for handling a click on the current cell.
void setType(CellType type); // Cell type mutator. Calls the "redrawCell" function.
signals:
void typeChanged(CellType type); // Signal to notify listeners when the cell type has changed.
public:
GridCell(QWidget *parent = NULL); // Constructor for creating a cell. Takes parent widget or default parent to NULL.
virtual ~GridCell(); // Destructor.
void redrawCell(); // Redraws cell: Sets new type/color.
CellType getType() const; //Simple getter for the cell type.
private:
Qt::GlobalColor getColorForCellType(); // Helper method. Returns color that cell should be based from its value.
};
#endif
gridcell.cpp
#include <iostream>
#include "gridcell.h"
#include "utility.h"
using namespace std;
// Constructor: Creates a grid cell.
GridCell::GridCell(QWidget *parent)
: QFrame(parent)
{
this->type = DEAD; // Default: Cell is DEAD (white).
setFrameStyle(QFrame::Box); // Set the frame style. This is what gives each box its black border.
this->button = new QPushButton(this); //Creates button that fills entirety of each grid cell.
this->button->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); // Expands button to fill space.
this->button->setMinimumSize(19,19); //width,height // Min height and width of button.
QHBoxLayout *layout = new QHBoxLayout(); //Creates a simple layout to hold our button and add the button to it.
layout->addWidget(this->button);
setLayout(layout);
layout->setStretchFactor(this->button,1); // Lets the buttons expand all the way to the edges of the current frame with no space leftover
layout->setContentsMargins(0,0,0,0);
layout->setSpacing(0);
connect(this->button,SIGNAL(clicked()),this,SLOT(handleClick())); // Connects clicked signal with handleClick slot.
redrawCell(); // Calls function to redraw (set new type for) the cell.
}
// Basic destructor.
GridCell::~GridCell()
{
delete this->button;
}
// Accessor for the cell type.
CellType GridCell::getType() const
{
return(this->type);
}
// Mutator for the cell type. Also has the side effect of causing the cell to be redrawn on the GUI.
void GridCell::setType(CellType type)
{
this->type = type;
redrawCell();
}
// Handler slot for button clicks. This method is called whenever the user clicks on this cell in the grid.
void GridCell::handleClick()
{ // When clicked on...
if(this->type == DEAD) // If type is DEAD (white), change to LIVE (black).
type = LIVE;
else
type = DEAD; // If type is LIVE (black), change to DEAD (white).
setType(type); // Sets new type (color). setType Calls redrawCell() to recolor.
}
// Method to check cell type and return the color of that type.
Qt::GlobalColor GridCell::getColorForCellType()
{
switch(this->type)
{
default:
case DEAD:
return Qt::white;
case LIVE:
return Qt::black;
}
}
// Helper method. Forces current cell to be redrawn on the GUI. Called whenever the setType method is invoked.
void GridCell::redrawCell()
{
Qt::GlobalColor gc = getColorForCellType(); //Find out what color this cell should be.
this->button->setPalette(QPalette(gc,gc)); //Force the button in the cell to be the proper color.
this->button->setAutoFillBackground(true);
this->button->setFlat(true); //Force QT to NOT draw the borders on the button
}
gridwindow.h
// A header file for a QT window that holds a grid of cells.
#ifndef GRIDWINDOW_H_
#define GRIDWINDOW_H_
#include <vector>
#include <QWidget>
#include <QTimer>
#include <QGridLayout>
#include <QLabel>
#include <QApplication>
#include "gridcell.h"
/*
class GridWindow:
This is the class representing the whole window that comes up when this program runs.
It contains a header section with a title, a middle section of MxN cells and a bottom section with buttons.
*/
class GridWindow : public QWidget
{
Q_OBJECT // Macro to allow this object to have signals & slots.
private:
std::vector<std::vector<GridCell*> > cells; // A 2D vector containing pointers to all the cells in the grid.
QLabel *title; // A pointer to the Title text on the window.
QTimer *timer; // Creates timer object.
public slots:
void handleClear(); // Handler function for clicking the Clear button.
void handleStart(); // Handler function for clicking the Start button.
void handlePause(); // Handler function for clicking the Pause button.
void timerFired(); // Method called whenever timer fires.
public:
GridWindow(QWidget *parent = NULL,int rows=3,int cols=3); // Constructor.
virtual ~GridWindow(); // Destructor.
std::vector<std::vector<GridCell*> >& getCells(); // Accessor for the array of grid cells.
private:
QHBoxLayout* setupHeader(); // Helper function to construct the GUI header.
QGridLayout* setupGrid(int rows,int cols); // Helper function to constructor the GUI's grid.
QHBoxLayout* setupButtonRow(); // Helper function to setup the row of buttons at the bottom.
};
#endif
gridwindow.cpp
#include <iostream>
#include "gridwindow.h"
using namespace std;
// Constructor for window. It constructs the three portions of the GUI and lays them out vertically.
GridWindow::GridWindow(QWidget *parent,int rows,int cols)
: QWidget(parent)
{
QHBoxLayout *header = setupHeader(); // Setup the title at the top.
QGridLayout *grid = setupGrid(rows,cols); // Setup the grid of colored cells in the middle.
QHBoxLayout *buttonRow = setupButtonRow(); // Setup the row of buttons across the bottom.
QVBoxLayout *layout = new QVBoxLayout(); // Puts everything together.
layout->addLayout(header);
layout->addLayout(grid);
layout->addLayout(buttonRow);
setLayout(layout);
}
// Destructor.
GridWindow::~GridWindow()
{
delete title;
}
// Builds header section of the GUI.
QHBoxLayout* GridWindow::setupHeader()
{
QHBoxLayout *header = new QHBoxLayout(); // Creates horizontal box.
header->setAlignment(Qt::AlignHCenter);
this->title = new QLabel("CONWAY'S GAME OF LIFE",this); // Creates big, bold, centered label (title): "Conway's Game of Life."
this->title->setAlignment(Qt::AlignHCenter);
this->title->setFont(QFont("Arial", 32, QFont::Bold));
header->addWidget(this->title); // Adds widget to layout.
return header; // Returns header to grid window.
}
// Builds the grid of cells. This method populates the grid's 2D array of GridCells with MxN cells.
QGridLayout* GridWindow::setupGrid(int rows,int cols)
{
QGridLayout *grid = new QGridLayout(); // Creates grid layout.
grid->setHorizontalSpacing(0); // No empty spaces. Cells should be contiguous.
grid->setVerticalSpacing(0);
grid->setSpacing(0);
grid->setAlignment(Qt::AlignHCenter);
for(int i=0; i < rows; i++) //Each row is a vector of grid cells.
{
std::vector<GridCell*> row; // Creates new vector for current row.
cells.push_back(row);
for(int j=0; j < cols; j++)
{
GridCell *cell = new GridCell(); // Creates and adds new cell to row.
cells.at(i).push_back(cell);
grid->addWidget(cell,i,j); // Adds to cell to grid layout. Column expands vertically.
grid->setColumnStretch(j,1);
}
grid->setRowStretch(i,1); // Sets row expansion horizontally.
}
return grid; // Returns grid.
}
// Builds footer section of the GUI.
QHBoxLayout* GridWindow::setupButtonRow()
{
QHBoxLayout *buttonRow = new QHBoxLayout(); // Creates horizontal box for buttons.
buttonRow->setAlignment(Qt::AlignHCenter);
// Clear Button - Clears cell; sets them all to DEAD/white.
QPushButton *clearButton = new QPushButton("CLEAR");
clearButton->setFixedSize(100,25);
connect(clearButton, SIGNAL(clicked()), this, SLOT(handleClear()));
buttonRow->addWidget(clearButton);
// Start Button - Starts game when user clicks. Or, resumes game after being paused.
QPushButton *startButton = new QPushButton("START/RESUME");
startButton->setFixedSize(100,25);
connect(startButton, SIGNAL(clicked()), this, SLOT(handleStart()));
buttonRow->addWidget(startButton);
// Pause Button - Pauses simulation of game.
QPushButton *pauseButton = new QPushButton("PAUSE");
pauseButton->setFixedSize(100,25);
connect(pauseButton, SIGNAL(clicked()), this, SLOT(handlePause()));
buttonRow->addWidget(pauseButton);
// Quit Button - Exits program.
QPushButton *quitButton = new QPushButton("EXIT");
quitButton->setFixedSize(100,25);
connect(quitButton, SIGNAL(clicked()), qApp, SLOT(quit()));
buttonRow->addWidget(quitButton);
return buttonRow; // Returns bottom of layout.
}
/*
SLOT method for handling clicks on the "clear" button.
Receives "clicked" signals on the "Clear" button and sets all cells to DEAD.
*/
void GridWindow::handleClear()
{
for(unsigned int row=0; row < cells.size(); row++) // Loops through current rows' cells.
{
for(unsigned int col=0; col < cells[row].size(); col++)
{
GridCell *cell = cells[row][col]; // Grab the current cell & set its value to dead.
cell->setType(DEAD);
}
}
}
/*
SLOT method for handling clicks on the "start" button.
Receives "clicked" signals on the "start" button and begins game simulation.
*/
void GridWindow::handleStart()
{
this->timer = new QTimer(this); // Creates new timer.
connect(this->timer, SIGNAL(timeout()), this, SLOT(timerFired())); // Connect "timerFired" method class to the "timeout" signal fired by the timer.
this->timer->start(500); // Timer to fire every 500 milliseconds.
}
/*
SLOT method for handling clicks on the "pause" button.
Receives "clicked" signals on the "pause" button and stops the game simulation.
*/
void GridWindow::handlePause()
{
this->timer->stop(); // Stops the timer.
delete this->timer; // Deletes timer.
}
// Accessor method - Gets the 2D vector of grid cells.
std::vector<std::vector<GridCell*> >& GridWindow::getCells()
{
return this->cells;
}
void GridWindow::timerFired()
{
// I'm not sure how to write this code.
// I want to take the original vector-vector, and also make a new, empty vector-vector of the same size.
// I would then go through the code below with the original vector, and apply the rules to the new vector-vector.
// Finally, I would make the new vector-vecotr the original vector-vector. (That would be one step in the simulation.)
cout << cells[1][2];
/*
for (unsigned int m = 0; m < original.size(); m++)
{
for (unsigned int n = 0; n < original.at(m).size(); n++)
{
unsigned int neighbors = 0; //Begin counting number of neighbors.
if (original[m-1][n-1].getType() == LIVE) // If a cell next to [i][j] is LIVE, add one to the neighbor count.
neighbors += 1;
if (original[m-1][n].getType() == LIVE)
neighbors += 1;
if (original[m-1][n+1].getType() == LIVE)
neighbors += 1;
if (original[m][n-1].getType() == LIVE)
neighbors += 1;
if (original[m][n+1].getType() == LIVE)
neighbors += 1;
if (original[m+1][n-1].getType() == LIVE)
neighbors += 1;
if (original[m+1][n].getType() == LIVE)
neighbors += 1;
if (original[m+1][n+1].getType() == LIVE)
neighbors += 1;
if (original[m][n].getType() == LIVE && neighbors < 2) // Apply game rules to cells: Create new, updated grid with the roundtwo vector.
roundtwo[m][n].setType(LIVE);
else if (original[m][n].getType() == LIVE && neighbors > 3)
roundtwo[m][n].setType(DEAD);
else if (original[m][n].getType() == LIVE && (neighbors == 2 || neighbors == 3))
roundtwo[m][n].setType(LIVE);
else if (original[m][n].getType() == DEAD && neighbors == 3)
roundtwo[m][n].setType(LIVE);
}
}*/
}
It looks like the timer is set up correctly to me.
For the timerFired() function, I would not create a temporary matrix of GridCell objects, but just a temporary matrix of flags that indicate whether the cell is live or not. These flags are really all that changes in your function, so just temporarily store the new flags, then set them on the original grid cells to save all of the extra memory and allocation time required for creating a temporary matrix of cells. Here is an example:
//Store flags that represent whether the new grid cells will be live or not
vector< vector<bool> > is_live(cells.size());
for(int m=0; m<cells.size(); m++)
{
is_live.at(m).resize(cells.at(m).size());
for(int n=0; n<cells.at(m).size(); n++)
{
//count neighbors
unsigned int neighbors = 0;
for(int i=-1; i<=1; i++)
{
for(int j=-1; j<=1; j++)
{
neighbors += static_cast<int>(
cells[m+i][n+j]->getType() == LIVE);
}
}
//we counted the current cell when counting the neighbors so
//subtract it back off if needed.
neighbors -= static_cast<int>(cells[m][n]->getType() == LIVE);
//Set the type to the original value
is_live[m][n] = cells[m][n]->getType() == LIVE;
//change it based on the neighbor count.
//Some of your logic around here seemed repetitive so I
//did it differently. You may want to change it back
//if you had a specific purpose for the way you did it
is_live[m][n] = (is_live[m][n] && neighbors <= 3) ||
(!is_live[m][n] && neighbors == 3);
}
}
//Set the cell types based on the is_live flags.
for(int m=0; m<cells.size(); m++)
{
for(int n=0; n<cells.at(m).size(); n++)
cells[m][n]->setType(is_live[m][n] ? LIVE : DEAD);
}
Note: I did not compile or test this so there are no guarantees.