PDA

View Full Version : Having trouble clearing a QTreeWidget.



Nyphel
8th October 2007, 22:23
Hello,

I use Qt4.3.1 Open Source edition for Windows 32 bits (under Windows XP Pro SP2).

My application has a small interface based upon a QTreeWidget (named "tree") that displays many QTreeWidgetItem *.
When the user clic an item, the signal itemSelectionChanged() is emited. I've got a SLOT linked to this signal, so that I can analyse the item that has been clicked.
So consider the following code as my SLOT block :




With the following code : no problem.
The QTreeWidget is correctly cleared.


{
tree->clear();
}


void QTreeWidget::clear () [slot]
Clears the tree widget by removing all of its items and selections.
Note: Since each item is removed from the tree widget before being deleted, the return value of QTreeWidgetItem::treeWidget() will be invalid when called from an item's destructor.




With the following code : no problem.
The QString contains the informations of the first column of the firts selected item on the QTreeWidget.


{
QString item_content = ((tree->selectedItems()).at(0))->text(0);
}



QList<QTreeWidgetItem *> QTreeWidget::selectedItems () const
Returns a list of all selected non-hidden items.




With the following code : problem.
The application crashes, like it generally happens with memory errors...


{
QString item_content = ((tree->selectedItems()).at(0))->text(0);
tree->clear();
}




I don't really understand the problem, and I can't find any solution.
The API concerning QTreeWidget and QTreeWidgetItem manages lots of pointers, like the list returned by selectedItems(). Certainly should I get the selected items instead of pointers to the selected ones... But I can't achieve to do that.

May you help me, please ? :)

wysota
8th October 2007, 23:46
If the above slot is connected to a signal that emits a QTreeWidgetItem* parameter, you can't delete the object pointed by that parameter and that's what you do by calling clear() here. And make sure selectedItems() is not empty.

Nyphel
9th October 2007, 01:09
Hi Wysota !

I've tested the selectedItems() returned list to be sure that it's not empty, but I didn't mentionned that before. Moreover the signal itemSelectionChanged() emited by the QTreeWidget doesn't use any parameters...

:crying:

wysota
9th October 2007, 09:23
But the signal may be emited by another slot that takes the item as a parameter :) Either show us the backtrace or make a little test - instead of
tree->clear() call:

QTimer::singleShot(0, tree, SLOT(clear()));

Nyphel
9th October 2007, 11:58
Hi,

