Char* corrupted when trying to get it / set it - c++

I used to add a feature in my app based on treewidget.
When right click on an item, I can create a folder on the FS over USB is used.
That mainly say that when selecting an item on the TreeWidget, I get its name and check if it's a folder, if yes, I create a child and add a new folder and file the name.
The make the folder creation done on the FS, I'm using this :
void TreeView::onAddFolderAction() {
char *parent_folder_name;
QList<QTreeWidgetItem *> item;
bool isFolder;
item = this->selectedItems();
if (item.empty())
parent_folder_name = NULL;
else {
QString str = item[0]->text(0);
QByteArray latin_str = str.toLatin1();
parent_folder_name = latin_str.data();
}
setParentFolder(parent_folder_name);
if(!item.empty())
isFolder = m_device.isParentFolder(parent_folder_name);
if(isFolder == false)
return;
QTreeWidgetItem* child = new QTreeWidgetItem();
child->setText(0, "New Folder");
child->setText(1, "--");
child->setText(2, "--");
child->setFlags(child->flags() | Qt::ItemIsEditable);
if(!item.empty()) {
item[0]->addChild(child);
item[0]->setExpanded(true);
}
else
addTopLevelItem(child);
this->editItem(child);
this->setCurrentItem(child);
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(onTreeItemChanged()));
}
this code is connected to a CONNECT when left click action and add folder is selected.
I have done this to avoid the use of a dialog box when renaming. what I'm doing in add an item named New Folder, make it editable and allow the user to change it. when user press enter, the itemChanged signal is triggered and I start onTreeItemChanged.
QList<QTreeWidgetItem *> items;
bool isFolderCreated;
items = this->selectedItems();
QString str = items[0]->text(0);
QByteArray latin_str = str.toLatin1();
char *newFolder = latin_str.data();
char *parent = getParentFolder();
isFolderCreated = m_device.CreateFolder(newFolder, parent);
My issue is that parent do not contain the rigth name.
I'm backuping it in the onAddFolderAction using
setParentFolder(parent_folder_name);
and get it back using
getParentFolder();
Both are declared as below
void TreeView::setParentFolder(char *name) {
this->ParentFolder = name;
}
char *TreeView::getParentFolder() {
return this->ParentFolder;
}
when using the getParent, the value return is not correct. but it seems that what the value is corrupted earlier.
in the on AddFolderACtion(), I had:
else {
QString str = item[0]->text(0);
QByteArray latin_str = str.toLatin1();
parent_folder_name = latin_str.data();
}
setParentFolder(parent_folder_name);
parent_folder_name contain "BLABLA" but when moving step-by-step on setParentFolder, the parent_folder_name change to "XFZV" strange caracters.
Any idea why ?

In the code
else {
QString str = item[0]->text(0);
QByteArray latin_str = str.toLatin1();
parent_folder_name = latin_str.data();
}
setParentFolder(parent_folder_name);
the variable latin_str is local inside the else block, and will go out of scope and get destructed once the else block end. This leaves you with a stray pointer, pointing to now destructed and unallocated memory, leading to undefined behavior when you dereference it.
My recommendation is to use QString as much as possible, and only convert to pointer when actually needed (even if you need to do it multiple times). Either that, or change the rest of the code to accept QString (which would be a better solution IMO).

Related

QListWidget deleting QListWidgetItem causes segmentaion fault

