Segfault iterating over a QList - qt

I've created two classes - one called circle:
class circle
{
public:
circle();
QString name ;
int id ;
};
and another class that use this class:
class soso
{
public:
soso();
QList<circle*> lis;
void go();
};
In the constructor of soso I add two circles:
soso::soso()
{
circle* c1 = new circle();
circle* c2= new circle();
c1->id=1;
c1->name="yamen";
c2->id=2;
c2->name="hasan";
lis.append(c1);
lis.append(c2);
}
and in the main window i've called go method which is included here
void soso::go()
{
QFile file("database.txt");
if(!file.open(QIODevice::WriteOnly))
throw " cannot open file ! ";
QDataStream out(&file);
int i=0;
QList<circle*>::iterator it1 =lis.begin();
for(i=0;it1!=lis.end();it1++);
{
out<<(*it1)->id; // segmentation error here
out<<(*it1)->name;
}
}
But I am getting a segmentation error. What am I doing wrong?

You have a semicolon after your for loop! Those are really hard to notice.
for(i=0;it1!=lis.end();it1++);
This works. Just changed it to look more like standard use of iterators:
QList<circle*>::iterator it1;
for(it1 = lis.begin();it1!=lis.end();it1++)
{
out<<(*it1)->id;
out<<(*it1)->name;
}

Related

Subclassing QTreeWidgetItem

I am trying to subclass QTreeWidgetItem so I can fill-in class with data I need.
Went through all the code and couldn't find one complete example on how to do it right.
In my implementation something is wrong because application won't start up, but if I do it with QTreeWidgetItem then it works without any problems.
So I have created subclass as H file:
#ifndef XITEM_H
#define XITEM_H
#include <QTreeWidget>
class XItem : public QObject,public QTreeWidgetItem
{
Q_OBJECT
public:
XItem ();
void setText(int column, const QString &atext);
void addChild(QTreeWidgetItem *child);
};
#endif
And C file:
#include "X.h"
XItem ::XItem (): QTreeWidgetItem(UserType)
{
}
void XItem::setText(int column, const QString &atext){
setText(column,atext);
}
void XItem::addChild(QTreeWidgetItem *child){
addChild(child);
}
And code that runs it all:
QTreeWidget * tree = ui->treeWidget;
QTreeWidgetItem * topLevel = new QTreeWidgetItem();
topLevel->setText(0, "This is top level");
for(int i=0; i<5; i++)
{
//QTreeWidgetItem * item = new QTreeWidgetItem();
XItem *item = new XItem();
item->setText(0,"item " + QString::number(i+1));
topLevel->addChild(item);
}
tree->addTopLevelItem(topLevel);
If I run it like this application hangs and if I comment:
XItem *item = new XItem();
and un-comment:
QTreeWidgetItem * item = new QTreeWidgetItem();
it works.
Could anyone help out on this please.
Kind regards!
Anyway... after all years still simple mistake as this one can come along long hours coding.
ERROR:
void XItem::setText(int column, const QString &atext){
setText(column,atext);
}
void XItem::addChild(QTreeWidgetItem *child){
addChild(child);
}
Should be:
void XItem::setText(int column, const QString &atext){
QTreeWidgetItem::setText(column,atext);
}
void XItem::addChild(QTreeWidgetItem *child){
QTreeWidgetItem::addChild(child);
}

Custom listModel does not notify the view