It appears that the call of clear() throught the Qtimer makes the application crash too :(



(I'm sorry for my bad English, I'm French and I do what I can to be understanded ^^)

wysota
9th October 2007, 13:35
In that case I need you to provide more code. Not only the method bodies, but also their prototypes, connect statements and class constructor.

Nyphel
9th October 2007, 15:12
The header file :



#ifndef interface_impl_h
#define interface_impl_h

#include "interface.h"
#include "Listeur.h"
#include "donnee.h"

#include <QObject>
#include <QMainWindow>
#include <QtGui>
#include <QUrl>
#include <QDesktopServices>
#include <QFileInfoList>
#include <QFileDialog>
#include <QComboBox>
#include <QList>
#include <QFile>
#include <QDir>
#include <QString>
#include <QPixmap>

class interface_impl : public QMainWindow, public Ui::MainWindow_interface
{
Q_OBJECT

public:
interface_impl(Listeur *appelant = 0, QWidget *parent = 0);
void resizer_tableaux();
void Enregistrer_config();

protected:
void showEvent(QShowEvent *event);
void closeEvent(QCloseEvent *event);

private slots:
void SLOT_ajouter_repertoire_scan();
void SLOT_supprimer_repertoire_scan();
void SLOT_rechercher_repertoire_scan();

void SLOT_ajouter_repertoire_noscan();
void SLOT_supprimer_repertoire_noscan();
void SLOT_rechercher_repertoire_noscan();

void SLOT_actualiser_liste_films();
void SLOT_lancer_le_film(QTreeWidgetItem *, int);

void SLOT_selection_changed();
void SLOT_ouvrir_selection();
void SLOT_lancer_selection();

void SLOT_rechercher_synopsis_jaquettes_ALL();
void SLOT_actualiser_synopsis_jaquettes_AC();
void SLOT_actualiser_synopsis_jaquettes_QLCS();
void SLOT_actualiser_sysnopsis_jaquettes_VC();
void SLOT_actualiser_jaquettes_MC();

private:
void Masquer_checkBoxes();
void Nettoyer_texte_checkBoxes();
void MAJ_checkBoxes_racines();
void MAJ_listes_repertoires();
void Charger_config();
void MAJ_checkBoxes_racines_selon_config(QString, QString); // Texte, valeur activation

QString get_filename_selection();
QString get_filepath_selection();

Listeur *Listeur_appelant;
int NB_ligne_tableau_recherche_repertoire;

QList<QTreeWidgetItem *> liste_selection;
};

#endif // interface_impl_h

Nyphel
9th October 2007, 15:14
The CPP file :



#include <fstream>
#include <iostream>
using namespace std;

#include "interface_impl.h"

interface_impl::interface_impl(Listeur *appelant, QWidget *parent) : QMainWindow(parent)
{
cout << "interface_impl" << endl;
setupUi(this);

Listeur_appelant = appelant;

Masquer_checkBoxes();
MAJ_checkBoxes_racines();

connect(bouton_scan_plus_rep, SIGNAL(clicked()), this, SLOT(SLOT_ajouter_repertoire_scan()));
connect(bouton_scan_moins_rep, SIGNAL(clicked()), this, SLOT(SLOT_supprimer_repertoire_scan()));
connect(bouton_noscan_plus_rep, SIGNAL(clicked()), this, SLOT(SLOT_ajouter_repertoire_noscan()));
connect(bouton_noscan_moins_rep, SIGNAL(clicked()), this, SLOT(SLOT_supprimer_repertoire_noscan()));
connect(bouton_lister, SIGNAL(clicked()), this, SLOT(SLOT_actualiser_liste_films()));

connect(bouton_rechercher, SIGNAL(clicked()), this, SLOT(SLOT_rechercher_synopsis_jaquettes_ALL()));
//connect(bouton_AC, SIGNAL(clicked()), this, SLOT(SLOT_actualiser_synopsis_jaquettes_AC()));
//connect(bouton_QLCS, SIGNAL(clicked()), this, SLOT(SLOT_actualiser_synopsis_jaquettes_QLCS()));
//connect(bouton_VC, SIGNAL(clicked()), this, SLOT(SLOT_actualiser_sysnopsis_jaquettes_VC()));
//connect(bouton_MC, SIGNAL(clicked()), this, SLOT(SLOT_actualiser_jaquettes_MC()));

connect(tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(SLOT_lancer_le_film(QTreeWidgetItem*, int)));

connect(tree, SIGNAL(itemSelectionChanged()), this, SLOT(SLOT_selection_changed()));
connect(bouton_ouvrir, SIGNAL(clicked()), this, SLOT(SLOT_ouvrir_selection()));
connect(bouton_lancer, SIGNAL(clicked()), this, SLOT(SLOT_lancer_selection()));

Charger_config();
}

void interface_impl::SLOT_rechercher_synopsis_jaquettes _ALL()
{
Listeur_appelant->Rechercher_jaquettes_synopsis();
}

void interface_impl::SLOT_actualiser_synopsis_jaquettes _AC()
{
Listeur_appelant->Utiliser_HTTP_AC();
}

void interface_impl::SLOT_actualiser_synopsis_jaquettes _QLCS()
{
Listeur_appelant->Utiliser_HTTP_QLCS();
}

void interface_impl::SLOT_actualiser_sysnopsis_jaquette s_VC()
{
Listeur_appelant->Utiliser_HTTP_VC();
}

void interface_impl::SLOT_actualiser_jaquettes_MC()
{
Listeur_appelant->Utiliser_HTTP_MC();
}

void interface_impl::SLOT_selection_changed()
{
cout << "\n\nSLOT_selection_changed" << endl;

liste_selection.clear();
//liste_selection = tree->selectedItems();

// ----------------------

// QTreeWidgetItem *item;
QString item_name;
// QString item_path;
// QDir mon_dossier_cible;
// QFile mon_fichier_cible;
// QUrl mon_url;
// bool isDir;
// bool isFile;

// item = liste_selection.at(0);

// item_name = item->text(0);
// item_path = item->text(1);

item_name = ((tree->selectedItems()).at(0))->text(0);

tree->clear();
//QTimer::singleShot(0, tree, SLOT(clear()));


// mon_dossier_cible.setPath(item_path);
// mon_fichier_cible.setFileName(item_path);
// mon_url = QUrl::fromLocalFile(item_path);
// isDir = mon_dossier_cible.exists();
// isFile = mon_fichier_cible.exists();
//
// // L'objet sélectionné n'est pas un dossier
// if (isDir == false)
// {
// // Par contre c'est bien un fichier, donc un film
// if (isFile == true)
// {
// // On reconstruit le chemin d'accès au synopsis
// QString acces_synopsys = "./Synopsis/";
// acces_synopsys.append(item_name);
// acces_synopsys.append(".html");
//
// // On crée l'URL qui sera utilisée par le QTextBroxser de synopsis
// QUrl html_url;
// html_url = QUrl::fromLocalFile(acces_synopsys);
//
// // On met à jour le QTextBrowser du synopsis
// TB_Synopsis->setSource(html_url);
//
// // On reconstruit le nom complet de la jaquette (avec extension)
// QString item_name_stored = "";
// QDir dossier_synopsis("Jaquettes");
// QFileInfoList synopsis_fileInfoList = dossier_synopsis.entryInfoList();
// for(int i = 0; i < synopsis_fileInfoList.size(); ++i)
// {
// if(synopsis_fileInfoList.at(i).isFile()) // On peut avoir des DIR
// {
// if (synopsis_fileInfoList.at(i).baseName() == item_name) // Nom sans extension
// {
// item_name_stored = synopsis_fileInfoList.at(i).fileName(); // Nom avec extension
// i = synopsis_fileInfoList.size(); // Fin de parcours
// }
// }
// }
//
// if (item_name_stored == "") // La recherche n'a pas aboutit, ne devrait pas survenir
// {
// item_name_stored .append(item_name);
// item_name_stored .append(".jpg");
// }
//
// // On reconstruit le chemin d'accès à la jaquette
// QString acces_jaquette = "./Jaquettes/";
// acces_jaquette.append(item_name_stored);
//
// cout << "Accès jaquette : " << acces_jaquette.toStdString() << endl;
//
// // Affichage de l'image
// int hauteur_jaquette = (tree->height()) / 2; // 50% de la hauteur de l'interface
// QPixmap jaquette_pixmap;
// jaquette_pixmap.load(acces_jaquette); // Chargement de l'image dans le QPixmap
// jaquette_pixmap = jaquette_pixmap.scaledToHeight(hauteur_jaquette, Qt::SmoothTransformation); // Redimensionnement de l'image
// label_jaquette->setPixmap(jaquette_pixmap); // Affichage du QPixmap (et donc de l'image) dans le QLabel de l'interface
// }
// }
// else // L'objet sélectionné est un dossier
// {
// label_jaquette->clear();
// TB_Synopsis->clear();
// }
}

Nyphel
9th October 2007, 15:16
QString interface_impl::get_filename_selection()
{
QTreeWidgetItem *item;
QString item_name;

item = liste_selection.at(0);
item_name = item->text(0);

return item_name;
}

QString interface_impl::get_filepath_selection()
{
QTreeWidgetItem *item;
QString item_path;

item = liste_selection.at(0);
item_path = item->text(1);

return item_path;
}

void interface_impl::SLOT_ouvrir_selection()
{
cout << "SLOT_ouvrir_dossier" << endl;

QTreeWidgetItem *item;
QString item_path;
QDir mon_dossier_cible;
QFile mon_fichier_cible;
QUrl mon_url;
bool isDir;
bool isFile;
QStringList liste_dossiers_ouverts;

for (int i=0; i<liste_selection.size(); i++)
{
item = liste_selection.at(i);
item_path = item->text(1);
mon_dossier_cible.setPath(item_path);
mon_fichier_cible.setFileName(item_path);
mon_url = QUrl::fromLocalFile(item_path);
isDir = mon_dossier_cible.exists();
isFile = mon_fichier_cible.exists();

cout << "\t Cible : \t" << (item->text(0)).toStdString() << endl;
cout << "\t Acces : \t" << item_path.toStdString() << endl;
cout << "\t isDir : \t" << isDir << endl;
cout << "\t isFile : \t" << isFile << endl;
cout << "\n" << endl;

// L'objet sélectionné est un dossier
if (isDir == true)
{
if (liste_dossiers_ouverts.contains(item_path) == false) // Si ce dossier n'a pas encore été ouvert
{
liste_dossiers_ouverts.append(item_path);
mon_url = QUrl::fromLocalFile(item_path);
QDesktopServices::openUrl(mon_url);
}
}

// L'objet sélectionné n'est pas un dossier
else
{
// Par contre c'est bien un fichier, donc un film
if (isFile == true)
{
QStringList liste_item_path = item_path.split("/");

// Parcours de la liste sauf le dernier élément
// Pour reconstruire le chemin d'accès au dossier parant
QString item_path_to_parent = "";
for (int j=0; j<liste_item_path.size()-1; j++)
{
item_path_to_parent.append(liste_item_path.at(j));
item_path_to_parent.append("/");
}

if (liste_dossiers_ouverts.contains(item_path_to_pare nt) == false) // Si ce dossier n'a pas encore été ouvert
{
liste_dossiers_ouverts.append(item_path_to_parent) ;
mon_url = QUrl::fromLocalFile(item_path_to_parent);
QDesktopServices::openUrl(mon_url);
}
}
}
}
}

void interface_impl::SLOT_lancer_selection()
{
cout << "SLOT_lancer_selection" << endl;

QTreeWidgetItem *item;
QString item_path;
QDir mon_dossier_cible;
QFile mon_fichier_cible;
QUrl mon_url;
bool isDir;
bool isFile;
QStringList liste_films_lances;

for (int i=0; i<liste_selection.size(); i++)
{
item = liste_selection.at(i);
item_path = item->text(1);
mon_dossier_cible.setPath(item_path);
mon_fichier_cible.setFileName(item_path);
mon_url = QUrl::fromLocalFile(item_path);
isDir = mon_dossier_cible.exists();
isFile = mon_fichier_cible.exists();

cout << "\t Cible : \t" << (item->text(0)).toStdString() << endl;
cout << "\t Acces : \t" << item_path.toStdString() << endl;
cout << "\t isDir : \t" << isDir << endl;
cout << "\t isFile : \t" << isFile << endl;
cout << "\n" << endl;

// L'objet sélectionné est un dossier
if (isDir == true)
{
// if (liste_films_lances.contains(item_path) == false) // Si ce film n'a pas encore été lancé
// {
// liste_films_lances.append(item_path);
// mon_url = QUrl::fromLocalFile(item_path);
// QDesktopServices::openUrl(mon_url);
// }
}

// L'objet sélectionné n'est pas un dossier
else
{
// Par contre c'est bien un fichier, donc un film
if (isFile == true)
{
if (liste_films_lances.contains(item_path) == false) // Si ce film n'a pas encore été lancé
{
liste_films_lances.append(item_path);
mon_url = QUrl::fromLocalFile(item_path);
QDesktopServices::openUrl(mon_url);
}
}
}
}
}

void interface_impl::SLOT_lancer_le_film(QTreeWidgetIte m *item, int column)
{
cout << "\n\n" << endl;
cout << "SLOT_lancer_le_film" << endl;
cout << "Film : \t\t" << (item->text(0)).toStdString() << endl;
cout << "Acces : \t" << (item->text(1)).toStdString() << endl;

QUrl mon_url;
mon_url = QUrl::fromLocalFile(item->text(1));
QDesktopServices::openUrl(mon_url);
}

void interface_impl::showEvent(QShowEvent *event)
{
cout << "showEvent" << endl;

// // Préparation de l'affichage taille "normale" (fenêtré)
// int hauteur_ecran = (QApplication::desktop()->availableGeometry(0)).height();
// int largeur_ecran = (QApplication::desktop()->availableGeometry(0)).width();
// setGeometry(50, 50, largeur_ecran - 100, hauteur_ecran - 100);
//
// // Passage en plein écran
showMaximized();

// Redimensionnement des colonnes de stableaux d'ajout / suppression de dossiers à scanner / ne pas scanner
// int largeur_tableau = (tableau_scan->width()) - (((tableau_scan->width()) * 3) / 100);
// int largeur_col_0 = (largeur_tableau * 10) / 100;
// int largeur_col_1 = (largeur_tableau * 20) / 100;
// int largeur_col_2 = largeur_tableau - (largeur_col_0 + largeur_col_1);

// tableau_scan->setColumnWidth(0, largeur_col_0);
// tableau_scan->setColumnWidth(1, largeur_col_1);
// tableau_scan->setColumnWidth(2, largeur_col_2);
//
// tableau_noscan->setColumnWidth(0, largeur_col_0);
// tableau_noscan->setColumnWidth(1, largeur_col_1);
// tableau_noscan->setColumnWidth(2, largeur_col_2);

// Masquage de la colonne des chemins d'accès
tree->hideColumn(1);
}

void interface_impl::resizer_tableaux()
{
tableau_scan->resizeColumnToContents(0);
tableau_scan->resizeColumnToContents(1);
tableau_scan->setColumnWidth(1, tableau_scan->columnWidth(1) + 5);
// tableau_scan->setColumnWidth(2, tableau_scan->width() - tableau_scan->columnWidth(0) - tableau_scan->columnWidth(1));

tableau_noscan->resizeColumnToContents(0);
tableau_noscan->resizeColumnToContents(1);
tableau_noscan->setColumnWidth(1, tableau_noscan->columnWidth(1) + 5);//
// tableau_noscan->setColumnWidth(2, tableau_noscan->width() - tableau_noscan->columnWidth(0) - tableau_noscan->columnWidth(1));

// cout << tableau_scan->width() << endl;
// cout << tableau_scan->columnWidth(0) << endl;
// cout << tableau_scan->columnWidth(1) << endl;
// cout << tableau_scan->columnWidth(2) << endl;
// cout << tableau_scan->columnWidth(3) << endl;
// cout << tableau_scan->width() - 10 - tableau_scan->columnWidth(0) - tableau_scan->columnWidth(1) << endl;

}

Nyphel
9th October 2007, 15:16
void interface_impl::MAJ_listes_repertoires()
{
cout << "MAJ_listes_repertoires" << endl;

// Suppression du contenu des listes
Listeur_appelant->vider_listes_repertoires();

// Variables temporaires
QTableWidgetItem *QWI_element_courant;
QString chemin_acces;

// Liste des répertoires à scanner
for (int r = 0; r < tableau_scan->rowCount(); ++r)
{
// Récupération du chemin
QWI_element_courant = tableau_scan->item(r, 0); // Checkbox
if (QWI_element_courant->checkState() == Qt::Checked)
{
QWI_element_courant = tableau_scan->item(r, 2); // Chemin d'accès
chemin_acces = "";
chemin_acces = QWI_element_courant->text();
if (chemin_acces != "")
{
// Ajout dans la liste
Listeur_appelant->ajouter_repertoire_scan(chemin_acces);
}
}
}

// Liste des répertoires à ne pas scanner
for (int r = 0; r < tableau_noscan->rowCount(); ++r)
{
// Récupération du chemin
QWI_element_courant = tableau_noscan->item(r, 0); // Checkbox
if (QWI_element_courant->checkState() == Qt::Checked)
{
QWI_element_courant = tableau_noscan->item(r, 2); // Chemin d'accès
chemin_acces = "";
chemin_acces = QWI_element_courant->text();
if (chemin_acces != "")
{
// Ajout dans la liste
Listeur_appelant->ajouter_repertoire_noscan(chemin_acces);
}
}
}
}

void interface_impl::SLOT_ajouter_repertoire_scan()
{
cout << "SLOT_ajouter_repertoire_scan" << endl;

tableau_scan->setRowCount(tableau_scan->rowCount() + 1);

QTableWidgetItem *Item_0 = new QTableWidgetItem("");
QTableWidgetItem *Item_2 = new QTableWidgetItem("");

QPushButton *bouton_rechercher = new QPushButton("Rechercher...");
connect(bouton_rechercher, SIGNAL(clicked()), this, SLOT(SLOT_rechercher_repertoire_scan()));

Item_0->setCheckState(Qt::Unchecked);
Item_0->setFlags(Item_0->flags() & (~(Qt::ItemIsEditable)));
//Item_2->setFlags(Item_2->flags() & (Qt::ItemIsEditable));

tableau_scan->setItem(tableau_scan->rowCount()-1, 0, Item_0);
tableau_scan->setCellWidget(tableau_scan->rowCount()-1, 1, bouton_rechercher);
tableau_scan->setItem(tableau_scan->rowCount()-1, 2, Item_2);
}

void interface_impl::SLOT_supprimer_repertoire_scan()
{
cout << "SLOT_supprimer_repertoire_scan" << endl;

// Recherche la ligne du tableau_scan sélectionnée
QPushButton *QPB_element_courant;
int row_number = -1;
for (int r = 0; r < tableau_scan->rowCount(); ++r)
{
for (int c = 0; c < tableau_scan->columnCount(); ++c)
{
if (c == 0) // Item
{
if ((tableau_scan->item(r,c))->isSelected() == true)
{
row_number = r;
}
}
else if (c == 1) // Boutons
{
QPB_element_courant = static_cast<QPushButton*>(tableau_scan->cellWidget(r, c));
if (QPB_element_courant->hasFocus() == true)
{
row_number = r;
}
}
else if (c == 2) // Item
{
if ((tableau_scan->item(r,c))->isSelected() == true)
{
row_number = r;
}
}
}
}

if (row_number == -1)
{
tableau_scan->removeRow(tableau_scan->rowCount()-1);
}
else
{
tableau_scan->removeRow(row_number);
}
}

void interface_impl::SLOT_rechercher_repertoire_scan()
{
cout << "SLOT_rechercher_repertoire_scan" << endl;

// Recherche la ligne du tableau_scan depuis laquelle le bouton a été cliqué
QPushButton *QPB_element_courant;
int row_number = -1;
for (int r = 0; r < tableau_scan->rowCount(); ++r)
{
for (int c = 0; c < tableau_scan->columnCount(); ++c)
{
if (c == 0) // Item
{
if ((tableau_scan->item(r,c))->isSelected() == true)
{
row_number = r;
}
}
else if (c == 1) // Boutons
{
QPB_element_courant = static_cast<QPushButton*>(tableau_scan->cellWidget(r, c));
if (QPB_element_courant->hasFocus() == true)
{
row_number = r;
}
}
else if (c == 2) // Item
{
if ((tableau_scan->item(r,c))->isSelected() == true)
{
row_number = r;
}
}
}
}

// Récupération du chemin affiché sur cette ligne du tableau_scan
QTableWidgetItem *QWI_element_courant;
QWI_element_courant = tableau_scan->item(row_number, 2);
QString chemin_acces_affiche = QWI_element_courant->text();

// Fenêtre de recherche de répertoire
QString titre = "Répertoire à scanner";
QString chemin = "/home";
if (chemin_acces_affiche != "")
{
chemin = chemin_acces_affiche;
}
QString chemin_acces_voulu = QFileDialog::getExistingDirectory(this, titre, chemin, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

// Mise à jour du chemin affiché dans cette ligne du tableau_scan
if (chemin_acces_voulu != "")
{
QWI_element_courant->setText(chemin_acces_voulu);
}
}
void interface_impl::SLOT_ajouter_repertoire_noscan()
{
cout << "SLOT_ajouter_repertoire_noscan" << endl;

tableau_noscan->setRowCount(tableau_noscan->rowCount() + 1);

QTableWidgetItem *Item_0 = new QTableWidgetItem("");
QTableWidgetItem *Item_2 = new QTableWidgetItem("");

QPushButton *bouton_rechercher = new QPushButton("Rechercher...");
connect(bouton_rechercher, SIGNAL(clicked()), this, SLOT(SLOT_rechercher_repertoire_noscan()));

Item_0->setCheckState(Qt::Unchecked);
Item_0->setFlags(Item_0->flags() & (~(Qt::ItemIsEditable)));
//Item_2->setFlags(Item_2->flags() & (Qt::ItemIsEditable));

tableau_noscan->setItem(tableau_noscan->rowCount()-1, 0, Item_0);
tableau_noscan->setCellWidget(tableau_noscan->rowCount()-1, 1, bouton_rechercher);
tableau_noscan->setItem(tableau_noscan->rowCount()-1, 2, Item_2);
}

void interface_impl::SLOT_supprimer_repertoire_noscan()
{
cout << "SLOT_supprimer_repertoire_noscan" << endl;

// Recherche la ligne du tableau_scan sélectionnée
QPushButton *QPB_element_courant;
int row_number = -1;
for (int r = 0; r < tableau_noscan->rowCount(); ++r)
{
for (int c = 0; c < tableau_noscan->columnCount(); ++c)
{
if (c == 0) // Item
{
if ((tableau_noscan->item(r,c))->isSelected() == true)
{
row_number = r;
}
}
else if (c == 1) // Boutons
{
QPB_element_courant = static_cast<QPushButton*>(tableau_noscan->cellWidget(r, c));
if (QPB_element_courant->hasFocus() == true)
{
row_number = r;
}
}
else if (c == 2) // Item
{
if ((tableau_noscan->item(r,c))->isSelected() == true)
{
row_number = r;
}
}
}
}

if (row_number == -1)
{
tableau_noscan->removeRow(tableau_noscan->rowCount()-1);
}
else
{
tableau_noscan->removeRow(row_number);
}
}

void interface_impl::SLOT_rechercher_repertoire_noscan( )
{
cout << "SLOT_rechercher_repertoire_noscan" << endl;

// Recherche la ligne du tableau_noscan depuis laquelle le bouton a été cliqué
QPushButton *QPB_element_courant;
int row_number = -1;
for (int r = 0; r < tableau_noscan->rowCount(); ++r)
{
for (int c = 0; c < tableau_noscan->columnCount(); ++c)
{
if (c == 0) // Item
{
if ((tableau_noscan->item(r,c))->isSelected() == true)
{
row_number = r;
}
}
else if (c == 1) // Boutons
{
QPB_element_courant = static_cast<QPushButton*>(tableau_noscan->cellWidget(r, c));
if (QPB_element_courant->hasFocus() == true)
{
row_number = r;
}
}
else if (c == 2) // Item
{
if ((tableau_noscan->item(r,c))->isSelected() == true)
{
row_number = r;
}
}
}
}

// Récupération du chemin affiché sur cette ligne du tableau_scan
QTableWidgetItem *QWI_element_courant;
QWI_element_courant = tableau_noscan->item(row_number, 2);
QString chemin_acces_affiche = QWI_element_courant->text();

// Fenêtre de recherche de répertoire
QString titre = "Répertoire à scanner";
QString chemin = "/home";
if (chemin_acces_affiche != "")
{
chemin = chemin_acces_affiche;
}
QString chemin_acces_voulu = QFileDialog::getExistingDirectory(this, titre, chemin, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

// Mise à jour du chemin affiché dans cette ligne du tableau_scan
if (chemin_acces_voulu != "")
{
QWI_element_courant->setText(chemin_acces_voulu);
}
}

Nyphel
9th October 2007, 15:20
Rah no... There is too much code, I'm at the end of the project.
Moreover, I should shouw you the Listeur class too, cause it adds QTreeWidgetItem* to the QTreeWidget (This is the main class that create theinterface and serach for the data that will fill the QTreeWidget... :s)

I'll try to do something more clearly later, because it won't be helpfull if I give you all that code as is... With so poor comments :p

wysota
9th October 2007, 15:28
I think you should have attached files instead of pasting them here :)

I'll dig into what you provided and see what might be wrong. Currently I can't find the slot where you call clear() on the tree widget, could you point me to it?

BTW. You really should have taken the model-view approach instead of using convenience classes...

Edit: Could you provide the backtrace after the crash?

Nyphel
9th October 2007, 15:55
Yes, great idea ;)

Link to the project archived in ZIP format (http://membres.lycos.fr/nyphel/Nyphel_project/Nyphel_project.zip)
Link to the project archived in RAR format (http://membres.lycos.fr/nyphel/Nyphel_project/Nyphel_project.rar)

I should have taken a MVC approach, yes, but this project was just a small personnal project in order to list me what movies I have on my computer. I made the first build very simply, and I didn't took care about the conceptual approach... Shame on me !
That's the reason why I didn't read all the informations relative to QTreeWidget in the Qt Assistant, and didn't saw the View and Model aspects avaibled for this widget ;).
After that I developped more and more small functionnality, when I needed them. The project isn't really well concepted...

My project is compounded of :
- Listeur class, the main class that set the interface and lauch the various threads used by my application.
- Interface_impl class, the interface implementation class that handle the user actions and wishes.
- Thread_scanneur class, that search on my HDD in order to list the movies.
- Thread_HTTP_XXX classes, that search on websites (1 class by website) in order to find, download and store the movies covers and resumes.

So the 3 classes that manage QTreeWidgetItem* are :
- Listeur
- Interface_impl
- Thread_scanneur

The class that actually make the application to crash is Interface_impl.
The signal concerned is : itemSelectionChanged().
The slot concerned by this signal is : SLOT_selection_changed().

The goal of this slot is to :
- store all the items selected by the user
- get the FIRST item selected in order to know the movie name that has been selected
- display the cover and the resume of this movie on the interface
Actually all this stuff is commented in the slot.

There is no need to clear the QTreeWidget for doing that job...
In reallity, the application used to crash when the user selected a movie, and then asked for an update of the movie list. This update was clearing the QTreeWidget in order to display the list of movies found on the HDD... And at this time, the application used to crash.

So, in order to simplify the tests, I decided to clear() the QTreeWidget directly in the SLOT called when the selection changed.

Thanks a lot for your help Wysota, as usually ! :)



EDIT : I've not really backtraces or debugger under the hand.
I simply displays some comments on the cout at each step, in order to detect and target where the application crashes.

EDIT : Attached the project source archived in ZIP format.

wysota
9th October 2007, 16:04
Yes, great idea ;)

Link to the project archived in ZIP format (http://membres.lycos.fr/nyphel/Nyphel_project/Nyphel_project.zip)
Link to the project archived in RAR format (http://membres.lycos.fr/nyphel/Nyphel_project/Nyphel_project.rar)
Khyym... please use the attachment feature of the forum next time. We don't like external links here.


I should have taken a MVC approach, yes, but this project was just a small personnal project in order to list me what movies I have on my computer. I made the first build very simply, and I didn't took care about the conceptual approach... Shame on me !
That's the reason why I didn't read all the informations relative to QTreeWidget in the Qt Assistant, and didn't saw the View and Model aspects avaibled for this widget ;).
That's usually the problem. You start with something simple and suddenly it happens to be a big project. You're wasting much effort operating on the filesystem and you could have just used QDirModel with an optional proxy model to filter out entries you didn't want. You should think about redesigning it...



EDIT : I've not really backtraces or debugger under the hand.
I simply displays some comments on the cout at each step, in order to detect and target where the application crashes.

That's not good, because what's important is not only that where the application crashes but also which method invoked the method that crashed and with what parameters. Unless you provide us with something small and compilable, you have to provide the backtrace yourself. Currently there's just too much code to analyse. It could be that the crash is caused by one of the threads (from what I understand you are using threads). Don't you by any chance operate on the tree from within a worker thread? Anyway please provide the backtrace or cut down the project to something small that reproduces the problem.

Nyphel
9th October 2007, 16:13
Oki, I've attached the source code in the previous post :).

So, how may I produce the fully backtrace for my code ?
I'm under Windows XP Pro SP2, using Eclipse as text editor.
I've recently added the Qt Plugin for Eclipse in order to managed my .pro file, to use the API code completion, to build and run my project...

But I've no idea about what concerns the backtraces/debugger functionalities of Qt and/or the Eclipse integration.

My Qt is compiled in release/shared configuration.
Should I rebuild it in debug/shared configuration ?



PS : I'm sure that the crash isn't caused by a Thread.
The application crashes when no Thread is started, I can make it crash just after the launch if necessary. My thread are only started on user ask, by clicking buttons on the interface et traces are displayed on the cout when the run() method is called.
Moreover, I've decided that no thread would modify the QTreeWidget. So only Listeur and Interface_impl classes can modify the QTreeWidget, I prefer that...

But I understand why the backtrace is necessary :)

wysota
9th October 2007, 16:24
Oki, I've attached the source code in the previous post :).
Yes, I know, but I assume that it's a bit too complex to understand it in 5 minutes.


So, how may I produce the fully backtrace for my code ?
I'm under Windows XP Pro SP2, using Eclipse as text editor.
I've recently added the Qt Plugin for Eclipse in order to managed my .pro file, to use the API code completion, to build and run my project...
If you're using MinGW as your compiler, you need to install gdb (GNU Debugger) and run your application under its control. When it crashes, type in "bt", press enter and paste in the result here. Just remember to compile your app in debug mode.


Should I rebuild it in debug/shared configuration ?
Not necessarily. You can compile your app in debug mode and link it against release Qt libs. But if you're not certain you can do it, it's better to compile Qt in debug mode as well.

Nyphel
9th October 2007, 23:13
I'm back :)

In order to remove all doubt, I uninstalled Qt.
I reinstalled Qt with the MinGW download.
I reconfigured it with the following options : "-debug -release -shared".
I re-built Qt without errors or warnings using "make sub-src".
I built the Qt Debug Librairies without errors or warnings.

I installed GDB (for MinGW).
I reinstalled the Eclipse Qt Integration tool.

I modified the .PRO file in order to add "Config += Debug".
I launched my Qt Command Prompt.
I built my application with "qmake" and "make".

I' launched the GDB tool with "gdb release/Listeur" :


Setting up a MinGW/Qt only environment...
-- QTDIR set to D:\Applications\Qt\4.3.1
-- PATH set to D:\Applications\Qt\4.3.1\bin
-- Adding D:\Applications\MinGW\bin to PATH
-- Adding C:\WINDOWS\System32 to PATH
-- QMAKESPEC set to win32-g++

D:\Applications\Qt\4.3.1\Workspace\Listeur>gdb release/Listeur
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-mingw32"...(no debugging symbols found)


Next, I ran my application under control of GDB :


(gdb) run
Starting program: D:\Applications\Qt\4.3.1\Workspace\Listeur/release/Listeur.exe

---Type <return> to continue, or q <return> to quit---


Of course, I pressed my RETURN key.
The application has been launched.
I selected an item of my QTreeWidget, and the application crashed :


Program received signal SIGSEGV, Segmentation fault.
0x00446d3b in ?? ()


I typed in "bt", and here is what GDB returned me :


(gdb) bt
#0 0x00446d3b in ?? ()
#1 0x0023979c in ?? ()
#2 0x01543350 in ?? ()
#3 0x10180418 in ZeqRK6QRectFS1_ ()
from D:\Applications\Qt\4.3.1\bin\QtCore4.dll
#4 0x10009f40 in ZN18QThreadStorageData6finishEPPv ()
from D:\Applications\Qt\4.3.1\bin\QtCore4.dll
#5 0x00455f40 in ?? ()
#6 0x004a871b in ?? ()
#7 0x002397cc in ?? ()
#8 0x00446e42 in ?? ()
#9 0x0023973c in ?? ()
#10 0x00000001 in ?? ()
#11 0x00000000 in ?? () from
#12 0x00000027 in ?? ()
#13 0x00239894 in ?? ()
#14 0x002397e4 in ?? ()
#15 0x00ba8372 in ZN11QMainWindow11qt_metacallEN11QMetaObject4CallEi PPv ()
#16 0x0023a31c in ?? ()
(gdb)

wysota
10th October 2007, 00:06
Hmm... this doesn't look like debug mode :) And if the data is correct, it means your application has gone mad. Omit the "-release" switch when compiling Qt (or use -debug_and_release instead of -debug and -release). And make sure you add "CONFIG+=debug" to the project file (I'm not sure if it's case sensitive or not).

Based on what we have here I'm sure your application uses threads (behind the scene, probably if you're not aware of it). Hard to say what exactly happens here without a proper backtrace.

Nyphel
10th October 2007, 00:34
Oki, I've edited my .PRO file.

A GDB run on "release/Listeur" returned me the same backtrace as the previous one, displayed above.

Like my built of the application ("qmake", "make") outputs 2 executables ("release/Listeur.exe" and "debug/Listeur.exe"), I've made a GDB run on "debug/Listeur". It returned me the following backtrace :


D:\Applications\Qt\4.3.1\Workspace\Listeur>gdb debug/Listeur
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-mingw32"...



(gdb) run
Starting program: D:\Applications\Qt\4.3.1\Workspace\Listeur/debug/Listeur.exe

The application started correctly.

Then, I made it crash, selected an item in my QTreeWidget :


warning: ASSERT failure in QList<T>::at: "index out of range", file ../../includ
e/QtCore/../../src/corelib/tools/qlist.h, line 386


Program exited with code 01.


Now, I'm re-configuring and re-building Qt in debug mode only.
See you tomorrow ;)

