My application is crashing with a BAD_ACCESS when quitting and when clearing the QTreeWidget.
This is how I'm populating the first level of the tree:
std::set<UrlItem>::iterator i;
for(i = crawler->getUrls()->begin() ; i != crawler->getUrls()->end() ; i++) {
QList<QString> cells;
cells.append(i->url);
cells.append(i->httpStatusMessage);
cells.append(QString("%1").arg(i->statusCode));
QTreeWidgetItem *item = new QTreeWidgetItem(ui->resultTreeView, QStringList(cells));
ui->resultTreeView->addTopLevelItem(item);
}
I believe that the header item is causing the crash:
ui->resultTreeView->setHeaderItem(new QTreeWidgetItem(ui->resultTreeView, QStringList(headers)));
What am I doing to cause this crash? The item that is dynamically allocated has the tree widget as it's parent so it should only be destroyed when the tree widget is.
It seems I was setting the header the wrong way.
This works fine:
QList<QString> headers;
headers.append(tr("Url"));
headers.append(tr("Message"));
headers.append(tr("Status code"));
ui->resultTreeView->setHeaderLabels(QStringList(headers));
Now, what setHeaderItem was supposed to do and why it crashed my application I don't know but the code above achieved the desired effect.
Related
I've searched many places and found lots of interesting information, but none of that seems to work for what I want. I've tried to follow the solution shown at https://stackoverflow.com/a/9986293/11035837 to no avail.
Basics of my structure: I have a QTreeWidget. I add top level QTreeWidgetItems dynamically (upon the push of a button in a header button box). Each top level QTreeWidgetItem then gets other widgets added to it using:
QTreeWidget* treeWidget = new QTreeWidget;
QTreeWidgetItem* new_record = new QTreeWidgetItem;
QPushButton* add_child = new QPushButton;
QLineEdit* user_input = new QLineEdit;
treeWidget->setItemWidget(new_record,1,add_child);
treeWidget->setItemWidget(new_record,2,user_input);
The add_child button works perfectly. I have a display that inserts all my QLabels, QLineEdits, and QPushButtons in a tree tiered fashion. My buttons work for adding and removing the visual display of items even triggering the visibility of various other elements.
However, I cannot get the user input data out of the QLineEdits to process for anything (such as writing to an output file).
I have my output function iterate through the QTreeWidget:
QTreeWidgetItemIterator iter(treeWidget);
while (*iter)
{
stream.writeStartElement("record");
if ((*iter) != nullptr)
{
for (int i = 0; i < 12; i++)
{
if((*iter)->text(i) != nullptr) stream.writeAttribute("record_name", (*iter)->text(i));
}
}
for (int i = 0; i < 12; i++)
{
if ((*iter) != nullptr && (*iter)->child(i) != nullptr)
{
for (int j = 0; j < 12; j++)
{
if ((*iter)->child(i)->text(j) != nullptr) stream.writeAttribute("record_name", (*iter)->child(i)->text(j));
}
}
}
++iter;
}
This prints as many records with record_name displayed as were created, but it doesn't display any of the other data, because the pointer defined by (*iter)->child(i) is nullptr regardless of i
I then tried using data();
stream.writeAttribute("record_name ", (*iter)->data(2, Qt::UserRole).toString());
This doesn't err out because of nullptr, but it prints out record_name="" rather than record_name="<user_input>"
I'm able to get the user input for QLineEdit widgets that are not in the QTreeWidget, just not the ones in the tree. I assume if I can figure out how to get the data out of the QLineEdits within the tree that I should be able to adapt that to getting the QLineEdits out of a custom QWidget also within the tree.
I found a solution using information from https://www.qtcentre.org/threads/23228-typecast-of-QWidget
stream.writeAttribute("record_name", qobject_cast<QLineEdit*>(treeWidget->itemWidget((*iter), 2))->text() );
The issue was that the QLineEdit widgets were being recalled as QWidgets and not as QLineEdit widgets and as such the text() method/function was not available to it until I cast it back as the desired type.
I have a delete button(QPushButton) in the last column of each row of my table view. I am creating these push buttons and directly setting them in view. Since I have allocated memory dynamically I wish to free this memory but I haven't stored pointers of these buttons anywhere so I am trying to obtain the widget at the time of clean up and deleting them.
SDelegate* myDelegate;
myDelegate = new SDelegate();
STableModel* model = new STableModel(1, 7, this);
myWindow->tableView->setModel(model);
myWindow->tableView->setItemDelegate(myDelegate);
for(int i = 0; i < no_of_rows; ++i) {
QPushButton* deleteButton = new QPushButton();
myWindow->tableView->setIndexWidget(model->index(i, 6), deleteButton);
}
exec();
// Cleanup
for(int i = 0; i < no_of_rows; ++i) {
// code works fine on removing this particular section
QWidget* widget = myWindow->tableView->indexWidget(model->index(i, 6));
if (widget)
delete widget;
}
delete model;
delete myDelegate;
I am getting a crash in qt5cored.dll (Unhandled exception) and application is crashing in qcoreapplication.h at the following code:
#ifndef QT_NO_QOBJECT
inline bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{ if (event) event->spont = false; return self ? self->notifyInternal(receiver, event) : false; }
While debugging there is no issue in deleting these widgets but code crashes afterwards at some other point. I am using QTableView and custom class for model which has inherited QAbstractTableModel.
There's a Qt bug that manifests as follow: if there are any index widgets, and you invoke setModel(nullptr) on the view, it'll crash in an assertion on visualRow != -1 in qtableview.cpp:1625 (in Qt 5.6.0). Presumably this bug could be triggered when the model is being removed in some other fashion too.
But I can't reproduce it by merely destroying the model instance. So I doubt that it's relevant here unless you get the same assertion failure.
Given the style of your code, it's more likely that you have a memory bug elsewhere. If you think that the code above is crashing, you should have a self-contained test case that demonstrates the crash. Is your model or delegate to blame? Would it crash using no delegate? Would it crash using a stock model?
Your code excerpt seems to be fine, if mostly unnecessary. You could allocate the delegate and the model locally. The buttons are owned by the view: as soon as the need for the buttons goes away, such as when the model changes the row count or goes away, they will get appropriately deleted. So you don't have to delete them yourself, it's safe but completely unnecessary.
Here's an example that demonstrates that in all cases, the buttons will get disposed when the model gets destroyed or the view gets destroyed, whichever comes first. Tracking object lifetime is super simple in Qt: keep a set of objects, and remove them from the set using a functor attached to the object's destroyed signal. In Qt 4 you'd use a helper class with a slot.
// https://github.com/KubaO/stackoverflown/tree/master/questions/model-indexwidget-del-38796375
#include <QtWidgets>
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QSet<QObject*> live;
{
QDialog dialog;
QVBoxLayout layout{&dialog};
QTableView view;
QPushButton clear{"Clear"};
layout.addWidget(&view);
layout.addWidget(&clear);
QScopedPointer<QStringListModel> model{new QStringListModel{&dialog}};
model->setStringList(QStringList{"a", "b", "c"});
view.setModel(model.data());
for (int i = 0; i < model->rowCount(); ++i) {
auto deleteButton = new QPushButton;
view.setIndexWidget(model->index(i), deleteButton);
live.insert(deleteButton);
QObject::connect(deleteButton, &QObject::destroyed, [&](QObject* obj) {
live.remove(obj); });
}
QObject::connect(&clear, &QPushButton::clicked, [&]{ model.reset(); });
dialog.exec();
Q_ASSERT(model || live.isEmpty());
}
Q_ASSERT(live.isEmpty());
}
Check out QObject::deleteLater() , it usually helps with issues around deleting QObjects / QWidgets.
When I press a key there shall be a query to an engine. The results get put into a QListWidget by adding an item and setting the widget. Somehow this causes a massive memory overflow and even crashed my machine. But I dont get the error. Does clear() not delete the items passed to the QListWidget and the widgets set by setItemWidget(). I even tried to delete them on my own (comment), but still got a memoryleak. The error is in the if (!results.empty())-block, I guess, since commenting it out plugs the memoryleak.
void Widget::onTextEdited(const QString & text)
{
// QListWidgetItem * takenItem;
// while (takenItem = _results->takeItem(0)){
// delete _results->itemWidget(takenItem);
// delete takenItem;
// }
_results->clear(); _results->hide();
if (!text.isEmpty())
{
const std::vector<const Items::AbstractItem *> results = _engine.request(text);
if (!results.empty())
{
for (auto i : results){
QListWidgetItem *lwi = new QListWidgetItem;
_results->addItem(lwi);
ListItemWidget *w = new ListItemWidget;
w->setName(i->name());
w->setTooltip(i->path());
_results->setItemWidget(lwi, w);
}
_results->setFixedHeight(std::min(5,_results->count()) * 48); // TODO
_results->show();
}
}
this->adjustSize();
}
You should definitely use a memory leak detection tool instead of guessing around :)
UPDATE: clear() only deletes items but does not delete the widgets belonging to it. The widgets will be deleted if the QListWidget is deleted.
clear() does delete items and widgets belonging to it. And you mentioned that commenting out if(!results.empty()) solved the problem. I don't see any problem in the setItemWidget part. So I think the problem lies somewhere else, maybe ListItemWidget. How about you try replacing ListItemWidget with QLabel and see what happens. Eg:
QListWidgetItem *lwi = new QListWidgetItem;
_results->addItem(lwi);
//ListItemWidget *w = new ListItemWidget;
//w->setName(i->name());
//w->setTooltip(i->path());
QLabel *w = new QLabel;
w->setText("Hello");
_results->setItemWidget(lwi, w);
In my project, I have a listWidget. When the user clicks an item in the list, it loads this:
void BlockSelect::on_blockList_clicked(const QModelIndex &index)
{
QString blockListName;
QString temp_hex;
QString temp_hex2;
int temp_int;
QListWidgetItem *newitem = ui->blockList->currentItem();
blockListName = newitem->text();
temp_hex = blockListName.mid(0, blockListName.indexOf(" "));
if(temp_hex.indexOf(":") == -1)
{
temp_int = temp_hex.toInt();
ui->blockIdIn->setValue(temp_int);
ui->damageIdIn = 0;
}
else
{
temp_hex2 = temp_hex.mid(temp_hex.indexOf(":")+1, temp_hex.length()-(temp_hex.indexOf(":")+1));
temp_hex = temp_hex.mid(0, temp_hex.indexOf(":"));
temp_int = temp_hex.toInt();
ui->blockIdIn->setValue(temp_int);
temp_int = temp_hex2.toInt();
ui->damageIdIn->setValue(temp_int);
}
}
Most of this is just string manipulation. (You don't have need to study this syntax or anything)
My problem is, when the user clicks on another list item quickly (Before this current process is finished) the program crashes. Is there any way to allow fast clicks (multiple processes at once) or maybe an alternative solution?
Thanks for your time :)
I hope that you execute all this code in the GUI thread. If so, then there will be no problem - if your code were correct (it isn't). There is no such thing as the "process" that you mention in your question. The clicks are handled by a slot, and they are invoked from an event handler within the list. This is not supposed to crash, and the clicks will be handled in a serialized fashion - one after another.
Here's the bug: Why do you reset the value of an allocated UI pointer element to zero?
ui->damageIdIn = 0;
This is nonsense. Maybe you mean to ui->damageIdIn->setValue(0) or ui->damageIdIn->hide(). You then proceed to use this zero value in
ui->damageIdIn->setValue(temp_int);
and it crashes.
You may also have bugs in other places in your code.
I'm having a really frustrating problem, trying to delete qgraphicsitems in my application. I have a menu controller which is responsible for adding buttons to a layout and adding them to the scene. These buttons are all connected with custom signals and slots. When I change states, I want to delete this controller and remove all of these qgraphicsitems.
Heres how I add them in my menu_controller.cpp:
QGraphicsWidget * temp;//this is used during iteration to add to the layout
this->layout = new QGraphicsLinearLayout(Qt::Vertical);//q graphics view layout
this->menu = new QGraphicsWidget;//holds the layout
// initialize the proper buttons
(this->game_state->is_logged_in()) ? (this->logged_in()) : (this->not_logged_in());//test whether or not the user is logged in to generate the correct menu
// now iterate through each button and add to the layout
for (int i = 0, z = this->buttons.size(); i < z; i++) {
temp = this->scene->addWidget(this->buttons[i]);//add widget to the scene
this->layout->addItem(temp);//add this widget to the layou
connect(this->buttons[i], SIGNAL(menu_selection(QString)), this, SLOT(set_menu_option(QString)));//connect the button to this
}
// set menu layout as the layout and then add the menu to the scene
this->menu->setLayout(this->layout);
this->position();
this->scene->addItem(this->menu);
Finally, my destructor looks like this:
QGraphicsScene * scene = this->game_state->get_scene();
QList<QGraphicsItem *> list = scene->items();
QList<QGraphicsItem *>::Iterator it = list.begin();
for (; it != list.end(); ++it)
if (*it)
scene->removeItem(*it);
for (int i = 0, z = this->buttons.size(); i < z; i++)
disconnect(this->buttons[i], 0, 0, 0);//button not connected to anything
// for each deletes each place in memory
for_each(this->buttons.begin(), this->buttons.end(), utilities::delete_ptr());
delete this->layout;//delete the layout container
delete this->menu;//delete the menu
I remove each of the buttons from the scene, disconnect the connected buttons and then try to call delete on them.
I get a segmentation fault each time. The scene items remove fine, and the disconnects work properly, but for some reason when I delete the items, it throws a segmentation fault and crashes the program.
My guess is there's something wrong in your utilities::delete_ptr().
But anyway. There's no need to disconnect the signal if you are deleting either the sender or receiver. That's automatically done when one of them is deleted.
There's also no need to go through the whole list of items in a scene and delete them. Calling QGraphicsScene::clear() will do. And even that is not necessary of you are deleting the scene anyway.
Thanks for the assistance.
What was causing the segmentation fault was the fact that the widgets were connected with signals and therefore needed to be deleted with the deleteLater() method.
It seems that deleting an element signals other widgets and when this occurred, it could not find the place an memory and thus called a seg fault ..