Recursive function does not exit prematurely - qt

I have a recursive function to find a treeitem in the treeview from its name.
bool SumCommandInterface::getTreeItem(const std::string &stdstrName, const QModelIndex & index, TreeModel *model, TreeItem** item)
{
if (index.isValid())
{
TreeItem* currentTreeItem = model->getItem(index);
if (currentTreeItem->getName() == stdstrName)
{
*item = currentTreeItem;
return true;
}
}
if(!model->hasChildren(index) || (index.flags() & Qt::ItemNeverHasChildren))
{
return false;
}
auto rows = model->rowCount(index);
for (int i = 0; i < rows; ++i)
getTreeItem(stdstrName , model->index(i, 0, index), model , item );
return false;
}
The function still runs even after it satisfies the condition.

The problem in you code is that you don't return from the function even if a recursive function call returns true, i.e. when the condition is met. The right way to implement the recursive call would be:
[..]
for (int i = 0; i < rows; ++i)
// Return if the condition is met.
if (getTreeItem(stdstrName , model->index(i, 0, index), model, item)) {
return true;
}
[..]

Related

When finding cycles in undirected graphs, why can't we just keep track of previous parent node while using BFS traversal

Basically when we use DFS we just check if the adjacent nodes for a newly visited node have been already visited and they are not the parent node which made the DFS call for this node. If this is the case cycle is present.
I was using similar thing for BFS while keeping track of previous parent node and my logic doesn't seem to work. The test case on which my code is failing is too big to understand the problem. Can anyone let me know where my logic is broken? Thank you in advance
bool bfs(vector<int> adj[], bool isVisited[], int s, int V)
{
queue<int> q;
q.push(s);
isVisited[s] = true;
int parent = s;
int prevParent = -1;
while(q.empty() == false)
{
int u = q.front();
prevParent = parent;
parent = u;
q.pop();
for(int i = 0 ; i < adj[u].size() ; i++)
{
if(isVisited[adj[u][i]] == false)
{
isVisited[adj[u][i]] = true;
q.push(adj[u][i]);
// parent[][i]] = u;
}
else
{
if(adj[u][i] != prevParent)
return true;
}
}
}
return false;
}
bool isCycle(int V, vector<int> adj[]) {
// Code here
bool isVisited[V] = {false};
for(int i = 0 ; i < V ; i++)
{
if(isVisited[i] == false)
if(bfs(adj, isVisited, i, V) == true)
{
return true;
}
}
return false;
}

How to get keypressed event for table view

This is my code of DropMimeData for the Tree Model.
The code works as expected , the user can drag and drop a treeitem from one location in the table view to another location in the view..
I need to add a condition in mimeData function based on Cntrl KeyPressed.
1) How can i get to know if the cntrl key is pressed in the function.
bool TreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction
action, int row, int column, const QModelIndex &parent)
{
if (!mimeData->hasFormat(s_treeNodeMimeType)) {
return false;
}
QByteArray data = mimeData->data(s_treeNodeMimeType);
QDataStream stream(&data, QIODevice::ReadOnly);
qint64 senderPid;
stream >> senderPid;
if (senderPid != QCoreApplication::applicationPid()) {
return false;
}
TreeItem *parentNode = getItem(parent);
int count;
stream >> count;
if (row == -1) {
if (parent.isValid())
row = 0;
else
row = rowCount(parent);
}
for (int i = 0; i < count; ++i) {
qlonglong nodePtr;
stream >> nodePtr;
TreeItem *node = reinterpret_cast<TreeItem *>(nodePtr);
if (node->row() < row && parentNode == node->parent())
--row;
TreeItem *nodeNew = new TreeItem(node->GetContainer(), parentNode);
nodeNew->setContainer(node->GetContainer());
parentNode->insertChild(row, nodeNew);
endInsertRows();
++row;
// if( ctrl key is pressed ) while dragging and dropping item the Cntrl key is pressed
// removeItem(node);
}
return true;
}
Try this.
if (QGuiApplication::keyboardModifiers() != Qt::ControlModifier)
removeItem(node);

why are different treeItems linked to the same data

