Detecting which extended sprited i touched - c++

I want detect which object of Card I touched. Card is custom Class which extends cocos Sprite.
I would like to call member methods on card. Something like this: if (target is Card) target.openCard();
Thank you very much in advance.
Main Class Body
bool HelloWorld::init()
{
... some init code, generating card arrays, shuffling
// draw memory cards
int count = 0;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 4; j++)
{
auto card = Card::createCard();
card->customInit(cardsPictures[count]);
this->addChild(card);
card->setPosition(100 + i*100, 600 - j*100);
count++;
}
}
// register event listener
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
touchListener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
touchListener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
touchListener->onTouchCancelled = CC_CALLBACK_2(HelloWorld::onTouchCancelled, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
return true;
}
bool HelloWorld::onTouchBegan(Touch* touch, Event* event)
{
auto target = event->getCurrentTarget();
if (target is Card) target.openCard(); // not working
return true;
}

(target is Card)
That doesn't look like C++ to me. What is it ? :D
First:
Is target a pointer ? If so do:
target->openCard(); // instead of target.openCard();
Anyway, if you want to call methods on an object that you are CERTAIN is of type card, perhaps you should do :
Card* myCard = static_cast<Card*>(target);
myCard->openCard();
To be honest unless you actually post the relevant code it would be hard for anyone to help you. What does Card even look like ? (I don't care! XD)

Related

How to create and use a QVector in other functions or is there an easier way to do this?

I am using multiple QSpinBoxes that all need to do the exact same thing when interacted with. I am able to get them to do what I want with no issue but I thought I could optimize them by just having them call on 1 function. The only issue was that I would have to have an if statement in order to set a new minimum every time they are interacted with. If I could put them into some sort of array or list then I would be able to easily just make the change based off of one number. Using various previous threads and websites I tried to make a QVector of QSpinboxes in order to use as I previously described. After a lot of trial and errors I've reached a point where I'm not sure why I am getting the error that I am.
Here is my code:
MainWindow::~MainWindow()
{
QVector <QSpinBox*> boxes(0);
QSpinBox* sqlBox=new QSpinBox(this);
QSpinBox* perlBox=new QSpinBox(this);
QSpinBox* rustBox=new QSpinBox(this);
QSpinBox* cSharpBox=new QSpinBox(this);
QSpinBox* htmlBox=new QSpinBox(this);
QSpinBox* cssBox=new QSpinBox(this);
QSpinBox* javaBox=new QSpinBox(this);
QSpinBox* pythonBox=new QSpinBox(this);
QSpinBox* cPlusBox=new QSpinBox(this);
boxes.append(sqlBox);
boxes.append(perlBox);
boxes.append(rustBox);
boxes.append(cSharpBox);
boxes.append(htmlBox);
boxes.append(cssBox);
boxes.append(javaBox);
boxes.append(pythonBox);
boxes.append(cPlusBox);
for (int i = 0; i <= 8; i++)
{
boxes[i]->setRange(0, 1000);
boxes[i]->accessibleName();
if (i < 3)
{
boxes[i]->setGeometry(100, 105+(90*i), 71, 21);
}
else if (i >= 3 && i < 6)
{
boxes[i]->setGeometry(290, 105+(90*(i%3)), 71, 21);
}
else
{
boxes[i]->setGeometry(460, 105+(90*(i%3)), 71, 21);
}
}
delete ui;
}
void MainWindow::buyPrgm(short unsigned int input,short unsigned int prgm)
{
int first = 0, second = 0;
first = revButton();
if (code >= prgmCostOG[prgm] + pow(prgmCost[prgm], 0.1*prgmAmount[prgm]))
{
prgmAmount[prgm] = input;
prgmCost[prgm] = prgmCostOG[prgm] + pow(prgmCost[prgm], 0.1*prgmAmount[prgm]);
ui->boxes[prgm]->setMinimum(prgmAmount);
}
else
{
ui->noBuy->setText("You don't have enough code!");
for(int i = 0; i > 1000; i++)
{
second = revButton();
if ((second - first) > 5)
{
ui->noBuy->setText("");
break;
}
}
}
}
The two errors I'm getting are:
"error: Member reference type 'QVector' (aka 'QList') is not a pointer; did you mean to use '.'? (fix available)"
// I can fix this error by changing
boxes[prgm]->setMinimum(prgmAmount[prgm]);
// into
boxes[prgm].setMinimum(prgmAmount[prgm]);
Even after making this fix I still have this error:
"error: No member named 'setMinimum' in 'QList'"
I assume this means that it only wants me to use the built in functions that work with QVectors for example "append' or "capacity" despite being able to use the built in functions for QSpinBoxes in the function that they were declared in.
Once again everything I've learned about QVectors has been through posts that people have already made so I may be making awful mistakes. Any help is welcome.

Qt Program has unexpectedly finished for no reason

I am new to Qt and trying to make my first Qt application. I found that I am getting "the program has unexpectedly finished" error for no apparent reason to me. I commented out all the code and uncommented them line by line and it seems to be only certain lines of my code that are generating this error. I can't for the life of me understand why this would be happening.
I have a main window that works perfectly fine. All the layouts and connections work, etc. When I click a certain button, it opens a new window. This window also works perfectly fine. When a certain button is clicked, a third window is opened. This is where the issue lies. For no apparent reason, specific lines are generating problems. I have a vertical layout object and when I try to addLayout(layout_horizontal) I get the unexpected finish error. In the next line after that, I add a grid layout with no issues, and can add two buttons to the horizontal layout with no issues. Why would my addLayout(layout_horizontal) not work but addLayout(layout_grid) does?
I do a button[i][j] = new QPushButton(this) and this also generates the same error. It is not an issue with my pointers because the same code runs fine in my Xcode environment.
What baffles me the most is that my this->size = size line causes this unexpected finish error! I have a private int size declared in my header, and then have int size parameter in my constructor. Why would that assignment cause my program to unexpectedly finish? That seems to make no sense to me at all.
And even more than that, if I copy my entire .h and .cpp files and paste them into a different Qt project in a different folder, the code works perfectly fine. No unexpected finish. Nothing.
Could it be that my project is somehow corrupted? Or there is a bug in the Qt development environment that is causing this code to be compiled incorrectly and cause a runtime error?
I am so confused and frustrated because the my code should work but it doesn't.
I have attached the code below.
This code works fine
GameWindow::GameWindow(int size, QWidget *previous) : QWidget() {
this->previous = previous;
int button_size = 0;
switch (size) {
case 10:
button_size = 50;
break;
case 20:
button_size = 35;
break;
case 30:
button_size = 30;
break;
}
//this->size = size; NOTICE THIS LINE IS COMMENTED
map = new Button**[size];
button_flag = new QPushButton(tr("&Flag"), this);
button_quit = new QPushButton(tr("&Quit"), this);
layout_grid = new QGridLayout;
layout_vertical = new QVBoxLayout;
layout_horizontal = new QHBoxLayout;
//set up the layout
this->setLayout(layout_vertical);
//layout_vertical->addLayout(layout_horizontal);
layout_vertical->addLayout(layout_grid);
layout_horizontal->addWidget(button_flag);
layout_horizontal->addWidget(button_quit);
layout_grid->setSpacing(0);
//set up buttons
button_flag->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
button_quit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
button_flag->setFixedHeight(50);
button_quit->setFixedHeight(50);
connect(button_quit, SIGNAL(clicked()), this, SLOT(close()));
connect(button_flag, SIGNAL(clicked()), this, SLOT(close()));
for (int i = 0; i < size; i++) {
map[i] = new Button*[size];
for (int j = 0; j < size; j++) {
//map[i][j] = new Button(this);
//map[i][j]->setFixedSize(button_size, button_size);
//layout_grid->addWidget(map[i][j], i, j);
//connect(map[i][j], SIGNAL(clicked()), map[i][j], SLOT(button_clicked()));
}
}
this->setFixedSize(this->minimumWidth(), this->minimumHeight());
initialize_bombs();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
count_neighboring_bombs(i, j);
}
}
}
This code unexpectedly finishes
GameWindow::GameWindow(int size, QWidget *previous) : QWidget() {
this->previous = previous;
int button_size = 0;
switch (size) {
case 10:
button_size = 50;
break;
case 20:
button_size = 35;
break;
case 30:
button_size = 30;
break;
}
this->size = size; //NOTICE THIS LINE IS NO LONGER COMMENTED
map = new Button**[size];
button_flag = new QPushButton(tr("&Flag"), this);
button_quit = new QPushButton(tr("&Quit"), this);
layout_grid = new QGridLayout;
layout_vertical = new QVBoxLayout;
layout_horizontal = new QHBoxLayout;
//set up the layout
this->setLayout(layout_vertical);
//layout_vertical->addLayout(layout_horizontal);
layout_vertical->addLayout(layout_grid);
layout_horizontal->addWidget(button_flag);
layout_horizontal->addWidget(button_quit);
layout_grid->setSpacing(0);
//set up buttons
button_flag->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
button_quit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
button_flag->setFixedHeight(50);
button_quit->setFixedHeight(50);
connect(button_quit, SIGNAL(clicked()), this, SLOT(close()));
connect(button_flag, SIGNAL(clicked()), this, SLOT(close()));
for (int i = 0; i < size; i++) {
map[i] = new Button*[size];
for (int j = 0; j < size; j++) {
//map[i][j] = new Button(this);
//map[i][j]->setFixedSize(button_size, button_size);
//layout_grid->addWidget(map[i][j], i, j);
//connect(map[i][j], SIGNAL(clicked()), map[i][j], SLOT(button_clicked()));
}
}
this->setFixedSize(this->minimumWidth(), this->minimumHeight());
initialize_bombs();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
count_neighboring_bombs(i, j);
}
}
}
I know that sometimes errors will arise but cleaning the project and running qmake before building again fixes it. Maybe the solution is some sort of trick like that. Thanks for any help.
I assume that you have no (own) size member in the GameWindow class.
If so, then you are assigning an int to QWidget's size member, which is of type QSize. I would expect the compiler to complain, but I as I don't know how your header looks like it is difficult to tell.
If you have a int size member in GameWindow that may also be a cause, as QWidget has already have a member with that name...

