Populate QTreeWidget with hierarchic structure from absolute filepaths - qt

I have a file which has absolute filepaths listed, 1 per line. The listed files are in order, so all files in e.g. the /Documents/ dir will be listed after eachother in the file.
What I want to do is to place all these files in a QTreeWidget in a nice hierarchic structure, just like a normal filesystem. How would I do that from my file of absolute paths I have?
This is how far I've gotten with my coding on this:
QFile file(FILENAME_ENCRYPTED);
QString line;
QDir dir;
QTreeWidgetItem *item;
if (file.open(QIODevice::ReadOnly)) {
QTextStream stream( &file );
do {
line = stream.readLine();
if (!line.isNull()) {
dir = QDir(line);
item = new QTreeWidgetItem();
item->setText(0, dir.dirName());
this->ui->treeWidget->addTopLevelItem(item);
}
} while (!line.isNull());
}
file.close();
This works fine, but it only lists all the filenames after eachother. I guess I have to do some recursive function but recursion is not my best friend, I prefer iteration! Could someone give me a push in the right direction? :)

No recursion should be necessary. You can use QString::split() to split the file path into separate QStrings in a QStringList based on a separator (i.e., "/"), then iterate through each QString to determine the file structure.
EDIT: Here is an example:
#include <QtGui>
const QString s1 = "Docs/Testing/textFile1.txt";
const QString s2 = "Docs/Testing/textFile2.txt";
const QString s3 = "Docs/Testing/textFile3.txt";
const QString s4 = "Docs/Testing/AnotherFolder/textFile4.txt";
const QString s5 = "ThisIsGonnaBeCrazy/WholeNewFolder/AndAnother/file.pdf";
const QString s6 = "ThisIsGonnaBeCrazy/file.doc";
class MainWindow : public QMainWindow
{
public:
MainWindow()
{
QTreeWidget *treeWidget = new QTreeWidget;
QStringList fileNames;
fileNames << s1 << s2 << s3 << s4 << s5 << s6;
QTreeWidgetItem *topLevelItem = NULL;
foreach (const QString &fileName, fileNames)
{
QStringList splitFileName = fileName.split("/");
// add root folder as top level item if treeWidget doesn't already have it
if (treeWidget->findItems(splitFileName[0], Qt::MatchFixedString).isEmpty())
{
topLevelItem = new QTreeWidgetItem;
topLevelItem->setText(0, splitFileName[0]);
treeWidget->addTopLevelItem(topLevelItem);
}
QTreeWidgetItem *parentItem = topLevelItem;
// iterate through non-root directories (file name comes after)
for (int i = 1; i < splitFileName.size() - 1; ++i)
{
// iterate through children of parentItem to see if this directory exists
bool thisDirectoryExists = false;
for (int j = 0; j < parentItem->childCount(); ++j)
{
if (splitFileName[i] == parentItem->child(j)->text(0))
{
thisDirectoryExists = true;
parentItem = parentItem->child(j);
break;
}
}
if (!thisDirectoryExists)
{
parentItem = new QTreeWidgetItem(parentItem);
parentItem->setText(0, splitFileName[i]);
}
}
QTreeWidgetItem *childItem = new QTreeWidgetItem(parentItem);
childItem->setText(0, splitFileName.last());
}
setCentralWidget(treeWidget);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Note that you can use QTreeWidgetItem::setData() to set the file name for each file if you like. My example does not do that, though.

This works for me:
QStringList tokens = path.split ( '/' );
QTreeWidgetItem* treeWidgetItem = NULL;
for ( int32_t j = 0 ; j < packageTreeWidget->topLevelItemCount(); ++j )
{
if ( packageTreeWidget->topLevelItem ( j )->text ( 0 ) == tokens.at ( 0 ) )
{
treeWidgetItem = packageTreeWidget->topLevelItem ( j );
break;
}
}
if ( treeWidgetItem == NULL )
{
treeWidgetItem = new QTreeWidgetItem;
treeWidgetItem->setText ( 0, tokens.at ( 0 ) );
packageTreeWidget->addTopLevelItem ( treeWidgetItem );
}
for ( int32_t j = 1; j < tokens.size(); ++j )
{
int32_t k;
for ( k = 0 ; k < treeWidgetItem->childCount(); ++k )
{
if ( treeWidgetItem->child ( k )->text ( 0 ) == tokens.at ( j ) )
{
treeWidgetItem = treeWidgetItem->child ( k );
break;
}
}
if ( k == treeWidgetItem->childCount() )
{
QTreeWidgetItem* newTreeWidgetItem = new QTreeWidgetItem;
newTreeWidgetItem->setText ( 0, tokens.at ( j ) );
treeWidgetItem->addChild ( newTreeWidgetItem );
treeWidgetItem = newTreeWidgetItem;
}
}
The index 'i' is missing from the loops because is the one I used to iterate the file paths, replace 'packageTreeWidget' with the name of your Tree Widget, I am using this to display the contents of a zip-like package file.

Related

How does QT get the ico of a process

I want to make a process list window, so I traverse the process list, but I don’t know how to get the icon and display it in the listWidget
This is my code:
#include <windows.h>
#include <psapi.h>
#include <tlhelp32.h>
#include <iostream>
HANDLE hSnap = CreateToolhelp32Snapshot(PROCESS_ALL_ACCESS, 0);
if( hSnap != INVALID_HANDLE_VALUE )
{
PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);
if( Process32First(hSnap, &pe) )
{
DWORD curID = GetCurrentThreadId();
do{
if(pe.th32ProcessID && pe.th32ProcessID != curID)
{
QListWidgetItem *it = new QListWidgetItem(
/* icon */
PaddingZero(QString::number(pe.th32ProcessID, 16)).toUpper() +"-"+ QString::fromWCharArray(pe.szExeFile),
ui->listWidget
);
ui->listWidget->addItem(it);
//printf("name: %ls, id: %d\n",pe.szExeFile,pe.th32ProcessID);
}
}while( Process32Next(hSnap, &pe) );
}
}
CloseHandle(hSnap);
How can I get the ico of each process.
After a few days, I finally found the answer
HANDLE hSnap = CreateToolhelp32Snapshot(PROCESS_ALL_ACCESS, 0);
if( hSnap != INVALID_HANDLE_VALUE )
{
PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);
if( Process32First(hSnap, &pe) )
{
do{
if(pe.th32ProcessID != GetCurrentProcessId())
{
// https://forum.qt.io/topic/62866/getting-icon-from-external-applications/5
// https://stackoverflow.com/questions/63653786/error-using-getmodulefilenameexa-function-in-qt?noredirect=1#comment112561229_63653786
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe.th32ProcessID);
wchar_t lpFilename[1024];
GetModuleFileNameExW(hProcess, NULL, lpFilename, sizeof(lpFilename));
CloseHandle(hProcess);
QFileInfo fin( QString::fromWCharArray(lpFilename) );
QFileSystemModel *model = new QFileSystemModel;
model->setRootPath(fin.path());
QIcon ic = model->fileIcon(model->index(fin.filePath()));
QListWidgetItem *it = new QListWidgetItem(
ic,
PaddingZero(QString::number(pe.th32ProcessID, 16)).toUpper() +
"-" +
QString::fromWCharArray(pe.szExeFile),
ui->list
);
ui->list->addItem(it);
//printf("name: %ls, id: %d\n",pe.szExeFile,pe.th32ProcessID);
}
}while( Process32Next(hSnap, &pe) );
}
}
CloseHandle(hSnap);