I have been trying to copy a treeitem to another location in the tree view along with its sub tree.
Finally i have been able to to move all of them successfully.
Header file for treeItem
class TreeItem
{
public:
explicit TreeItem( Container *data , TreeItem *parent = 0 );
~TreeItem();
TreeItem *parent();
void appendChild(TreeItem *child);
TreeItem& operator = (const TreeItem &item);
TreeItem *child(int iNumber);
int childCount() const;
int childNumber() const;
Container data() const ;
Container* GetContainer();
bool setData(Container* data , QVariant value);
void setContainer( Container* data);
bool insertChildren(int position, int count );
bool removeChildren( int position , int count );
void removeChild(int row);
void removeChild(TreeItem* itm);
QList<TreeItem*> children();
std::string getChildName(int row);
std::string getName();
int row() const;
void insertChild(int pos, TreeItem *child);
private:
QList<TreeItem*> childItems;
Container* itemData;
TreeItem* parentItem;
};
Cpp file for treeItem
TreeItem::TreeItem( Container *data, TreeItem *parent )
{
parentItem = parent;
itemData = new Container;
*itemData = *data;
}
TreeItem::~TreeItem()
{
if (itemData != nullptr)
{
delete itemData;
}
qDeleteAll(childItems);
}
TreeItem& TreeItem::operator = (const TreeItem &item)
{
qDebug() << "TreeItem operator called";
// if( this->itemData == nullptr)
// this->itemData = new Container;
*this->itemData = *item.itemData;
this->childItems = item.childItems;
this->parentItem = item.parentItem;
return *this;
}
TreeItem *TreeItem::parent()
{
return parentItem;
}
TreeItem *TreeItem::child(int iNumber)
{
return childItems.value(iNumber);
}
int TreeItem::childCount() const
{
return childItems.count();
}
int TreeItem::childNumber() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*> (this));
return 0;
}
Container TreeItem::data() const
{
return *itemData;
}
bool TreeItem::setData( Container* data , QVariant value )
{
//*itemData = *data; // Do Not !!!! uncomment this ///////////////////////////as it will set the value of default container constructor.
itemData->SetName(value.toString().toStdString() );
return true;
}
bool TreeItem::insertChildren(int position, int count)
{
if (position < 0 || position > childItems.count())
return false;
Container cont;
TreeItem *item = new TreeItem(&cont, this);
childItems.insert(position, item);
return true;
}
bool TreeItem::removeChildren(int position, int count)
{
if (position < 0 || position > childItems.count())
return false;
for (int row = 0; row < count; ++row)
{
delete childItems.takeAt(position);
}
return true;
}
void TreeItem::setContainer( Container* cont)
{
*itemData = *cont;
}
void TreeItem::appendChild(TreeItem *node)
{
childItems.append( node );
}
int TreeItem::row() const
{
// qDebug() << "The child count = " << parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
if (parentItem)
return parentItem->childItems.indexOf( const_cast<TreeItem*>(this) );
return 0;
}
void TreeItem::removeChild(int row)
{
// qDebug() << "The Row Number" << row;
// childItems.removeAt(row);
delete childItems.takeAt(row);
}
void TreeItem::insertChild(int pos, TreeItem *child)
{
childItems.insert(pos, child);
child->parentItem = this;
}
void TreeItem::removeChild(TreeItem* itm)
{
childItems.removeOne(itm);
}
std::string TreeItem::getChildName(int row)
{
return childItems.value(row)->getName();
}
std::string TreeItem::getName()
{
return itemData->GetName();
}
Container* TreeItem::GetContainer()
{
return itemData;
}
QList<TreeItem*> TreeItem::children()
{
return childItems;
}
This is my function to build the tree for subcontainers of the tree item in the tree structure.
void TreeModel::buildTree(TreeItem * pItem, QDataStream & ds) const
{
if (pItem == NULL)
return;
ds << reinterpret_cast<qlonglong>(pItem);
ds << pItem->childCount();
foreach(TreeItem* childItem, pItem->children())
{
buildTree(childItem, ds);
}
}
This is in the Drop mime data function where i restore the sub tree of the container.
// count is the number of child containers of the tree item
TreeItem *node;
//qDebug() << "The row" << row << parentNode->data().GetName().c_str() ;
for (int i = 0; i < count; ++i) {
// Decode data from the QMimeData
qlonglong nodePtr;
int childCount;
stream >> nodePtr;
stream >> childCount;
node = reinterpret_cast<TreeItem *>(nodePtr);
// Adjust destination row for the case of moving an item
// within the same parent, to a position further down.
// Its own removal will reduce the final row number by one.
if (node->row() < row && parentNode == node->parent())
--row;
TreeItem *nodeNew = new TreeItem(node->GetContainer(), node->parent());
nodeNew->setContainer(node->GetContainer());
// Insert at new position
//qDebug() << "Inserting into" << parent << row;
beginInsertRows(parent, row, row);
parentNode->insertChild(row, nodeNew);
endInsertRows();
for (int k = 0; k < childCount; k++)
{
restoreTree(stream, nodeNew);
}
++row;
}
This is the function to restore the sub tree.
TreeItem * TreeModel::restoreTree(QDataStream & ds , TreeItem* parent) const
{
Container cont;
// Restore the info from the stream
int childCount;
qlonglong nodePtr;
ds >> nodePtr;
ds >> childCount;
TreeItem *currentItem = reinterpret_cast<TreeItem *>(nodePtr);
if (currentItem == nullptr)
return nullptr;
TreeItem *thisItem = new TreeItem(currentItem->GetContainer(), currentItem->parent() );
thisItem->setContainer(currentItem->GetContainer());
//*thisItem = *currentItem;
parent->appendChild(thisItem);
for (auto nChild = 0; nChild < childCount; ++nChild)
{
TreeItem* oldChild = restoreTree(ds, thisItem);
if (oldChild != nullptr)
{
TreeItem *pChild = new TreeItem( oldChild->GetContainer() , oldChild->parent() );
pChild->setContainer(oldChild->GetContainer());
thisItem->appendChild(pChild);
qDebug() << "Appending child";
}
}
return thisItem;
}
The tree gets restored in the correct manner.
But now i have a issue, the treeitem in the old position and the new position are linked together , if i make any changes to the tree item in the old position than it also affects the TreeItem in the new position.
Changing parent->appendChild(thisItem); to parent->insertChild(row, thisItem); solved the problem now the BuildTree function that works.
TreeItem * TreeModel::restoreTree(QDataStream & ds , TreeItem* parent , int row) const
{
Container cont;
// Restore the info from the stream
int childCount;
qlonglong nodePtr;
QModelIndex parentIndex;
ds >> nodePtr;
ds >> childCount;
TreeItem *currentItem = reinterpret_cast<TreeItem *>(nodePtr);
if (currentItem == nullptr)
return nullptr;
TreeItem *thisItem = new TreeItem(currentItem->GetContainer(), currentItem->parent() );
thisItem->setContainer(currentItem->GetContainer());
//*thisItem = *currentItem;
// parent->appendChild(thisItem);
parent->insertChild(row, thisItem);
for (auto nChild = 0; nChild < childCount; ++nChild)
{
restoreTree(ds, thisItem , nChild);
}
return thisItem;
}
It was more of hit and try to find the solution that worked , if someone can give a explanation on the difference between parent->appendChild(thisItem); to parent->insertChild(row, thisItem) that would be great.
Or Any alternative solution.