In GTKMM, on_draw method stops being called after invalidate occurs in separated thread

Using GTKMM, I'm extending the DrawingArea widget with the idea that an external process provides it with images. My CameraDrawingArea will then display the images at the right size using Cairo.
Each time an image arrives, I store it and I call the invalidate method, which eventually ends up in a call to on_draw, where I can resize and display the image.
My problem is the following:
The first 10 or 20 images are displayed as I expected.
After a while, the images keep coming from the provider process, I keep calling invalidate
but on_draw is not called any more.
To show it here, I've simplified the code so that there is nothing external to the class, and no link with other libraries. I've replaced the process providing the images by a method with for-loops, and the display of the image by printing a simple text in the middle of the widget area:
In the constructor I launch a new std::thread to call the doCapture method in the same instance. I also set up a font description, to use it later.
The doCapture method is a silly CPU eater, that does nothing except calling from time to time the refreshDrawing method, as long as keepCapturing is not false.
refreshDrawing invalidates the whole window's rectangle via a call to invalidate.
Gtk's magic is suppose to call on_draw and provide a Cairo context to draw whatever. In my case, for tests purposes, I draw a brownish centered integer.
The class destructor stops the thread by set keepCapturing to false, and waits for termination with a join.
#include "camera-drawing-area.hpp"
#include <iostream>
CameraDrawingArea::CameraDrawingArea():
captureThread(nullptr) {
fontDescription.set_family("Monospace");
fontDescription.set_weight(Pango::WEIGHT_BOLD);
fontDescription.set_size(30 * Pango::SCALE);
keepCapturing = true;
captureThread = new std::thread([this] {
doCapture();
});
}
void CameraDrawingArea::doCapture() {
while (keepCapturing) {
float f = 0.0;
for (int n = 0; n < 1000; n++) {
for (int m = 0; m < 1000; m++) {
for (int o = 0; o < 500; o++) {
f += 1.2;
}
}
}
std::cout << "doCapture - " << f << std::endl;
refreshDrawing();
}
}
void CameraDrawingArea::refreshDrawing() {
auto win = get_window();
if (win) {
win->invalidate(false);
std::cout << "refreshDrawing" << std::endl;
}
}
bool CameraDrawingArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
std::cout << "on_draw" << std::endl;
static char buffer[50];
static int n = 0;
sprintf(buffer, "-%d-", n++);
Gtk::Allocation allocation = get_allocation();
const int width = allocation.get_width();
const int height = allocation.get_height();
auto layout = create_pango_layout(buffer);
layout->set_font_description(fontDescription);
int textWidth, textHeight;
layout->get_pixel_size(textWidth, textHeight);
cr->set_source_rgb(0.5, 0.2, 0.1);
cr->move_to((width - textWidth) / 2, (height - textHeight) / 2);
layout->show_in_cairo_context(cr);
cr->stroke();
return true;
}
CameraDrawingArea::~CameraDrawingArea() {
keepCapturing = false;
captureThread->join();
free(captureThread);
}
And this is my header file:
#ifndef CAMERA_DRAWING_AREA_HPP
#define CAMERA_DRAWING_AREA_HPP
#include <gtkmm.h>
#include <thread>
class CameraDrawingArea : public Gtk::DrawingArea {
public:
CameraDrawingArea();
virtual ~CameraDrawingArea();
protected:
bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override;
private:
bool keepCapturing;
void doCapture();
void refreshDrawing();
std::thread* captureThread;
Pango::FontDescription fontDescription;
};
#endif
The problem manifests itself as follows:
When starting the application, it faithfully displays 1, 2, 3...
Between 5th and 20th iteration (it's random, but rarely outside these ranges), it stops refreshing.
Because of the cout, I can see that refreshDrawing is called be sure that invalidate is also called, but on_draw isn't.
Also, if I stop the application before it stops refreshing, then it ends up nicely. But, if I stop the application after it stops refreshing, then I see this message below (the ID value varies):
GLib-CRITICAL **: 10:05:04.716: Source ID 25 was not found when attempting to remove it
I'm quite sure that I do something wrong, but clueless about what. Any help would be appreciated.
I also checked the following questions, but they don't seem to be related with my case:
Draw signal doesn't get fired in GTKMM, when derived class doesn't call a superclass's constructor
You can't use GTK methods from any other thread than the one in which you started the GTK main loop. Probably the win->invalidate() call is causing things to go wrong here.
Instead, use Glib::Dispatcher to communicate with the main thread, or use gdk_threads_add_idle() for a more C-style solution.
Based on the answer form #ptomato, I've rewritten my example code. The golden rule is do not call GUI functions from another thread, but if you do, then acquire some specific GDK locks first. That's the purpose of Glib::Dispatcher :
If a Glib::Dispatcher object is constructed in the main GUI thread (which will therefore be the receiver thread), any worker thread can emit on it and have the connected slots safely execute gtkmm functions.
Based on that, I've added a new private member Glib::Dispatcher refreshDrawingDispatcher that will allow threads to safely the invalidate the windows area:
#ifndef CAMERA_DRAWING_AREA_HPP
#define CAMERA_DRAWING_AREA_HPP
#include <gtkmm.h>
#include <thread>
class CameraDrawingArea :
public Gtk::DrawingArea {
public:
CameraDrawingArea();
virtual ~CameraDrawingArea();
protected:
bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override;
private:
bool keepCapturing;
void doCapture();
void refreshDrawing();
Glib::Dispatcher refreshDrawingDispatcher;
std::thread* captureThread;
Pango::FontDescription fontDescription;
};
#endif
Then, I've connected the dispatcher to the refreshDrawing method. I do this in the class constructor, which is called during GUI start up and therefore in the main GUI thread:
CameraDrawingArea::CameraDrawingArea():
refreshDrawingDispatcher(),
captureThread(nullptr) {
fontDescription.set_family("Monospace");
fontDescription.set_weight(Pango::WEIGHT_BOLD);
fontDescription.set_size(30 * Pango::SCALE);
keepCapturing = true;
captureThread = new std::thread([this] {
doCapture();
});
refreshDrawingDispatcher.connect(sigc::mem_fun(*this, &CameraDrawingArea::refreshDrawing));
}
Finally, the thread has to call the dispatcher:
void CameraDrawingArea::doCapture() {
while (keepCapturing) {
float f = 0.0;
for (int n = 0; n < 1000; n++) {
for (int m = 0; m < 1000; m++) {
for (int o = 0; o < 500; o++) {
f += 1.2;
}
}
}
std::cout << "doCapture - " << f << std::endl;
refreshDrawingDispatcher.emit();
}
}
And now, this works without further problems.

How to animate through an a container for windows.draw()?

I have a container of objects:
std::vector<sf::Drawable> gameObjects;
My goal is to iterate through these objects and draw them:
for (auto it : gameObjects)
{
window.draw(*it);
}
You can assume these methods are already implemented. What I mean by 'animate': I want these objects drawn one at a time, rather than all at once immediately.
void Run::render()
{
window.clear(colorWindow);
for (auto it : gameObjects)
{
window.draw(*it);
}
window.display();
}
That is, have every render() draw an additional object in the container. Do you know how I can go about doing this? Thank you in advance.
edit: I've tried something unconventional - I dont think it's good practice, but it works:
int count = 0;
int countReset = 1;
...
while (countReset > count)
{
objects.at(count++).draw(window);
}
countReset++;
count = 0;
You should never make decisions about your game in your rendering. You should have an update method somewhere, that makes decisions on how input and time passed affect your game world. Rendering should only do one thing: rendering. No decision making, no data manipulation.
You could for example use a compound class to indicate which object should be drawn or you could derive your own base class from sf::Drawable and implement a bool IsVisible() const; method. Either way, the decision if it's drawn is made elsewhere, the rendering is only executing commands what to render specifically:
struct MaybeDrawn
{
bool IsVisible;
sf::Drawable Object;
};
...
std::vector<MaybeDrawn> gameObjects;
...
void Run::render()
{
window.clear(colorWindow);
for (auto it : gameObjects)
{
if(it->IsVisible)
{
window.draw(it->Object);
}
}
window.display();
}
Your decision, how and when to set the bool to true should happen in your Update/ChangeWorld method. Maybe you want to have a look here for a general description on how to structure a game and how to build a timer.
Alternatively, you could leave your program as it is and insert and/or delete from your vector in your decision making method.
Use a static counter for your index in list of objects. Measure time and incremente the counter if a time period has elapsed. So next time is drawn next object in list.
#include <chrono>
void Run::render()
{
// actual time
std::chrono::high_resolution_clock::time_point actTime = std::chrono::high_resolution_clock::now();
// remember time
static bool init = false;
std::chrono::high_resolution_clock::time_point lastTime;
if ( !init )
{
lastTime = actTime;
init = true;
}
// delta time
long microseconds = std::chrono::duration_cast<std::chrono::duration<long, std::micro> >( actTime - lastTime ).count();
double deltaTms = ((double)microseconds) / 1000.0;
window.clear(colorWindow);
static size_t actObjInx = 0;
window.draw(gameObjects[actObjInx]);
// increment object index if time period has elapsed
if ( deltaTms > 100.0 ) // 0.1 seconds
{
lastTime = actTime;
actObjInx ++;
if ( actObjInx == gameObjects.size() )
actObjInx = 0;
}
window.display();
}

Maintaining checkedstatus inheritance in a QTreeView

I'm trying to do something basic : you have a QTreeView. 1st depth are folders only, 2nd depth are files only. I want to have a check box with the checked status next to each item. Files are either checked or unchecked, folders can also be partiallyChecked depending on their files; all in all quite natural I believe.
The way I though I should go was using a QStandardItemModel and populate it with a custom subclass of QStandardItem : DescriptionFileItem. Maybe that was a bad idea, if there's an easier way please enlight me.
I tried using signals and slots so that my signal CheckStateChanged on a file would be connected to a slot UpdateCheckedStateOnChildStateChanged on its containing folder. This required my DescriptionFileItem to inherit from QObject as well (BTW, I was surprised that QStandardItem did not inherit from QObject). I initially hoped this would work seamlessly with the provided base classes but it did not : emitDataChanged() didn't seem to trigger my model's dataChanged() signal...
Using the model's dataChanged signals directly didn't work either: it's call is protected so you can't use it without subclassing (I think that's my next move unless somebody can help me get it right).
At the moment I have a signal -> slot connection that won't work and I have no idea why; compile and link work ok. Here's the code; perhapps you'll spot my mistakes easily. I'm leaving some commented lines so you can maybe see what I did wrong in a previous attempt. Thanks for your input!
#ifndef DESCRIPTIONFILEITEM_H
#define DESCRIPTIONFILEITEM_H
#include <QStandardItem>
#include <Qt>
class DescriptionFileItem : public QObject, public QStandardItem
{
Q_OBJECT
public:
explicit DescriptionFileItem(const QString & text, bool isFileName=false, QObject* parent = 0);
void setData ( const QVariant & value, int role = Qt::UserRole + 1 );
QVariant data( int role = Qt::UserRole + 1 ) const;
QString text;
Qt::CheckState checkedState;
bool isFileName;
signals:
void CheckStateChanged();
public slots:
void UpdateCheckedStateOnChildStateChanged();
};
#endif // DESCRIPTIONFILEITEM_H
Corresponding .cpp :
#include "DescriptionFileItem.h"
DescriptionFileItem::DescriptionFileItem(const QString & text, bool isFileName, QObject* parent):
QObject(parent),QStandardItem(text)
{
this->isFileName = isFileName;
checkedState = Qt::Checked;
}
void DescriptionFileItem::setData ( const QVariant & value, int role){
if(role == Qt::CheckStateRole){
Qt::CheckState newCheckState = (Qt::CheckState)value.toInt();
checkedState = newCheckState;
if(isFileName){
if(newCheckState == Qt::Unchecked || newCheckState == Qt::Checked){
for(int i = 0; i<rowCount(); i++){
DescriptionFileItem* child = (DescriptionFileItem*)QStandardItem::child(i);
QModelIndex childIndex = child->index();
child->model()->setData(childIndex,newCheckState, Qt::CheckStateRole);
//child->setCheckState(newCheckState);
//child->setData(newCheckState,Qt::CheckStateRole);
}
/*if(rowCount()>1){
emit this->model()->dataChanged(this->child(0)->index(),this->child(rowCount()-1)->index());
}else{
emit this->model()->dataChanged(this->child(0)->index(),this->child(0)->index());
}*/
}
}else{
emit CheckStateChanged();
}
//emit this->model()->dataChanged(this->index(),this->index());
}else{
QStandardItem::setData(value,role);
}
}
QVariant DescriptionFileItem::data( int role ) const{
if (role == Qt::CheckStateRole){
return checkedState;
}
return QStandardItem::data(role);
}
void DescriptionFileItem::UpdateCheckedStateOnChildStateChanged()
{
Qt::CheckState min = Qt::Checked;
Qt::CheckState max = Qt::Unchecked;
Qt::CheckState childState;
for(int i = 0; i<rowCount(); i++){
DescriptionFileItem* child = (DescriptionFileItem*)QStandardItem::child(i);
childState = (Qt::CheckState) child->data(Qt::CheckStateRole).toInt();
min = min>childState ? childState: min;
max = max<childState ? childState: max;
}
if(min >= max)
setData(min, Qt::CheckStateRole);
else
setData(Qt::PartiallyChecked, Qt::CheckStateRole);
}
And the construction of the connection / tree:
DescriptionFileItem* descFileStdItem = new DescriptionFileItem(descriptionFileName, true);
descFileStdItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsTristate);
descriptionFileSIModel.appendRow(descFileStdItem);
typedef pair<string,int> indexType;
foreach(indexType index,dataFile->indexes){
DescriptionFileItem* key_xItem = new DescriptionFileItem(index.first.c_str());
descFileStdItem->appendRow(key_xItem);
key_xItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
QObject::connect(key_xItem,SIGNAL(CheckStateChanged()),descFileStdItem,SLOT(UpdateCheckedStateOnModelDataChanged()));
}
EDIT: final answer, thanks to stu (see below)
void DataLoadWidget::ModelItemChanged(QStandardItem *item)
{
QStandardItem* parent = item->parent();
if(parent == 0){
//folder state changed--> update children if not partially selected
Qt::CheckState newState = item->checkState();
if(newState != Qt::PartiallyChecked){
for (int i = 0; i < item->rowCount(); i++)
{
item->child(i)->setCheckState(newState);
}
}
}
else{//child item changed--> count parent's children that are checked
int checkCount = 0;
for (int i = 0; i < parent->rowCount(); i++)
{
if (parent->child(i)->checkState() == Qt::Checked)
checkCount++;
}
if(checkCount == 0)
parent->setCheckState(Qt::Unchecked);
else if (checkCount == parent->rowCount())
parent->setCheckState(Qt::Checked);
else
parent->setCheckState(Qt::PartiallyChecked);
}
}
Unless I've misunderstood your question it seems that your solution is massively over-complicated. You should be able to do this trivially with the default QStandardItemModel implementation.
How about something like this (error handling omitted)?
QObject::connect(model, SIGNAL(itemChanged(QStandardItem*)), someObject, SLOT(modelItemChanged(QStandardItem*)));
And then in the signal handler:
void modelItemChanged(QStandardItem* item)
{
QStandardItem* parent = item->parent();
int checkCount = 0;
int rowCount = parent->rowCount();
for (int i = 0; i < rowCount; i++)
{
if (parent->child(i)->checkState() == Qt::Checked)
checkCount++;
}
switch (checkCount)
{
case 0:
parent->setCheckState(Qt::Unchecked);
break;
case rowCount:
parent->setCheckState(Qt::Checked);
break;
default:
parent->setCheckState(Qt::PartiallyChecked);
}
}
This is by no means optimal but it may be good enough for your purposes.