segfault when reading QTextStream

Context:
I have a set of file like that:
alias
USER = usr_name
PASSWORD = pass
...
alias2
...
I have a combobox to choose which file to display in a qtableview. The below is the slot called when the value of the combobox changes.
void paramTableModel::switchTable(QString env)
{
// is there an environment defined
// --------------------------------------
if(env.compare("") != 0)
{
// does the param file exist ?
// ---------------------------
QString file_path = "/path/to/" + env + "/path/to/file;
QFile param_file(file_path);
if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
{
QString program_decrypt = "/path/to/decrypt";
QStringList args;
args << file_path;
QProcess* process = new QProcess();
process->setReadChannel(QProcess::StandardOutput);
process->start(program_decrypt,args);
process->waitForFinished(-1);
QTextStream in(&process->readAll());
//start resetting model (notify the view )
beginResetModel();
this->table.clear();
std::set<QString> uniq;
// parse file to model
// -------------------
while( !in.atEnd() )
{
QString line = in.readLine();
// some new UT ?
// -------------------
if( !line.isNull() && line.trimmed().compare("") != 0 && !line.contains("="))
{
line = line.trimmed();
std::vector<QString> row;
if(uniq.find(line) == uniq.end())
{
uniq.insert(line);
row.push_back(line);
QString user_line = in.readLine().trimmed();
QString password_line = in.readLine().trimmed();
QString server_line = in.readLine().trimmed();
if( user_line.startsWith("USER") )
{
user_line = user_line.split('=').at(1).trimmed();
}
if( password_line.startsWith("PASSWORD") )
{
password_line = password_line.split('=').at(1).trimmed();
}
if( server_line.startsWith("SERVER") )
{
server_line = server_line.split('=').at(1).trimmed();
}
row.push_back(user_line);
row.push_back(password_line);
row.push_back(server_line);
this->table.push_back(row);
}
else
{
QMessageBox msgBox;
msgBox.setText(QString("%1 is already defined, ingnoring").arg(line));
msgBox.exec();
}
}
}
param_file.close();
endResetModel();
delete process;
}
}
}
Most of the time, my view is properly refreshed. And from time to time, this line segfaults:
QString line = in.readLine();
I have found no pattern to reproduce the bug in a systematic way. I can click back and forth between two files in the combobox, it will work ten, twelve, twenty times and then segfault.
EDIT:
This does work.
void paramTableModel::switchTable(QString env)
{
// is there an environment defined
// --------------------------------------
if(env.isEmpty())
return;
// does the param file exist ?
// --------------------------------
QString file_path = "/path/to/env/" + env + "/path/to/file.dat";
QFile param_file(file_path);
if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
{
// Call QProcess, fx_decrypt, read standard output
QString program_decrypt = "/path/to/decrypt";
QStringList args;
args << file_path;
QProcess process;
process.setReadChannel(QProcess::StandardOutput);
process.start(program_decrypt,args);
process.waitForFinished(-1);
//QTextStream in(&process->readAll());
QString out = process.readAll();
QStringList list_out = out.split("\n",QString::SkipEmptyParts);
//start resetting model (notify the view )
beginResetModel();
this->table.clear();
std::set<QString> uniq;
// parse file to model
// -----------------------
//while( !in.atEnd() )
int counter = 0;
while(counter < list_out.size())
{
//QString line = in.readLine();
QString line = list_out.at(counter);
// some new UT ?
// -------------------
if( !line.isNull() && !line.trimmed().isEmpty() != 0 && !line.contains("="))
{
line = line.trimmed();
std::vector<QString> row;
if(uniq.find(line) == uniq.end())
{
uniq.insert(line);
row.push_back(line);
/**QString user_line = in.readLine().trimmed();
QString password_line = in.readLine().trimmed();
QString server_line = in.readLine().trimmed();*/
QString user_line = list_out.at(++counter).trimmed();
QString password_line = list_out.at(++counter).trimmed();
QString server_line = list_out.at(++counter).trimmed();
if( user_line.startsWith("USER") )
{
user_line = user_line.split('=').at(1).trimmed();
}
if( password_line.startsWith("PASSWORD") )
{
password_line = password_line.split('=').at(1).trimmed();
}
if( server_line.startsWith("SERVER") )
{
server_line = server_line.split('=').at(1).trimmed();
}
row.push_back(user_line);
row.push_back(password_line);
row.push_back(server_line);
this->table.push_back(row);
}
else
{
QMessageBox msgBox;
msgBox.setText(QString("%1 is already defined, ingnoring").arg(line));
msgBox.exec();
}
}
++counter;
}
endResetModel();
}
}
Trying to get the why of the bug, following #Kuba Ober suggestion
I've tried to copy the first version into a main function looping indefinitely over all my environments:
But it does work fine!
#include <QtCore/QCoreApplication>
#include <QProcess>
#include <QTextStream>
#include <set>
#include <vector>
#include <QDebug>
#include <QFile>
#include <deque>
#include <QDir>
#include <QStringList>
std::deque< std::vector < QString > > table;
void f( QString file_path )
{
// does the param file exist ?
// --------------------------------
QFile param_file(file_path);
if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
{
// Call QProcess, fx_decrypt, read standard output
QString program_decrypt = "/path/to/decrypt";
QStringList args;
args << file_path;
QProcess* process = new QProcess();
process->setReadChannel(QProcess::StandardOutput);
process->start(program_decrypt,args);
process->waitForFinished(-1);
QTextStream in(&process->readAll());
//start resetting model (notify the view )
table.clear();
std::set<QString> uniq;
// parse file to model
// -----------------------
while( !in.atEnd() )
{
QString line = in.readLine();
// some new UT ?
// -------------------
if( !line.isNull() && line.trimmed().isEmpty() != 0 && !line.contains("="))
{
line = line.trimmed();
std::vector<QString> row;
if(uniq.find(line) == uniq.end())
{
uniq.insert(line);
row.push_back(line);
QString user_line = in.readLine().trimmed();
QString password_line = in.readLine().trimmed();
QString server_line = in.readLine().trimmed();
if( user_line.startsWith("USER") )
{
user_line = user_line.split('=').at(1).trimmed();
}
if( password_line.startsWith("PASSWORD") )
{
password_line = password_line.split('=').at(1).trimmed();
}
if( server_line.startsWith("SERVER") )
{
server_line = server_line.split('=').at(1).trimmed();
}
row.push_back(user_line);
row.push_back(password_line);
row.push_back(server_line);
table.push_back(row);
}
else
{
qDebug() << QString("%1 is already defined, ingnoring").arg(line);
}
}
}
param_file.close();
delete process;
}
}
QStringList getEnvList()
{
QDir dir("/path/to/env/");
QStringList list_env;
foreach(QString folder, dir.entryList(QStringList(),QDir::AllDirs | QDir::NoDotAndDotDot))
{
QFile app_file( dir.absolutePath() + '/' + folder + '/' + "file.dat" );
if (app_file.exists())
{
list_env.push_back(folder);
}
}
return list_env;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int counter = 0;
while(true)
{
foreach ( QString s, getEnvList() )
{
qDebug() << counter++;
qDebug() << s;
f(s);
}
}
return a.exec();
}
I have no idea, why the two below codes never crash while the first one does...