I have my custom list model where I put data which should be displayed on the QML view. But for some kind of reason the view in QML sometimes is updated normally, sometimes with previous data and sometimes updating is not performed.
Here is the function where I fill the model - this function is called from some other thread.
void MyScreen::fillListModel()
{
const QString SEPARATOR = " ";
myListModel->resetModel();
for (int i = 0; i < MAX_ROWS; ++i)
{
QString key = QString::fromUtf16(MyData::getParameterKey(i).c_str());
QString val = QString::fromUtf16(MyData::getParameterVal(i).c_str());
myListModel->addItem(key + SEPARATOR + val);
}
}
Implementation of model reset:
void BrowsingModelBase::resetModel()
{
beginResetModel();
m_items.clear();
endResetModel();
}
Implementation of addItem():
void BrowsingModelBase::addItem(const BrowsingItemModelBase &item)
{
int count = m_items.size();
beginInsertRows(QModelIndex(), count, count);
m_items.append(item);
endInsertRows();
}
Finally my QML file:
MyScreen {
Column {
id: myFlowList
y: 110
x: 220
ListView {
height:1000
spacing: 35;
model: myListModelRoot.myListModel
delegate: Text {
text: text1
}
}
}
}
The strange thing is that after loop with line
myListModel->addItem(key + SEPARATOR + val);
when I print logs with data from myListModel it is filled with proper data, but the view is usually updated with previous data. Is it possible that data change signal is stuck somewhere? Any idea what is the solution?
Assuming that you call your model's methods from another thread, with the model being fundamentally not thread-safe, you have two options:
Make certain methods of your model thread-safe, or
Explicitly invoke the methods in a thread-safe fashion.
But first, you'd gain a bit of performance by adding all the items at once, as a unit. That way, the model will emit only one signal for all of the rows, instead of one signal per row. The views will appreciate it very much.
class BrowsingModelBase {
...
};
Q_DECLARE_METATYPE(QList<BrowsingItemModelBase>)
void BrowsingModelBase::addItems(const QList<BrowsingItemModelBase> & items)
{
beginInsertRows(QModelIndex(), m_items.size(), m_items.size() + items.size() - 1);
m_items.append(items);
endInsertRows();
}
You should probably also have a method called clear instead of resetModel, since to reset a model has a much more general meaning: "change it so much that it's not worth emitting individual change signals". To reset a model does not mean "clear it"! Thus:
void BrowsingModelBase::clear()
{
beginResetModel();
m_items.clear();
endResetModel();
}
Finally, following the 2nd approach of safely invoking the model's methods, fillListModel becomes as follows. See this answer for discussion of postTo.
template <typename F>
void postTo(QObject * obj, F && fun) {
if (obj->thread() != QThread::currentThread()) {
QObject signalSource;
QObject::connect(&signalSource, &QObject::destroyed, obj, std::forward<F>(fun));
} else
fun();
}
void MyScreen::fillListModel()
{
auto separator = QStringLiteral(" ");
QList<BrowserItemModelBase> items;
for (int i = 0; i < MAX_ROWS; ++i) {
auto key = QString::fromUtf16(MyData::getParameterKey(i).c_str());
auto val = QString::fromUtf16(MyData::getParameterVal(i).c_str());
items << BrowserItemModelBase(key + separator + val);
}
postTo(myListModel, [this, items]{
myListModel->clear();
myListModel->addItems(items);
});
}
Alternatively, following the first approach, you can make the clear and addItems methods thread-safe:
/// This method is thread-safe.
void BrowsingModelBase::addItems(const QList<BrowsingItemModelBase> & items)
{
postTo(this, [this, items]{
beginInsertRows(QModelIndex(), m_items.size(), m_items.size() + items.size() - 1);
m_items.append(items);
endInsertRows();
});
}
/// This method is thread-safe.
void BrowsingModelBase::clear()
{
postTo(this, [this]{
beginResetModel();
m_items.clear();
endResetModel();
});
}
You then need no changes to fillListModel, except to make it use addItems:
void MyScreen::fillListModel()
{
auto separator = QStringLiteral(" ");
myListModel->clear();
QList<BrowserItemModelBase> items;
for (int i = 0; i < MAX_ROWS; ++i) {
auto key = QString::fromUtf16(MyData::getParameterKey(i).c_str());
auto val = QString::fromUtf16(MyData::getParameterVal(i).c_str());
items << BrowserItemModelBase(key + separator + val);
}
myListModel->addItems(items);
}
The problem is most likely the fact that you are calling the fillListModel() method not from the main GUI thread. You can update the model from other threads but the beginResetModel();, endResetModel();, beginInsertRows(QModelIndex(), count, count);... methods have to be called in the main GUI thread.
One way to call these method in GUI thread (maybe not the most efficient) is to :
Create signals for each method you want to call:
signals:
//these signals are emitted from worker thread
void requestBeginResetModel();
void requestEndResetModel();
Create slots that will actually call the methods:
private slots:
//these slots execute the model reset operations in main thread
void callBeginResetModel();
void callEndResetModel();
Connect the signals and the slots:
//connect the appropriate signals
connect(this, SIGNAL(requestBeginResetModel()),
this, SLOT(callBeginResetModel()));
connect(this, SIGNAL(requestEndResetModel()),
this, SLOT(callEndResetModel()));
Your reset model will then be:
void BrowsingModelBase::resetModel()
{
emit requestBeginResetModel();
m_items.clear();
emit requestEndResetModel();
}
Finally the slots are implemented as:
void ObjectModel::callBeginResetModel()
{
beginResetModel();
}
void ObjectModel::callEndResetModel()
{
endResetModel();
}
Note that you will have to do the same for row insert methods as well. Or alternatively you could fill you model in the resetModel() method in between emitted signals.