I have a C++ application where I am reading pcap file format and processing usb packets. For each packet, I would like to create a QListWidgetItem to which I am storing some data and then adding it to the QListWidget. Here is where the trouble begins. According to QListWidget documentation, after inserting item :
The list widget will take ownership of the item.
So what I thought is that its on QListWidget to delete all QListWidgetItems. The items are added fine, but when I close my app(I suppose thats when desctructor od QListWidget is called so he is calling desctructor of each QListWidgetItem) I get delete_scalar exception. According to call stack, deletion of some QListWidgetItem triggers it :
Snippet of my code (this function is being called for each packet in pcap file and is responsible for creating and adding items):
void ItemManager::ProcessPacket(QByteArray packetData)
{
const unsigned char* packet = (unsigned char*)packetData.data();
PUSBPCAP_BUFFER_PACKET_HEADER usbh = (PUSBPCAP_BUFFER_PACKET_HEADER)packet;
QListWidgetItem* item = new QListWidgetItem;
//set USBPCAP header to item
QByteArray usbhArray((const char*)packet, sizeof(USBPCAP_BUFFER_PACKET_HEADER));
item->setData(dataHolder->USBPCAP_HEADER_DATA, QVariant(usbhArray));
packet += sizeof(USBPCAP_BUFFER_PACKET_HEADER);
if (usbh->transfer == USBPCAP_TRANSFER_ISOCHRONOUS || usbh->transfer == USBPCAP_TRANSFER_CONTROL) //check for optional header data
{
int additionalDataSize = usbh->headerLen - sizeof(USBPCAP_BUFFER_PACKET_HEADER);
if (additionalDataSize > 0)
{
//set additional header data to item
QByteArray additionalDataArray((const char*)(packet), additionalDataSize);
item->setData(dataHolder->TRANSFER_OPTIONAL_HEADER, QVariant(additionalDataArray));
packet += additionalDataSize;
}
else
{
item->setData(dataHolder->TRANSFER_OPTIONAL_HEADER, QVariant()); //QVariant creates invalid QVariant, later i just need to check with QVariant::isValid()
}
}
else
{
item->setData(dataHolder->TRANSFER_OPTIONAL_HEADER, QVariant());
}
//set leftover data to item
QByteArray leftoverDataArray((const char*)packet, usbh->dataLength);
item->setData(dataHolder->TRANSFER_LEFTOVER_DATA, QVariant(leftoverDataArray));
listWidget->insertItem(listWidget->count(), item);
}
Calling of ProcessPacket function :
void ItemManager::ProcessFile(QString filename, bool liveReading)
{
if (fileReader.OpenNewFile(filename))
{
if (fileReader.ReadFileHeader())
{
while (!stopButtonClicked)
{
while (!fileReader.EndOfFile())
{
QByteArray packetData = fileReader.GetPacket();
if (!pauseButtonClicked)
{
ProcessPacket(packetData);
}
}
parent->Refresh(); //this is just calling QCoreApplication::processEvents();
if (!atBottomOfList)
{
listWidget->scrollToBottom();
}
if (liveReading)
{
Sleep(50);
}
else
{
return;
}
}
}
}
}
EDIT
I found out that this problem is happening only when appending to QListWidget through ItemManager class. In my main Q_OBJECT class USB_Packet_Analyzer(which holds QlistWidget that I am appending to) i have slot on_OpenButton_clicked() which looks like this :
void USB_Packet_Analyzer::on_OpenButton_clicked()
{
QString fil = QFileDialog::getOpenFileName(this, "Select source file", ".", "Pcap files (*.pcap)");
ItemManager manager(ui.listWidget,this);
manager.ProcessFile(fil, ui.liveCaptureRadioButton->isChecked());
}
where in ItemManager class constructor looks like this :
ItemManager::ItemManager(QListWidget* listWidget, USB_Packet_Analyzer* parent)
{
this->stopButtonClicked = false;
this->pauseButtonClicked = false;
this->atBottomOfList = false;
this->listWidget = listWidget;
this->parent = parent;
this->dataHolder = DataHolder::GetDataHolder();
}
Now, if I add items in on_OpenButton_clicked() slot and close the app, everything is fine. But when I create ItemManager instance and append items in that class, the error occurs. Could it be that I am not allowed to pass QListWidget* as parameter ?
You have to create a new item inside your loop. You must not reuse the pointer because no content is copied, only the pointer is stored within the QListWidget
To this day I am not completely sure what caused the error, but it was fixed after I modified USBPCAP structs which I were using. They use all kind of stuff like #pragma pack(1) , #pragma pack(push) and so on. It looks like I was missing some of those, which might result in some undefined behaviour while using them.

QTreeWigetItem - How to manage a click to directly change the name of the item text