Updating QTableView with more rows dynamically

I have a QTableView, which uses a model derived from QAbstractTableModel.
The model starts out with a number of rows and displays the data properly.
The model also has a timer attached to it, which upon expiring, gets the number of rows and columns, constructs an index for it, and emits dataChanged signal, to update the dynamic contents of the table.
The problem is when the number of rows it is supposed to display, changes.
In that case, even though I obtain a new index with the changed no. of rows, and update the table, it still doesn't display any new rows.
I've identified why, perhaps. Let's say I have 2 rows in the beginning, and next, 5 rows are supposed to be displayed. In the timer expiration logic, I've asked it to construct a new index, with new row count… but dataChanged() signal, will only change the data for the rows which had already been inserted in the first. The new rows are not displayed.
This is my code for the model.
TableLayout::TableLayout()
: QAbstractTableModel()
{
rowCount();
columnCount();
timer = new QTimer(this);
timer->setInterval(3000);
timer->start();
connect(timer, SIGNAL(timeout()), this, SLOT(timerHit()));
//ItemList[0].SetFields("Blah" ,"Blah" , "Blah" , "Blah" , "Blah", "Blah", true );
//ItemList[1].SetFields("Blah1" ,"Blah1" ,"Blah1" ,"Blah1" ,"Blah1", "Blah1", true );
}
void TableLayout::timerHit()
{
int row = this->rowCount();
qDebug() << "RowCOunt::" << row;
qDebug() << " Now IT IS : " << row;
int col = this->columnCount();
qDebug() << "ColumnCOunt::" << col;
QModelIndex Ind = createIndex( 0, 0);
QModelIndex Ind1 = createIndex( row, col);
data(Ind1);
emit dataChanged(Ind, Ind1);
}
int TableLayout::rowCount(const QModelIndex& /*parent*/) const
{
RtdbReader* rtdbReader = RtdbReader::instance();
if (isConnected)
return rtdbReader->Get_Number_of_Items();
else return 2;
}
int TableLayout::columnCount(const QModelIndex& /*parent*/) const
{
return 6;
}
QVariant TableLayout::data(const QModelIndex& index, int role) const
{
int row = index.row();
int col = index.column();
//return row;
int row_count, col_count = 0;
if ( role == Qt::DisplayRole) {
if ( col == 1)
return ItemList[row]->GetExplanation();
if ( col == 2) {
qDebug() << " Now printing Sig name for row" << index.row();
return ItemList[row]->GetCond_Sig_Name();
}
if ( col == 3)
return ItemList[row]->GetDescription();
if ( col == 5)
return ItemList[row]->GetLogic();
if ( col == 4)
return ItemList[row]->Get_State_Text();
} else if ( role == Qt::DecorationRole && col == 0 ) {
bool col_to_display = ItemList[row]->GetValueColour();
qDebug() << "colour in view:" << col_to_display;
if ( col_to_display ) {
QString path = "Green.png";
//QPixmap p( s_type1Icon );
// return s_type1Icon;
QIcon icon ( path );
return icon;
} else {
QString path = "Yellow.png";
//QPixmap p( s_type1Icon );
// return s_type1Icon;
QIcon icon ( path );
return icon;
}
}
if (role == Qt::TextAlignmentRole )
return Qt::AlignCenter | Qt::AlignVCenter;
/* else if ( role == Qt::BackgroundRole)
{
QBrush yellowBackground(Qt::yellow);
return yellowBackground;
}*/
return QVariant();
}
Please suggest how I can change the view so new rows are added.
Thanks.
Just because you create a QModelIndex to the element at row, col, doesn't mean you created data for that element (and also not for the elements between the current end and that element).
You have to use setData(...) to add new data to the table, and then emit dataChanged(...) for the view to display it.

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;
};