wysota
10th October 2007, 10:02
Wait, now we see you are accesing an item in a list that doesn't exist. It's probably your
item_name = ((tree->selectedItems()).at(0))->text(0); line. selectedItems() is probably empty. Try changing the above line to:

QList<QTreeWidgetItem*> sel = tree->selectedItems();
qDebug("ITEMS SELECTED: %d", sel.count());

if(!sel.isEmpty()){
item_name = sel.at(0)->text(0);
}

Nyphel
10th October 2007, 12:41
Hello,

I've re-configured and re-built Qt in debug mode.

I've tested my code under GDB and I got the same backtrace as before (the one in the above message).

I've tested your code under GDB and I got the following backtrace :


D:\Applications\Qt\4.3.1\Workspace\Listeur>gdb debug/Listeur
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-mingw32"...
(gdb) run
Starting program: D:\Applications\Qt\4.3.1\Workspace\Listeur/debug/Listeur.exe
warning: ITEMS SELECTED: 1

warning: ITEMS SELECTED: 0


At this step the QTeeWidget is cleared and the application crashed.

wysota
10th October 2007, 13:28
So as you see there are no items selected so you can't call at(0). You have to redesign the method.

Nyphel
10th October 2007, 14:16
Yes, but I still don't understand the problem.



void QTreeWidget::itemSelectionChanged () [signal]
This signal is emitted when the selection changes in the tree widget. The current selection can be found with selectedItems()