Qt almost same main menu entries

I'm using c++ Qt library and I want to do something which would do :
connect(actionB11, SIGNAL(triggered()), this, SLOT(SetSomething(1, 1)));
connect(actionB12, SIGNAL(triggered()), this, SLOT(SetSomething(1, 2)));
connect(actionB21, SIGNAL(triggered()), this, SLOT(SetSomething(2, 1)));
connect(actionB22, SIGNAL(triggered()), this, SLOT(SetSomething(2, 2)));
The code above doesnt work because SIGNAL function has to have same number and argument types as SLOT function.
Does exist a way how to do it? I dont want to have about 20 function as SetSomething11, SetSomething12 calling SetSomething(1, 1) etc.
In situations like this you have three simple options:
connect each QAction to its own slot (not good)
use a QSignalMapper
add each QAction to a QActionGroup and use the QActionGroup::triggered(QAction*) signal, coupled with setting each QAction's data (see QAction::setData() and QAction::data())
When you set the data for a QAction, you can only store one QVariant (i.e., one value). So if you want two values, I would recommend just creating a simple mapping, like this:
void Window::onActionGroupTriggered(QAction *action);
{
int i = action->data().toInt();
int a, b;
a = i / 10;
b = i - 10;
setSomething(a, b); // for example if i = 15, then a = 1 and b = 5
}
You may modify QAction class.
class QMyAction : public QAction
{
Q_OBJECT
QMyAction ( QObject * parent ) :
QAction(parent), _x(0), _y(0)
{
connect(this, SIGNAL(triggered(bool)), this, SLOT(re_trigger(bool)));
}
QMyAction ( const QString & text, QObject * parent ) :
QAction (text, parent), _x(0), _y(0)
{
connect(this, SIGNAL(triggered(bool)), this, SLOT(re_trigger(bool)));
}
QMyAction ( const QIcon & icon, const QString & text, QObject * parent ) :
QAction(icon, text, parent), _x(0), _y(0)
{
connect(this, SIGNAL(triggered(bool)), this, SLOT(re_trigger(bool)));
}
void setX(int x)
{
_x = x;
}
int getX()
{
return _x;
}
void setY(int y)
{
_y = y;
}
int getY()
{
return _y;
}
public slots:
void re_trigger(bool)
{
emit triggered(_x, _y);
}
signals:
void triggered(int,int);
private:
int _x;
int _y;
};
Now, you can connect triggered(int,int) to SetSomething(int,int). But, you have to set x and y. Unless, they will always be 0.
You cannot use constant in SLOT signature, you have to use types there. When connecting signal to slot the slot must have the same subset of parameters signal has, otherwise they cannot be connected and QObject::connect() will return false.
connect(actionB11, SIGNAL(triggered()),
this, SLOT(SetSomething()));
This slot takes no parameters, but you can use QObject::sender() to get pointer to the object, which emitted the signal. Then this pointer can be used to discriminate the source of the signal:
void SetSomething() {
switch(sender()) {
case actionB11;
// do something
break;
case actionB12;
// do something
break;
case actionB21;
// do something
break;
case actionB22;
// do something
break;
default:
// Exceptional situation
}
}
Alternatively you can use QSignalMapper to append additional discriminating parameters to slots.