I have create a QTreeView and which is filed by QTreeWidgetItem. My application is a kind of file browser.
ViewTree is defined as below:
void MainWindow::createTreeView()
{
TreeViewSection = new QWidget();
QVBoxLayout *TreeViewLayout = new QVBoxLayout(TreeViewSection);
MyTree = new TreeWidget();
contextMenu = new QMenu(MyTree);
MyTree->setContextMenuPolicy(Qt::ActionsContextMenu);
addFolderAction = new QAction("Add Folder", contextMenu);
MyTree->addAction(addFolderAction);
connect(addFolderAction, SIGNAL(triggered()),this,SLOT(onAddFolderAction()));
deleteAction = new QAction("Delete", contextMenu);
MyTree->addAction(deleteAction);
connect(deleteAction, SIGNAL(triggered()),this,SLOT(onDeleteAction()));
MyTree->setSortingEnabled(true);
MyTree->setColumnWidth(0, 400);
headerItem = new QTreeWidgetItem();
headerItem->setText(0,QString("File Name"));
headerItem->setText(1,QString("Last Modified"));
headerItem->setText(2,QString("Size"));
MyTree->setHeaderItem(headerItem);
MyTree->setAcceptDrops(true);
MyTree->setDragEnabled(true);
MyTree->setDragDropMode(QAbstractItemView::InternalMove);
MyTree->setDefaultDropAction(Qt::MoveAction);
MyTree->setDropIndicatorShown(true);
MyTree->setDragDropOverwriteMode(true);
MyTree->setSelectionMode(QAbstractItemView::SingleSelection);
TreeViewLayout->addWidget(MyTree);
}
I have added an "Add Folder" Button, as defined below for the action:
void MainWindow::onAddFolderAction()
{
QList<QTreeWidgetItem *> item;
uint32_t index;
item = MyTree->selectedItems();
if (item.empty())
index = 0;
else {
QString str = item[0]->text(0);
QByteArray latin_str = str.toLatin1();
char *utf8_text = latin_str.data();
index = m_device.getIdByName(utf8_text);
}
if(m_device.isFolder(index) == true) {
QTreeWidgetItem* child = new QTreeWidgetItem();
child->setText(0, "NewFolder");
child->setText(1, "--");
child->setText(2, "--");
item[0]->addChild(child);
item[0]->setExpanded(true);
//item[0]->setSelected();
// MyTree->edit(selectedItem());
m_device.CreateNewFolder("New Folder", index);
}
}
As you see, by default I create a "NewFolder" which on a mtp FS system.
What I want to do it before creating the folder, I would like to be able to rename "NewFolder" to what the user want. I don't want to create a pop-up to enter text. What I want is a "kind of renaming" behavior. The folder appears in the treeview, the new folder is selected and the text is allowed to be changed directly. Like on Finder when you made a single click on the folder name, it become selected, and user can change the name in the finder without any pop-up
Thanks
If you want to edit the item right after creating it you should be able to achieve this behaviour by simply calling the editor on it:
QTreeWidgetItem* child = new QTreeWidgetItem();
child->setText(0, "NewFolder");
child->setText(1, "--");
child->setText(2, "--");
child->setFlags(child->flags() | Qt::ItemIsEditable);
item[0]->addChild(child);
item[0]->setExpanded(true);
MyTree->editItem(child);

Qt pass additional argument to slot AND keep emitted signal data