Qt signal emit in addressbook

I am learning the Model-View in Qt by the AddressBook example. https://doc.qt.io/qt-5/qtwidgets-itemviews-addressbook-example.html
And I find something interesting. The code construct a TableModel class besed on QAbstractTableModel. In the override setData function, it emit the dataChanged signal. But, there is no signal emit in removeRows/insertRows. Then, how can these function update the View.
bool TableModel::removeRows(int position, int rows, const QModelIndex &index)
{
Q_UNUSED(index);
beginRemoveRows(QModelIndex(), position, position + rows - 1);
for (int row = 0; row < rows; ++row) {
listOfPairs.removeAt(position);
}
endRemoveRows();
return true;
}
bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
int row = index.row();
QPair<QString, QString> p = listOfPairs.value(row);
if (index.column() == 0)
p.first = value.toString();
else if (index.column() == 1)
p.second = value.toString();
else
return false;
listOfPairs.replace(row, p);
emit(dataChanged(index, index));
return true;
}
return false;
}
Make note of function calls beginRemoveRows() and endRemoveRows() in the function removeRows() of your posted code.
The beginRemoveRows() function, emits a signal rowsAboutToBeRemoved(). This is how connected views can know about the deletion and the underlying connected views must handle before data is removed.
Look the note in below documentation:
https://doc.qt.io/qt-5/qabstractitemmodel.html#beginRemoveRows