It appears that the signal itemSelectionChanged() has been emited 2 times. This is strange... The first time we have 1 item selected : that's OK, I select 1 item in the QTreeWidget. But why is the list empty ?! It should contains the item, like it is described in the Assistant :



QList<QTreeWidgetItem *> QTreeWidget::selectedItems () const
Returns a list of all selected non-hidden items.
See also itemSelectionChanged().


So I can't understand :
- Why the signal has been emited 2 times (1 just clic 1 item, 1 time, with 1 clic)
- Why the function isEmpty() returns "true", where as count() returns "1".

Have you got an idea ?

wysota
10th October 2007, 14:28
If isEmpty() returns true, count() has to return 0. There is no other way, you must be messing something up. I can't say why the selection is empty in your case - maybe you are mixing the selected and the current item? Current item doesn't have to be selected...

Nyphel
10th October 2007, 14:50
With this code for my SLOT :


void interface_impl::SLOT_selection_changed()
{
QString item_name;

QList<QTreeWidgetItem*> sel = tree->selectedItems();
qDebug("ITEMS SELECTED: %d", sel.count());

if(!sel.isEmpty())
{
item_name = sel.at(0)->text(0);
}
}


I get the following backtrace :


D:\Applications\Qt\4.3.1\Workspace\Listeur>gdb debug/Listeur
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-mingw32"...
(gdb) run
Starting program: D:\Applications\Qt\4.3.1\Workspace\Listeur/debug/Listeur.exe
warning: ITEMS SELECTED: 1