Need example about nodes in QAbstractItemModel for QTreeView?

Problem: I'm looking example about creating model ( based on QAbstractItemModel ) to QTreeView, but can't find sane codes. Qt examples are based on QStandardModel, which is not very useful and complex, Internet examples are based on python?! codes... Other information can't gave me the right directions to go. So, here is what I have:
std::map
typedef std::map< CompanyData, std::vector< ContractorData >, LessData< CompanyData > > Companies;
Here it's data example (CompanyName + ContractorsNames):
[Microsoft]*
[Bill Gates]
[Steve Balmer]
[...]
[Apple]*
[Steve Jobs - R.I.P.]
[Wozniak]
[OtherStuff]*
...
where * means - expandable item (parent)
And all that I need it to create QTreeView with this data above!
Can any one help?
Many thanks!
So, as no posts was done here, I post here my own solution (also used text filtering):
void ContractorsFilter::onCustomFilterChanged( const QString& text )
{
try
{
struct MatchFilter
{
// data
QString filter_;
Companies& filtered_recipients_;
// methods
MatchFilter( const QString& _filter, Companies& _recipients )
: filter_( _filter )
, filtered_recipients_( _recipients )
{
filtered_recipients_.clear();
}
void operator()( const Companies::value_type& val ) const
{
bool isFound = false;
std::vector< ContractorData >::const_iterator con_i( val.second.begin() ), con_e( val.second.end() );
for( ; con_i != con_e; ++con_i )
{
const QString contractorName = (*con_i).name;
if( contractorName.contains( filter_, Qt::CaseInsensitive ) )
{
filtered_recipients_[ val.first ].push_back( (*con_i) );
isFound = true;
}
}
const QString companyName = val.first.name;
if( companyName.contains( filter_, Qt::CaseInsensitive ) )
{
filtered_recipients_[ val.first ].push_back( ContractorData() );
}
}
};
struct FillView
{
// data
QFont boldFont;
QBrush whiteBrush;
QStandardItemModel* model_;
// methods
FillView( QStandardItemModel* model )
: model_( model )
{
model_->clear();
}
void operator ()( const Companies::value_type& val ) const
{
struct AppendContractors
{
// data
QStandardItem* parent_;
// methods
AppendContractors( QStandardItem* _parent = 0 )
: parent_( _parent )
{}
bool isEmpty( const ContractorData& contractor ) const
{
return contractor.id.isEmpty();
}
void operator()( const std::vector< ContractorData >::value_type& contractor ) const
{
if( !isEmpty( contractor ) )
{
QStandardItem *item = 0;
QList< QStandardItem* > line;
line << ( item = new QStandardItem( QIcon( ACCOUNT_ITEM_ICON ), contractor.name ) );
item->setSizeHint( QSize( 0, 25 ) );
parent_->appendRow( line );
}
}
};
QStandardItem *parentItem = model_->invisibleRootItem();
// добавляем новую компанию + контрагента
QList< QStandardItem* > line;
line << ( parentItem = new QStandardItem( QIcon( COMPANY_ITEM_ICON ), val.first.name ) );
parentItem->setSizeHint( QSize( 0, 25 ) );
model_->appendRow( line );
std::for_each( val.second.begin(), val.second.end(), AppendContractors( parentItem ) );
}
};
// удаляем символ(ы), которые не фильтруются
// формируется новая таблица, которая и будет использоваться моделью для отображения
std::for_each( data_.begin(), data_.end(),
MatchFilter( text, filter_data_ ) );
// вывод отфильтрованных контрагентов
std::for_each( filter_data_.begin(), filter_data_.end(),
FillView( model_ ) );
ui_.treeView->expandAll();
}
catch( const std::exception& e )
{
Core::errLog( "ContractorsFilter::onCustomFilterChanged", e.what() );
throw;
}
}
PS: type Companies is a
typedef std::map< CompanyData, ContractorsData, LessData< CompanyData > > Companies;
where CompanyData, ContractorsData are simple structures...
Have a nice day!

Resources