QSignalSpy to capture a reference argument

It is not possible to capture an argument that has been passed as reference with a QSignalSpy:
QSignalSpy spy( myObject, SIGNAL(foo(int&)));
...
int& i=spy.at(0).at(0).value<int&>();
Since a QVariant can not contain a reference member. Plain logic.
But are there other solutions to check the passed-in argument?
Since Qt 5, we can simply connect to a lambda function, which makes the use of the QSignalSpy unnecessary:
std::vector<Value> values;
QObject::connect(myObject, &MyObject::foo,
[&](const auto &value)
{ values.emplace_back(value); });
myObject.somethingCausingFoo();
ASSERT_EQ(1u, values.size());
EXPECT_EQ(expectedValue, values.at(0));
An "ugly solution" would be to hack the fairly simple QSignalSpy code in order to handle the reference passed arguments. I provide a minimal working example for int reference arguments. The only changes were made to initArgs and appendArgs functions.
Notice that with this approach you will only be able to check the value of the passed argument by reference. You will not be able to change it's value.
In the initArgs function we check if we have references by argument and we populate the shouldreinterpret list.
void initArgs(const QMetaMethod &member)
{
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
{
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
// Check if we have a reference by removing the & from the parameter name
QString argString(params.at(i).constData());
argString.remove("&");
tp = QMetaType::type(argString.toStdString().c_str());
if (tp != QMetaType::Void)
shouldReinterpret << true;
}
else
shouldReinterpret << false;
args << tp;
}
}
and the appendArgs function, where we reinterpret the passed by reference arguments:
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
if (shouldReinterpret.at(i))
{
switch (type)
{
case QMetaType::Int:
list << QVariant(type, &(*reinterpret_cast<int*>(a[i + 1])));
break;
// Do the same for other types
}
}
else
list << QVariant(type, a[i + 1]);
}
append(list);
}
Complete code for reference:
class MySignalSpy: public QObject, public QList<QList<QVariant> >
{
public:
MySignalSpy(QObject *obj, const char *aSignal)
{
#ifdef Q_CC_BOR
const int memberOffset = QObject::staticMetaObject.methodCount();
#else
static const int memberOffset = QObject::staticMetaObject.methodCount();
#endif
Q_ASSERT(obj);
Q_ASSERT(aSignal);
if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) {
qWarning("QSignalSpy: Not a valid signal, use the SIGNAL macro");
return;
}
QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
const QMetaObject *mo = obj->metaObject();
int sigIndex = mo->indexOfMethod(ba.constData());
if (sigIndex < 0) {
qWarning("QSignalSpy: No such signal: '%s'", ba.constData());
return;
}
if (!QMetaObject::connect(obj, sigIndex, this, memberOffset,
Qt::DirectConnection, 0)) {
qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
return;
}
sig = ba;
initArgs(mo->method(sigIndex));
}
inline bool isValid() const { return !sig.isEmpty(); }
inline QByteArray signal() const { return sig; }
int qt_metacall(QMetaObject::Call call, int methodId, void **a)
{
methodId = QObject::qt_metacall(call, methodId, a);
if (methodId < 0)
return methodId;
if (call == QMetaObject::InvokeMetaMethod) {
if (methodId == 0) {
appendArgs(a);
}
--methodId;
}
return methodId;
}
private:
void initArgs(const QMetaMethod &member)
{
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
{
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
QString argString(params.at(i).constData());
argString.remove("&");
tp = QMetaType::type(argString.toStdString().c_str());
if (tp != QMetaType::Void)
shouldReinterpret << true;
}
else
shouldReinterpret << false;
args << tp;
}
}
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
if (shouldReinterpret.at(i))
{
switch (type)
{
case QMetaType::Int:
int k = (*reinterpret_cast<int*>(a[i + 1]));
list << QVariant(type, &k);
break;
}
}
else
list << QVariant(type, a[i + 1]);
}
append(list);
}
// the full, normalized signal name
QByteArray sig;
// holds the QMetaType types for the argument list of the signal
QList<int> args;
// Holds the indexes of the arguments that
QList<bool> shouldReinterpret;
};

Resources