multiple signals for one slot

For my GUI i would like to have two pairs of buttons that scroll up and down a scrollarea. The first set of buttons should work on say scrollarea1 and the second set of buttons should work on a scrollarea2. The widgets that I put in the scrollarea are called viewport1 and viewport2.
Since both both set of buttons should do the same (scrolling up and down) I thought I would make two slots called scrollUp and scrollDown that would handle the scrolling for both sets of buttons. Unfortunately I cannot make this work and need some help. I have tried the following:
QPushButton up;
QPushButton down;
QPushButton up2;
QPushButton down2;
connect(&up,SIGNAL(clicked()),&up,SLOT(scrollUp()));
connect(&up2,SIGNAL(clicked()),&up,SLOT(scrollUp()));
connect(&down,SIGNAL(clicked()),&down,SLOT(scrollDown()));
connect(&down2,SIGNAL(clicked()),&down,SLOT(scrollDown()));
void MainWindow::scrollDown()
{
QScrollArea area;
QWidget view;
if((QPushButton) &sender = down)
{
area=scrollArea;
view=viewport;
}
if((QPushButton) &sender = down2)
{
area=scrollArea;
view=viewport;
}
int curpos = area.verticalScrollBar()->value();
area.verticalScrollBar()->setValue(curpos+15);
int newpos = area.verticalScrollBar()->value();
QPoint topLeft = area.viewport()->rect().topLeft();
view.move(topLeft.x(),topLeft.y()-(newpos));
}
void MainWindow::scrollUp()
{
QScrollArea area;
QWidget view;
if((QPushButton) &sender = up)
{
area=scrollArea;
view=viewport;
}
if((QPushButton) &sender = up2)
{
area=scrollArea2;
view=viewport2;
}
int curpos = area.verticalScrollBar()->value();
area.verticalScrollBar()->setValue(curpos-15);
int newpos = area.verticalScrollBar()->value();
QPoint topLeft = area.viewport()->rect().topLeft();
view.move(topLeft.x(),topLeft.y()-(newpos));
}
But this doesn´t work for several reasons. I also tried giving the slot some arguments, something like:
connect(&up,SIGNAL(clicked()),&up,SLOT(scrollUp(scrollarea1,viewport1)));
connect(&up2,SIGNAL(clicked()),&up,SLOT(scrollUp(scrollarea2,viewport2)));
But again, no succes. Can anybody help me?
First of all, "It doesn't work" does not mean anything, and it is hard to help you if you do not say what errors you get. Then, there are few problems.
All QObject's derived classes are not copiable, it means you can not do
QWidget a;
QWidget b;
b = a; // Wrong
You should use pointers (or perhaps references).
QWidget a;
QWidget * b = new QWidget(...);
QWidget * c;
c = & a; // Ok
c = b; // Ok
Then your connect calls are wrong:
connect(&up, SIGNAL(clicked()), &up, SLOT(scrollUp()));
The third argument is the object who has the slot. up is a QPushButton, it does not have a scrollUp() slot, it is your MainWindow who does:
connect(&up, SIGNAL(clicked()), this, SLOT(scrollUp()));
(since connect is called in MainWindow's constructor this points to the current MainWindow object).
Also in C++ the single = sign means assignment, for equality comparison use =='. Andsender` is a function.
Your approach should work if implemented in the right way:
class MainWindow: public QWidget
{
QScrollArea * scroll1;
QScrollArea * scroll2;
QWidget * view1;
QWidget * view2;
QPushButton * up1;
QPushButton * up2;
QPushButton * down1;
QPushButton * down2;
public:
MainWindow()
{
// Here initialize member variables.
...
connect(up1, SIGNAL(clicked()), this, SLOT(scrollUp()));
connect(up2, SIGNAL(clicked()), this, SLOT(scrollUp()));
connect(down1, SIGNAL(clicked()), this, SLOT(scrollDown()));
connect(down2, SIGNAL(clicked()), this, SLOT(scrollDown()));
}
public slots:
void scrollDown()
{
QScrollArea * area;
QWidget * view;
if(qobject_cast<QPushButton>(sender()) == down1) {
area = & scroll1;
view = & view1;
} else if(qobject_cast<QPushButton>(sender()) == down2) {
area = & scroll2;
view = & view2;
} else {
// Error.
}
// Now `area` and `view` point to the right widgets.
...
}
void scrollUp()
{
// The same as before.
}
};
Another approach would be to extract the actual scrolling instructions to a separate function:
class MainWindow: public QWidget
{
// Same variables as before
...
public:
MainWindow()
{
// Here initialize member variables.
...
connect(up1, SIGNAL(clicked()), this, SLOT(scrollUp1()));
connect(up2, SIGNAL(clicked()), this, SLOT(scrollUp2()));
connect(down1, SIGNAL(clicked()), this, SLOT(scrollDown1()));
connect(down2, SIGNAL(clicked()), this, SLOT(scrollDown2()));
}
public slots:
void scrollDown(QScrollArea * area, QWidget * view)
{
// Here you scroll over `area` and `view`.
}
void scrollDown1()
{
scrollDown(scroll1, area1);
}
void scrollDown2()
{
scrollDown(scroll2, area2);
}
// Again, the same for `scrollUp`.
};
There are several mistakes in your code :
About the sender of the signal : There is not a QObject called "sender" but a method QObject * QObject::sender() const; which returns a pointer on the sender of the signal.
In the if conditions : you are casting a QPushButton** into a QPushButton ((QPushButton) &sender) and you dont compare that thing with your buttons up(2) and down(2).
In your connections between slots and signals : the scrollUp and scrollDown slots do not belong to the QPushButton class but to your MainWindow class.
Finally, you should write something like this :
connect(&up, SIGNAL(clicked()), this, SLOT(scrollUp()));
connect(&up2, SIGNAL(clicked()), this, SLOT(scrollUp()));
connect(&down, SIGNAL(clicked()), this, SLOT(scrollDown()));
connect(&down2, SIGNAL(clicked()), this, SLOT(scrollDown()));
void MainVindow::scrollDown() {
// [...]
QPushButton * senderButton = qobject_cast<QPushButton *>(this->sender());
// QPushButton * senderButton = (QPushButton *) this->sender(); works too
if (senderButton == &down) {
// [...]
}
if (senderButton == &down2) {
// [...]
}
// [...]
}
void MainVindow::scrollUp() {
// [...]
QPushButton * senderButton = qobject_cast<QPushButton *>(this->sender());
// QPushButton * senderButton = (QPushButton *) this->sender(); works too
if (senderButton == &up) {
// [...]
}
if (senderButton == &up2) {
// [...]
}
// [...]
}
First of all the slot can have no other arguments than the signal hands to it. Clicked has no arguments and there fore the slot can have no arguments.
I would think that the easiest way to check whether scrollArea 1 or 2 has focus and decide from that which one should move.
I also think that there is an error in your code. Shouldn't this:
if((QPushButton) &sender = down2)
{
area=scrollArea;
view=viewport;
}
Be this:
if((QPushButton) &sender = down2)
{
area=scrollArea2;
view=viewport2;
}
First of all, this is pseudo code. It won't compile, but it should contain the necessary information.
I believe this problem can be most elegantly solved using the QSignalMapper class. It allows parameterless signals from multiple senders to connect to one slot.
In the header, write something like this:
class QSignalMapper;
class MainWindow : public QMainWindow
{
public:
void init();
public slots:
void handleScrollButtons(int id);
private:
enum { ScrollUp1, ScrollDown1, ScrollUp2, ScrollDown2 } // just makes it more convenient to use
QSignalMapper *m_scrollbuttonhandler;
}
In the source file, write something like this
#include <QSignalMapper>
void MainWindow::init()
{
m_scrollbuttonhandler = new QSignalMapper(this);
m_scrollbuttonhandler->setMapping(scrollup1button, ScrollUp1);
m_scrollbuttonhandler->setMapping(scrolldown1button, ScrollDown1);
m_scrollbuttonhandler->setMapping(scrollup2button, ScrollUp2);
m_scrollbuttonhandler->setMapping(scrolldown2button, ScrollDown2);
connect(scrollup1button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
connect(scrolldown1button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
connect(scrollup2button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
connect(scrolldown2button, SIGNAL(clicked(bool)), m_scrollbuttonhandler, SLOT(map()));
connect(m_scrollbuttonhandler, SIGNAL(mapped(int)), this, SLOT(handleScrollButtons(int)));
}
void MainWindow::handleScrollButtons(int id)
{
switch (id)
{
case ScrollUp1:
// stuff to do for scrollup1button
case ScrollDown1:
// stuff to do for scrolldown1button
case ScrollUp2:
// stuff to do for scrollup2button
case ScrollDown2:
// stuff to do for scrolldown2button
}
}

QDialog exec() (SEG FAULT)

My QDialog is causing a seg fault after it closes. I malloc my struct before using it, settings is of type PSETTINGS and is a private variable.
MainWindow Class:(Seg Fault happens in settingsDiag->exec())
Settings *settingsDiag = new Settings(this);
settingsDiag->exec();
Settings Class:
In header file:
typedef struct ConnSettings {
ConnSettings():ipAddr(""), alias("Local"), port(8000), isClient(false){}
QString ipAddr;
QString alias;
int port;
bool isClient;
} SETTINGS, *PSETTINGS;
In CPP file:
Settings::Settings(QWidget *parent) :
QDialog(parent),
ui(new Ui::Settings)
{
ui->setupUi(this);
QButtonGroup serviceGroup(ui->serviceBox);
QValidator *validPort = new QRegExpValidator(QRegExp("^\\d*$"), this);
QValidator *validIp = new QRegExpValidator(QRegExp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"), this);
serviceGroup.addButton(ui->clientButton);
serviceGroup.addButton(ui->serverButton);
connect(ui->okCancel, SIGNAL(accepted()), this, SLOT(storeSettings()));
connect(ui->clientButton, SIGNAL(toggled(bool)), this, SLOT(enableDisableClient(bool)));
ui->portText->setValidator(validPort);
ui->ipText->setValidator(validIp);
}
Settings::~Settings() {
delete ui;
}
void Settings::storeSettings() {
settings = (PSETTINGS)malloc(sizeof(SETTINGS));
settings->port = ui->portText->text().toInt();
if((settings->isClient = ui->clientButton->isChecked())) {
settings->ipAddr = ui->ipText->text();
settings->alias = ui->aliasText->text();
}
}
PSETTINGS Settings::getSettings() {
return settings;
}
void Settings::enableDisableClient(bool client) {
ui->clientBox->setEnabled(client);
}
Thanks :)
The malloc is causing problems too. The strings in that struct get used without being constructed. Assume your heap corrupted after that point.
Instead of malloc try...
settings = new SETTINGS();
... and of course delete when you are done with it.
You are allocating the QButtonGroup serviceGroup on the stack. Probably not what you want to do.

Resources