Program exited normally.
(gdb)


So now the QTreeWidget isn't cleared and the signal isn't emited a second time. That's OK.
And, if I'm right :
- 1 select an item : the signal is emited and the slo is performed
- "tree->selectedItems();" returns 1 QTreeWidgetItem* in the list
- "count()" confirms that there is 1 item in the list
- "isEmpty()" returns "TRUE", indicating that the list is empty

Like "tree->selectedItems();" returns a QList of selected items, I suppose I never take care about current item :s

wysota
10th October 2007, 14:54
It is "!isEmpty()", so it negates the result.

Nyphel
10th October 2007, 15:00
Yes of course :)
Like "isEmpty()" returns TRUE, "!isEmpty()" returns FALSE and the instruction "item_name = sel.at(0)->text(0);" is performed, like shown on the backtrace... This is my problem.

I can't understand how this QList can be empty with an item inside of it...

Nyphel
10th October 2007, 15:11
Oh I'm so stupid... What am I saying... All works well...
Sorry Wysota ^_^

To conclude, here is my last backtrace showing that there is no more problem.



void interface_impl::SLOT_selection_changed()
{
QString item_name;

QList<QTreeWidgetItem*> sel = tree->selectedItems();
qDebug("COUNT: %d", sel.count());
qDebug("SIZE: %d", sel.size());
qDebug("EMPTY: %d", sel.isEmpty());

if(sel.isEmpty() == false)
{
qDebug("item_name : before acces");
item_name = sel.at(0)->text(0);
qDebug("item_name : after acces");
}

qDebug("tree : before clear");
tree->clear();
qDebug("tree : after clear");
}





D:\Applications\Qt\4.3.1\Workspace\Listeur>gdb debug/Listeur
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-mingw32"...
(gdb) run
Starting program: D:\Applications\Qt\4.3.1\Workspace\Listeur/debug/Listeur.exe

warning: COUNT: 1

warning: SIZE: 1

warning: EMPTY: 0

warning: item_name : before acces

warning: item_name : after acces

warning: tree : before clear

warning: COUNT: 0

warning: SIZE: 0

warning: EMPTY: 1

warning: tree : before clear

warning: tree : after clear

warning: tree : after clear


Program exited normally.
(gdb)

Like it has been recommanded me on Qtfr forums, I should use the accessors "value()" or "take()" instead of "at()" in order to prevent segmentation fault errors :). This would have help us so much... :o

wysota
10th October 2007, 15:33
Like it has been recommanded me on Qtfr forums, I should use the accessors "value()" or "take()" instead of "at()" in order to prevent segmentation fault errors :). This would have help us so much... :o
I don't think it would help. Your problem was that you were dereferencing a pointer without checking it for null first.