I have searched the web on this issue and I've repeatedly got answers referring to the use of QSignalMapper. But my problem is pretty clear, QSignalMapper automatically gets rid of whatever is originally emitted and replaces it with basically nothing, plus the new data that is set via setMapping().
The problem here is simple.
I have a QNetworkAccessManager that parses html and updates a vector containing text data:
void DataManager::startHttpRequest(QString url, int index)
{
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
//QSignalMapper* signalMapper = new QSignalMapper(this);
//connect(manager,SIGNAL(finished(QNetworkReply*)), signalMapper,SLOT(map()));
//signalMapper->setMapping(manager, index);
//connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(insertUpdate(int)));
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(finishHttpRequest(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl(url)));
qDebug() << index;
}
and here is what happens when the request is finished, the normal way:
void DataManager::finishHttpRequest(QNetworkReply *reply)
{
QString html = QString(reply->readAll()).simplified();
QString info;
int start = html.indexOf("<span id=\"SalePrice\" >");
if(start != -1)
{
QString price = html.mid(start + 23, 30);
int end = price.indexOf("</span>");
info = price.mid(0, end - 1);
qWarning() << price.mid(0, end - 1);
}
else
{
info = "NA";
}
// Do more stuff
}
Using the normal way of signals and slots, I would not be able to know the index of the vector I am updating,
Or,
If I am using QSignalMapper, I know the index, but not the data that comes with it.
How do I get BOTH working (index + data)?
(something like mySlot(QNetworkReply *reply, int *index), but we all know that won't work)
Many thanks in advance.
While it's probably not the best,
sender()->setObjectName(const QString & name) allows the sender to name itself.
The sender's name can be accessed from the receiving slot via sender()->ObjectName()
As documented on http://qt-project.org/doc/qt-5/qobject.html#objectName-prop.

How to build a Qt tree widget step by step, when the user clicks on the branches?

I built a large tree whose branches are loaded from disk, an operation that takes 5 minutes to complete. To speed it up, I want to only load the sub-trees the user selects from the GUI.
This is the current code, that loads everything all at once:
int MyWidget::AddSubtree(QTreeWidgetItem *_projectItem,
const QString &_filePath)
{
const QString currentPrj = _projectItem->text(0);
QDir* rootDir = new QDir(_filePath);
QFileInfoList filesList = rootDir->entryInfoList();
filesList = rootDir->entryInfoList();
foreach(QFileInfo fileInfo, filesList)
{
if(fileInfo.isDir())
{
const QString job = fileInfo.fileName();
QTreeWidgetItem* jobItem = new QTreeWidgetItem();
jobItem->setText(0, job);
//+P Performance critical! Do it on demand
AddSubSubtree(jobItem, fileInfo.filePath());
_projectItem->addChild(jobItem);
}
}
return 0;
}
int MyWidget::AddSubSubtree(QTreeWidgetItem *_jobItem,
const QString &_filePath)
{
const QString currentJob = _jobItem->text(0);
QDir* rootDir = new QDir(_filePath);
rootDir->cd("subDir");
// Retrieve the runs
filesList = rootDir->entryInfoList();
foreach(QFileInfo fileInfo, filesList)
{
if(fileInfo.isDir())
{
const QString run = fileInfo.fileName();
QTreeWidgetItem* runItem = new QTreeWidgetItem();
runItem->setText(0, run);
_jobItem->addChild(runItem);
}
}
return 0;
}
What I would like to do is to remove the performance critical line (below //+P), and call AddSubSubtree() on a single sub-sub-tree just when the user clicks on the corresponding parent branch.
How can I detect a specific parent branch has been clicked?
I know I need something like this in AddSubtree:
connect(jobItem, SIGNAL(triggered()), this, SLOT(AddSubSubtree()));
but I cannot make it work.
Am I lost in the details, or am I on a completely wrong path?
I think you may be looking for the signal itemClicked(QTreeWidgetItem * item, int column) from QTreeWidget. I'd try something like:
connect(yourTree, SIGNAL(itemClicked()), yourTree, SLOT(AddSubTree()));
You'd have to subclass TreeWidget and make AddSubTree(QTreeWidgetItem* item, int column) a slot of that subclass. Then in AddSubTree, check the column number is right and if so add the appropriate sub-tree to the argument item.
you might wish to port to a QAbstractItemModel and a QTreeView
where you implement the QAbstractItemModel to return the data and use canFetchMore(QModelIndex&) and fetchMore to do the actual fetching

Empty scrollArea QT C++

I am loading data from xml executed by timer. xml is parsed and populated in entity object.
In a loop im taking the data from entity object and populate QCommandLinkButton's. and in the end a batch of buttons are set into the verticalLayout and then in scrollArea.
but every time data is loaded its appends to the old data. How can I empty the content of the srollArea before repopulating scrollArea.
MainWindow::methodExecudedByTimer(){
foreach(int i, map.keys()){
QCommandLinkButton* buttonEmail = new QCommandLinkButton(this);
Email em = map[i];
buttonEmail->setText(em.__toString());
ui->verticalLayout->addWidget(buttonEmail);
}
ui->scrollArea->setLayout(ui->verticalLayout);
}
you can use setWidget replace setLayout.and then new data coming,you can call takeWidget to remove old data.
MainWindow::methodExecudedByTimer(){
foreach(int i, map.keys()){
QCommandLinkButton* buttonEmail = new QCommandLinkButton(this);
Email em = map[i];
buttonEmail->setText(em.__toString());
ui->verticalLayout->addWidget(buttonEmail);
}
ui->scrollArea->takeWidget();
QWidget *widget = new QWidget();
QSize size = ui->scrollArea->size();
widget->setMinimumSize(size.width(),size.height());
widget->setLayout(ui->verticalLayout);
ui->scrollArea->setWidget(widget);
}
I found the answer in the depths of Internet.
before foreach i added this:
qDebug() << "check if layout filled";
if(ui->verticalLayout->count() > 0){
qDebug() << "empty layout";
QLayoutItem *item = NULL;
while ((item = ui->verticalLayout->takeAt(0)) != 0) {
delete item->widget();
}
}else{
// on app first run
qDebug() << "layout empty";
}