Название: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 13, 2017, 12:49
Доброго времени суток. Имею дерево (QTreeView), которое содержит список сетей: 255.255, Для каждой сети сегментов: 255.255.255, Для каждого сегмента ip-адресов: 255.255.255.255 При клике на Item, отвечающий за сегмент, открывается QTableView, при открытии этой таблицы создаётся модель, содержащая строки с ip-адресами, и некоторой информацией об этом устройстве. Самый первый Item имеет тип IpStandardItemWithThread, наследуемый от QStandardItem и QObject. При клике на другой item в дереве – строки в QTableView удаляются. Суть в чём. В классе IpStandardItemWithThread я создаю объект класса Ping, который перемещаю в созданный в IpStandardItemWithThread поток (moveToThread()) . В классе Ping имеется функция, в которой в бесконечном цикле происходит пингование ip-адреса. Возникли 2 проблемы: Во-первых, при таком коннекте connect(itemPing,&Ping::isPing,[this](QString,bool statusPing){} Вываливаются вот такие ошибки. QObject::connect: Cannot queue arguments of type 'QVector<int>' (Make sure 'QVector<int>' is registered using qRegisterMetaType().) QObject::connect: Cannot queue arguments of type 'QVector<int>' (Make sure 'QVector<int>' is registered using qRegisterMetaType().) QObject::connect: Cannot queue arguments of type 'QVector<int>' (Make sure 'QVector<int>' is registered using qRegisterMetaType().) QObject::connect: Cannot queue arguments of type 'QVector<int>' (Make sure 'QVector<int>' is registered using qRegisterMetaType().)
Как от них избавиться? Во-вторых, как по дестрою грамотно завершить поток и удалить объекты классов QThread и Ping? При нынешнем варианте после клика на другой item вылетает программа, хотя должен завершаться поток и должна очищаться память, выделенная под объекты. Класс пинга: #ifndef PING_H #define PING_H
#include "winsock2.h" #include "iphlpapi.h" #include "icmpapi.h"
#include <QObject> #include <QString>
class Ping : public QObject { Q_OBJECT private:
HANDLE hIcmpFile; // Обработчик unsigned long ipaddr; // Адрес назначения DWORD dwRetVal; // Количество ответов char SendData[32]; // Буффер отсылаемых данных LPVOID ReplyBuffer; // Буффер ответов DWORD ReplySize;
QString _ipAddress; int _pingInterval; public: explicit Ping(QString ipAddress,int pingInterval,QObject *parent = 0); ~Ping();
signals:
void isPing(QString ipAddress,bool is_p);
public slots: void windowsPing(); };
#endif // PING_H
#include "ping.h" #include <QDebug>
Ping::Ping(QString ipAddress,int pingInterval,QObject *parent) : QObject(parent), _pingInterval(pingInterval), _ipAddress(ipAddress), ipaddr(INADDR_NONE), dwRetVal(0), //SendData("Data Buffer"), ReplyBuffer(NULL), ReplySize(0) { strcpy(SendData,"Data Buffer");
// Установка ip-адреса
ipaddr = inet_addr(_ipAddress.toStdString().c_str()); hIcmpFile = IcmpCreateFile(); // создание обработчика
// Выделение памяти под буффер ответов
ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData); ReplyBuffer = (VOID*) malloc(ReplySize);
}
Ping::~Ping() { if(ReplyBuffer) free(ReplyBuffer); }
void Ping::windowsPing() { while(true) { // Вызов функции ICMP эхо запроса dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData), NULL, ReplyBuffer, ReplySize, 100); emit isPing(_ipAddress,(dwRetVal != 0)); Sleep(_pingInterval); } }
Класс модели таблицы: #ifndef SEGMENTMODEL_H #define SEGMENTMODEL_H
#include <QObject> #include <QStandardItemModel> #include <QStandardItem> #include <QModelIndex> #include <QStringList> #include <QIcon> #include <QThread>
#include "centralmodel.h" #include "ping.h"
class SegmentModel; class IpStandardItemWithThread;
class SegmentModel : public QStandardItemModel { Q_OBJECT public: SegmentModel(QObject *parent = NULL); private: QModelIndex _segmentModel; QString _segmentAddress; QString _segmentName;
void insertRow(HostInfo ipInf);
public slots: void setNewSegment(QModelIndex segmentModel); void clearAll(); };
class IpStandardItemWithThread : public QStandardItem, public QObject { public: IpStandardItemWithThread(QString ipAddress,int pingInterval); ~IpStandardItemWithThread(); private: QString _ipAddress; int _pingInterval; QThread *ipThread; Ping *itemPing; };
#endif // SEGMENTMODEL_H
#include "segmentmodel.h"
SegmentModel::SegmentModel(QObject *parent): QStandardItemModel(parent) { this->setHorizontalHeaderLabels(QStringList() << tr("Д") << tr("IP") << tr("NetBIOS") << tr("MAC") << tr("Тип устройства") << tr("Операционная\nсистема") << tr("Комментарий") << tr("Р")); }
void SegmentModel::insertRow(HostInfo ipInf) {
IpStandardItemWithThread *item0 = new IpStandardItemWithThread(ipInf.IpAddress,3000); item0->setEditable(false); item0->setSelectable(false);
QStandardItem *item1 = new QStandardItem(ipInf.IpAddress); item1->setEditable(false); item1->setSelectable(false);
QStandardItem *item2 = new QStandardItem(ipInf.NetBIOS); item2->setEditable(false); item2->setSelectable(false);
QStandardItem *item3 = new QStandardItem(ipInf.MAC); item3->setEditable(false); item3->setSelectable(false);
QStandardItem *item4 = new QStandardItem(ipInf.DeviceType); item4->setEditable(false); item4->setSelectable(false);
QStandardItem *item5 = new QStandardItem(ipInf.OS); item5->setEditable(false); item5->setSelectable(false);
QStandardItem *item6 = new QStandardItem(ipInf.comment); item6->setEditable(false); item6->setSelectable(false);
QStandardItem *item7 = new QStandardItem(""); item7->setEditable(false); item7->setSelectable(false);
this->appendRow(QList<QStandardItem *>() << item0 << item1 << item2 << item3 << item4 << item5 << item6 << item7); }
void SegmentModel::setNewSegment(QModelIndex segmentModel) { _segmentModel = segmentModel; _segmentAddress = _segmentModel.data().toString(); _segmentName = _segmentModel.sibling(_segmentModel.row(),1).data().toString();
QStandardItem *segmentItem = ((CentralModel*)(_segmentModel.model()))->itemFromIndex(_segmentModel);
for(int i=0;i<segmentItem->rowCount();i++) { IpStandardItem *ipItem = (IpStandardItem *)segmentItem->child(i,0); insertRow(ipItem->getIpInformation()); }
}
void SegmentModel::clearAll() { this->removeRows(0,this->rowCount()); }
IpStandardItemWithThread::IpStandardItemWithThread(QString ipAddress, int pingInterval): _ipAddress(ipAddress), _pingInterval(pingInterval) { this->setIcon(QIcon(":/icons/ping_unknown.png"));
ipThread = new QThread();
itemPing = new Ping(_ipAddress,_pingInterval);
itemPing->moveToThread(ipThread);
connect(itemPing,&Ping::isPing,[this](QString,bool statusPing){ if(statusPing) { this->setIcon(QIcon(":/icons/ping_on.png")); } else { this->setIcon(QIcon(":/icons/ping_off.png")); } });
connect(ipThread,SIGNAL(started()),itemPing,SLOT(windowsPing()));
connect(ipThread,SIGNAL(finished()),itemPing,SLOT(deleteLater()));
ipThread->start(); }
IpStandardItemWithThread::~IpStandardItemWithThread() {
if(ipThread->isRunning()) { ipThread->terminate();
} }
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: kuzulis от Ноябрь 13, 2017, 13:11
А не проще ли юзать QProcess && утилиту ping?
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 13, 2017, 15:05
А не проще ли юзать QProcess && утилиту ping?
А как это поможет в решении моей проблемы?
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: __Heaven__ от Ноябрь 13, 2017, 15:24
А как это поможет в решении моей проблемы?
Товарищ kuzulis предлагает использовать процесс вместо потока. А за освобождение ресурсов процесса заботится уже ОС.
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 13, 2017, 16:32
Не подходит этот вариант из-за того, что системный пинг (то бишь утилита ping) требует определённое время для своей работы.
Через winAPI делается это очень быстро.
Я уже не знаю. Начитался супер "мудрых" статей о том, что согласно ООП не кошерно наследовать QThread и переопределять run(). Сделал по их статьям movetothread(). А как грамотно завершить поток так никто нигде не может сказать.
Неужели такая жесть с этими потоками?
Как его просто завершить?
Я уже вместо true использую bool переменную в while, чтобы выйти из цикла.
И всё равно thread.isRunning().
Как его завершить-то?
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Igors от Ноябрь 13, 2017, 17:00
Я уже вместо true использую bool переменную в while, чтобы выйти из цикла.
Способ правильный и фактически единственный. Нитка должна сама завершиться, вынудить ее - себе дороже И всё равно thread.isRunning().
Значит что-то не так. Остановитесь в отладчике и посмотрите что происходит
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: kuzulis от Ноябрь 13, 2017, 17:13
требует определённое время для своей работы.
зависит от аргументов.. можно и навечно запустить
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: __Heaven__ от Ноябрь 13, 2017, 23:45
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: qate от Ноябрь 14, 2017, 08:16
Через winAPI делается это очень быстро.
аватарку пингвина для отпугивания bsd демонов поставил ? ) а если через потоки, так тут qtconcurrent напрашивается
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 14, 2017, 11:21
аватарку пингвина для отпугивания bsd демонов поставил ? )
На работе ОС выбирать не приходится
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 14, 2017, 11:25
а если через потоки, так тут qtconcurrent напрашивается
Есть простейший пример запуска QConcurent и завершения по сигналу или дестрою? Допустим есть класс, в котором есть функция doWork в ней while(true) { ping; emit status_ping(); } Как это реализовать?
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: zhbr от Ноябрь 14, 2017, 11:38
Во-первых, при таком коннекте connect(itemPing,&Ping::isPing,[this](QString,bool statusPing){} Вываливаются вот такие ошибки.
Как от них избавиться?
чтонить типа `qRegisterMetaType<QVector<int>>("QVector<int>");` вставить кудато в начало программы (главное чтобы выполнилось до connect) Во-вторых, как по дестрою грамотно завершить поток и удалить объекты классов QThread и Ping?
по дестрою чего? я бы запускал пинг через QtConcurent::run(). но если хочешь с QThread, то важно чтобы метод Ping::windowsPing() закончил выполнение (прервать выполнение цикла либо через условие в заголовке цикла, либо внутри break по условию), затем вызвать thread.quit() (можно в конце Ping::windowsPing() эмитировать сигнал, предварительно связав его с thread.quit()), ну и потом можно удалять объекты.
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: qate от Ноябрь 14, 2017, 13:28
Как это реализовать?
QFutureWatcher<QByteArray>* fw = new QFutureWatcher<QByteArray>(this);
fw->setFuture(QtConcurrent::run ( [=]() {
// YOUR CODE HERE
QProcess p; p.start("/bin/bash", QStringList() << "-c" << "ping -c 1 -w 2 8.8.8.8"); p.waitForFinished(); return p.readAll().simplified(); }));
connect(fw, &QFutureWatcher<QString>::finished, this, [=] { qDebug() << fw->result(); });
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 14, 2017, 16:01
Как это реализовать?
QFutureWatcher<QByteArray>* fw = new QFutureWatcher<QByteArray>(this);
fw->setFuture(QtConcurrent::run ( [=]() {
// YOUR CODE HERE
QProcess p; p.start("/bin/bash", QStringList() << "-c" << "ping -c 1 -w 2 8.8.8.8"); p.waitForFinished(); return p.readAll().simplified(); }));
connect(fw, &QFutureWatcher<QString>::finished, this, [=] { qDebug() << fw->result(); });
Вынуждаете использовать системный пинг))) Ну, ладно, буду его использовать. Я в Windows столкнулся с такой проблемой, что в зависимости от языка ОС - вывод в терминале пинга - тоже на разных языках. А это значит, что обработка вывода будет привязана к языку. А там особо привязаться не к чему при парсинге. В Линукс в этом плане лучше - вывод пинга всегда на английском.
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: qate от Ноябрь 14, 2017, 16:24
Я в Windows столкнулся с такой проблемой, что в зависимости от языка ОС - вывод в терминале пинга - тоже на разных языках.
chcp 437 && ping -n 1 -w 2 8.8.8.8
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 15, 2017, 09:31
Я в Windows столкнулся с такой проблемой, что в зависимости от языка ОС - вывод в терминале пинга - тоже на разных языках.
chcp 437 && ping -n 1 -w 2 8.8.8.8 А как запустить эти 2 процесса вместе в QProcess? Не получается.
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 15, 2017, 10:51
Сделал процесс в QConcurrent::run(). Однако, при удалении моего объекта класса, наследуемого от QStandardItem (в котором всё и происходит), вылетает программа. Ошибки связаны с памятью. ASSERT: "pid" in file io\qprocess_win.cpp, line 771 Invalid parameter passed to C runtime function. Invalid parameter passed to C runtime function. QWaitCondition: Destroyed while threads are still waiting QWaitCondition: Destroyed while threads are still waiting QWaitCondition: Destroyed while threads are still waiting QWaitCondition: Destroyed while threads are still waiting Вот мой участок кода, отвечающий за это. IpStandardItemWithThread::IpStandardItemWithThread(QString ipAddress, int pingInterval): _ipAddress(ipAddress), _pingInterval(pingInterval), unknownIcon(QIcon(":/icons/ping_unknown.png")), onIcon(QIcon(":/icons/ping_on.png")), offIcon(QIcon(":/icons/ping_off.png")) { this->setIcon(unknownIcon);
QFutureWatcher<QByteArray>* fw = new QFutureWatcher<QByteArray>(this);
connect(fw, &QFutureWatcher<QString>::finished, this, [=] { qDebug() << fw->result(); });
fw->setFuture(QtConcurrent::run ( [=]() {
// YOUR CODE HERE
QProcess p;
connect(this,&IpStandardItemWithThread::closeProcess,[this,&p](){ if(p.isOpen()) { p.kill(); p.waitForFinished();
qDebug() << p.isOpen(); } });
p.start("C:/Windows/System32/PING.EXE",QStringList() << "-n" << "1" << "-w" << "2" << _ipAddress);
qApp->processEvents(); p.waitForFinished(); qApp->processEvents();
return p.readAll().simplified(); }));
}
IpStandardItemWithThread::~IpStandardItemWithThread() { //itemPing->setWorkFalse();
emit closeProcess(); }
Суть в чём. Ещё раз повторюсь. Есть объект QTableView. Каждый раз при клике на айтем второго уровня в QTreeView у меня создаются айтемы для QTableView. При клике на другой айтем в QTreeView – вызываю функцию clearAll, которая делает removerows для модели QTableView. То, есть удаляет все айтемы в QTableView. Один из айтемов в модели QTableView я изменил. Сделал наследование от QStandardItem, код этого изменённого класса представлен выше. То, есть в айтеме у меня создаётся процесс, который автоматически должен удаляться при удалении айтема в деструкторе. Но, программа вылетает. Собственно, что мне надо. Чтобы происходил пинг для каждого ip-адреса в таблице и прекращался при закрытии таблицы. А после прекращения, чтобы очищалась память, выделенная под пинги в айтеме.
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: qate от Ноябрь 15, 2017, 12:42
Я в Windows столкнулся с такой проблемой, что в зависимости от языка ОС - вывод в терминале пинга - тоже на разных языках.
chcp 437 && ping -n 1 -w 2 8.8.8.8 А как запустить эти 2 процесса вместе в QProcess? Не получается. это не два процесса, а один - это команда для cmd.exe /с
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 15, 2017, 14:54
Я в Windows столкнулся с такой проблемой, что в зависимости от языка ОС - вывод в терминале пинга - тоже на разных языках.
chcp 437 && ping -n 1 -w 2 8.8.8.8 А как запустить эти 2 процесса вместе в QProcess? Не получается. это не два процесса, а один - это команда для cmd.exe /с Так proc->start("C:/Windows/System32/CMD.EXE",QStringList() << "/c" << "C:/Windows/System32/CHCP.COM" << "437" << "&&" << "C:/Windows/System32/PING.EXE" << "-n" << "1" << "-w" << "2" << _ipAddress); не работает. Так proc->start("C:/Windows/System32/CMD.EXE",QStringList() << "/c" << "C:/Windows/System32/CHCP.COM 437" << "&&" << "C:/Windows/System32/PING.EXE -n 1 -w 2 " + _ipAddress); тоже не работает. Для одного пинга работает: proc->start("C:/Windows/System32/PING.EXE",QStringList() << "-n" << "1" << "-w" << "2" << _ipAddress);
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: qate от Ноябрь 15, 2017, 15:58
process.start("cmd", QStringList() << "/C" << "chcp 437 && ping -n 1 -w 2 8.8.8.8");
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 15, 2017, 21:03
process.start("cmd", QStringList() << "/C" << "chcp 437 && ping -n 1 -w 2 8.8.8.8");
Да, это работает. Только не завершается процесс при уничтожении айтема, из-за этого вылетает программа.
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: zhbr от Ноябрь 16, 2017, 07:27
qApp->processEvents(); p.waitForFinished(); qApp->processEvents();
processEvents тут не нужен, так как внутри потока запущенного через QtConcurent::run нет ни живущих объектов ни цикла обработки событий.
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 16, 2017, 15:40
qApp->processEvents(); p.waitForFinished(); qApp->processEvents();
processEvents тут не нужен, так как внутри потока запущенного через QtConcurent::run нет ни живущих объектов ни цикла обработки событий. спасибо
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 16, 2017, 15:41
Кажется, я понял, в чём дело. Я создаю цикл в QConcurrent::run() и в условиях цикла пишу переменную, по которой собираюсь выйти из цикла. Но, суть в том, что если я изменяю значение переменной за пределами QConcurrent::run(), в самом QConcurrent::run() она остаётся всё время одинаковой. Как будто первый раз при создании QConcurrent::run() туда передались копии всех внешних переменных. Я и по указателю пытался передавать переменную work, и писал функцию возвращающую текущее значение work, всё бесполезно – внутри переменные всегда одинаковые. В связи с чем возникли 2 новых вопроса. 1) Как сделать, чтобы в QConcurrent::run() переменные ОБНОВЛЯЛИСЬ? А то у меня подозрения, что из-за этого и пинг некорректно определяется. 2) Как замедлить выполнение в цикле while внутри QConcurrent::run(), чтобы скажем раз в 500 мс выполнялось тело цикла? Никаких sleep() внутри QStandardItem нет. Спасибо. class IpStandardItemWithThread : public QStandardItem { public: IpStandardItemWithThread(QString ipAddress,int pingInterval); ~IpStandardItemWithThread(); private: QString _ipAddress; int _pingInterval; QIcon unknownIcon, onIcon,offIcon;
HANDLE hIcmpFile; // Обработчик unsigned long ipaddr; // Адрес назначения // Количество ответов char SendData[32]; // Буффер отсылаемых данных LPVOID ReplyBuffer; // Буффер ответов DWORD ReplySize;
QFuture<void> future;
bool work; };
IpStandardItemWithThread::IpStandardItemWithThread(QString ipAddress, int pingInterval): _ipAddress(ipAddress), _pingInterval(pingInterval), unknownIcon(QIcon(":/icons/ping_unknown.png")), onIcon(QIcon(":/icons/ping_on.png")), offIcon(QIcon(":/icons/ping_off.png")), ipaddr(INADDR_NONE), ReplyBuffer(NULL), ReplySize(0), work(true) {
this->setIcon(unknownIcon);
strcpy(SendData,"Data Buffer");
// Установка ip-адреса
ipaddr = inet_addr(_ipAddress.toStdString().c_str()); hIcmpFile = IcmpCreateFile(); // создание обработчика
// Выделение памяти под буффер ответов
ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData); ReplyBuffer = (VOID*) malloc(ReplySize);
future = QtConcurrent::run([this](){
while(work) { if(IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData), NULL, ReplyBuffer, ReplySize, 100)) { this->setIcon(onIcon); qDebug() << _ipAddress << work; } else { this->setIcon(offIcon); qDebug() << _ipAddress << work; } } });
}
IpStandardItemWithThread::~IpStandardItemWithThread() { work = false; future.waitForFinished();
if(ReplyBuffer) { free(ReplyBuffer); ReplyBuffer = NULL; } }
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: qate от Ноябрь 16, 2017, 15:48
1. лучше взять тогда поток, если хочется между ними бросать изменения переменных 2. тогда и замедление не нужно - будут события
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 16, 2017, 17:07
1. лучше взять тогда поток, если хочется между ними бросать изменения переменных 2. тогда и замедление не нужно - будут события
А насколько корректным является то, что я сделал теперь? Я взял таймер и засунул в него QFuture. Форма не виснит. #include "segmentmodel.h" #include "segmentwidget.h"
SegmentModel::SegmentModel() { this->setHorizontalHeaderLabels(QStringList() << tr("Д") << tr("IP") << tr("NetBIOS") << tr("MAC") << tr("Тип устройства") << tr("Операционная\nсистема") << tr("Комментарий") << tr("Р"));
}
void SegmentModel::insertRow(HostInfo ipInf) {
IpStandardItemWithThread *item0 = new IpStandardItemWithThread(ipInf.IpAddress,1000); item0->setEditable(false); item0->setSelectable(false);
connect(item0,SIGNAL(canRepaint()),this,SIGNAL(canRepaint()));
QStandardItem *item1 = new QStandardItem(ipInf.IpAddress); item1->setEditable(false); item1->setSelectable(false);
QStandardItem *item2 = new QStandardItem(ipInf.NetBIOS); item2->setEditable(false); item2->setSelectable(false);
QStandardItem *item3 = new QStandardItem(ipInf.MAC); item3->setEditable(false); item3->setSelectable(false);
QStandardItem *item4 = new QStandardItem(ipInf.DeviceType); item4->setEditable(false); item4->setSelectable(false);
QStandardItem *item5 = new QStandardItem(ipInf.OS); item5->setEditable(false); item5->setSelectable(false);
QStandardItem *item6 = new QStandardItem(ipInf.comment); item6->setEditable(false); item6->setSelectable(false);
QStandardItem *item7 = new QStandardItem(""); item7->setEditable(false); item7->setSelectable(false);
this->appendRow(QList<QStandardItem *>() << item0 << item1 << item2 << item3 << item4 << item5 << item6 << item7); }
void SegmentModel::setNewSegment(QModelIndex segmentModel) { _segmentModel = segmentModel; _segmentAddress = _segmentModel.data().toString(); _segmentName = _segmentModel.sibling(_segmentModel.row(),1).data().toString();
QStandardItem *segmentItem = ((CentralModel*)(_segmentModel.model()))->itemFromIndex(_segmentModel);
for(int i=0;i<segmentItem->rowCount();i++) { IpStandardItem *ipItem = (IpStandardItem *)segmentItem->child(i,0); insertRow(ipItem->getIpInformation()); }
}
void SegmentModel::insertClearRow() { if(this->rowCount() < 254) { this->setRowCount(this->rowCount() + 1); } }
IpStandardItemWithThread::IpStandardItemWithThread(QString ipAddress, int pingInterval): _ipAddress(ipAddress), _pingInterval(pingInterval), unknownIcon(QIcon(":/icons/ping_unknown.png")), onIcon(QIcon(":/icons/ping_on.png")), offIcon(QIcon(":/icons/ping_off.png")), ipaddr(INADDR_NONE), ReplyBuffer(NULL), ReplySize(0), testTimer(new QTimer()) {
this->setIcon(unknownIcon);
strcpy(SendData,"Data Buffer");
// Установка ip-адреса
ipaddr = inet_addr(_ipAddress.toStdString().c_str()); hIcmpFile = IcmpCreateFile(); // создание обработчика
// Выделение памяти под буффер ответов
ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData); ReplyBuffer = (VOID*) malloc(ReplySize);
connect(testTimer,&QTimer::timeout,[this](){
future = QtConcurrent::run(this,testFunction);
});
testTimer->start(_pingInterval);
}
IpStandardItemWithThread::~IpStandardItemWithThread() { testTimer->stop();
future.waitForFinished();
if(ReplyBuffer) { free(ReplyBuffer); ReplyBuffer = NULL; }
if(testTimer) { delete testTimer; testTimer = NULL; } }
void IpStandardItemWithThread::testFunction() { if(IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData), NULL, ReplyBuffer, ReplySize, 100)) { this->setIcon(onIcon); } else { this->setIcon(offIcon); }
emit canRepaint(); }
Только есть небольшая проблемка. Перерисовка иконок периодически в айтемах не происходит. Никаких методов типа repaint в QStandardItem нет. Когда кликаю мышкой на айтем, то идёт перерисовка. Я даже сделал высулку сигнала после добавления иконки. Но, дело в том, что таких айтемов одновременно может быть 254. Перерисовка всего QTreeView не хорошее дело. Всё зависает. Как можно обновить отображение айтема с иконкой?
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: kuzulis от Ноябрь 16, 2017, 19:00
Много говно-говно-кода, и, это все напоминает попытку наговнокодить еще больше, чтобы уже переливалось и "втереть нам какую-то дичь" (с). Нет желания ну никакого копаться в коде.
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 16, 2017, 21:26
Много говно-говно-кода, и, это все напоминает попытку наговнокодить еще больше, чтобы уже переливалось и "втереть нам какую-то дичь" (с). Нет желания ну никакого копаться в коде.
тут говнокод только winAPIшный сишный участок, отвечающий за пинг, который я урвал в сети, и не знаю, что там происходит. Что так вызвало отвращение, malloc что ли? Я действовал по-принципу: работает - не трожь. Или эта инициализация в конструкторе дико смотрится? _ipAddress(ipAddress), _pingInterval(pingInterval), unknownIcon(QIcon(":/icons/ping_unknown.png")), onIcon(QIcon(":/icons/ping_on.png")), offIcon(QIcon(":/icons/ping_off.png")), ipaddr(INADDR_NONE), ReplyBuffer(NULL), ReplySize(0), testTimer(new QTimer()) А так, всё чисто Qtшное. Да, и не в этом дело. Вопрос был в том, что если я в QTimer вызываю QConcurrent::run() - так нормально? Получается, что всё чистится и закрывается. И это не самое плохое решение на мой взгляд. Если с QProcess мутить, то вывод его обрабатывается дольше, да и с неудачным завершением программы потом приходится в диспетчере задач удалять cmd.exe, ping.exe, chcp.com. Завтра, как вариант, попробую repaint QTableView по таймеру сделать, чтобы всё-таки иконки в айтемах обновлялись не только при клике мышкой.
Название: Re: Как закрыть поток и очистить выделенную под него память в деструкторе?
Отправлено: Павелъ от Ноябрь 22, 2017, 13:38
Давно не было меня здесь. С таймером мутить было глупой идеей, так как поток не успевал завершиться, а таймер создавал новый. Проблему я всё-таки решил с помощью QThread. Переменную в цикле while, по которой необходимо выходить, объявил как volatile bool. Использовал также QMutex. Выкладываю участок своего говнокода, может кому-то понадобится сама идея. И да, всё-таки пришлось наследовать QThread и переопределять run(), хотя на Хабре этого делать не советуют. Заголовочник #ifndef SEGMENTMODEL_H #define SEGMENTMODEL_H
#include <QObject> #include <QStandardItemModel> #include <QStandardItem> #include <QModelIndex> #include <QStringList> #include <QIcon>
#include <QThreadPool>
#include <QThread>
#include <QMutex>
#include "centralmodel.h"
class SegmentModel; class IpStandardItemWithThread; class PingThread;
class SegmentModel : public QStandardItemModel { Q_OBJECT public: SegmentModel(QObject *parent = nullptr);
QString getSegmentAddress();
private: QModelIndex _segmentModel; QString _segmentAddress; QString _segmentName;
public slots: void setNewSegment(QModelIndex segmentModel); void insertRow(HostInfo ipInf); void removeIp(QString);
void clearAll(); };
class IpStandardItemWithThread : public QObject, public QStandardItem { Q_OBJECT public: IpStandardItemWithThread(QString ipAddress,int pingInterval); ~IpStandardItemWithThread(); private: PingThread *_pThread;
QIcon unknownIcon, onIcon,offIcon; private slots: void setItemIcon(int);
};
class PingThread : public QThread { Q_OBJECT
public: PingThread(QString ipAd,int ipInterval,QObject * parent = nullptr); private: int _pingInterval; QString _ipAddress; volatile bool _stopped; QMutex mutex; void run(); signals: void statusPing(int); public slots: void stop(); };
#endif // SEGMENTMODEL_H
сипипишник #include "segmentmodel.h" #include "segmentwidget.h"
SegmentModel::SegmentModel(QObject *parent): QStandardItemModel(parent) { }
QString SegmentModel::getSegmentAddress() { return _segmentAddress; }
void SegmentModel::insertRow(HostInfo ipInf) { if(CommonFunctions::getSegmentFromIp(ipInf.IpAddress) != _segmentAddress) return;
this->appendRow(QList<QStandardItem *>() << new IpStandardItemWithThread(ipInf.IpAddress,1000) << new QStandardItem(ipInf.IpAddress) << new QStandardItem(ipInf.NetBIOS) << new QStandardItem(ipInf.MAC) << new QStandardItem(ipInf.DeviceType) << new QStandardItem(ipInf.OS) << new QStandardItem(ipInf.comment) << new QStandardItem(""));
for(int i=0;i<this->columnCount();i++) this->item(this->rowCount() - 1,i)->setEditable(false);
}
void SegmentModel::removeIp(QString ipAddr) { if(CommonFunctions::getSegmentFromIp(ipAddr) != _segmentAddress) return; for(int i=0;i<this->rowCount();i++) { if(this->item(i,1)->text() == ipAddr) { this->removeRow(i); return; } } }
void SegmentModel::clearAll() { this->clear(); _segmentName = ""; }
void SegmentModel::setNewSegment(QModelIndex segmentModel) { this->setHorizontalHeaderLabels(QStringList() << tr("Д") << tr("IP") << tr("NetBIOS") << tr("MAC") << tr("Тип устройства") << tr("Операционная\nсистема") << tr("Комментарий") << tr("Р"));
_segmentModel = segmentModel;
_segmentAddress = _segmentModel.data().toString();
_segmentName = _segmentModel.sibling(_segmentModel.row(),1).data().toString();
QStandardItem *segmentItem = ((CentralModel*)(_segmentModel.model()))->itemFromIndex(_segmentModel);
int i;
for(i=0;i<segmentItem->rowCount();i++) { IpStandardItem *ipItem = (IpStandardItem *)segmentItem->child(i,0); insertRow(ipItem->getIpInformation()); } }
IpStandardItemWithThread::IpStandardItemWithThread(QString ipAddress, int pingInterval): unknownIcon(QIcon(":/icons/ping_unknown.png")), onIcon(QIcon(":/icons/ping_on.png")), offIcon(QIcon(":/icons/ping_off.png"))
{ this->setIcon(unknownIcon);
_pThread = new PingThread(ipAddress,pingInterval,this);
connect(_pThread,SIGNAL(statusPing(int)),this,SLOT(setItemIcon(int)));
_pThread->start();
}
IpStandardItemWithThread::~IpStandardItemWithThread() { if(_pThread->isRunning()) { _pThread->stop();
_pThread->terminate();
_pThread->wait();
} }
void IpStandardItemWithThread::setItemIcon(int pStatus) { if(pStatus) { this->setIcon(onIcon); } else { this->setIcon(offIcon); } }
PingThread::PingThread(QString ipAd, int ipInterval, QObject *parent): QThread(parent), _pingInterval(ipInterval), _ipAddress(ipAd), _stopped(false) {
}
void PingThread::run() { for(;;) { mutex.lock();
if(_stopped) { _stopped = false; mutex.unlock(); break; } mutex.unlock();
msleep(_pingInterval);
emit statusPing(CommonFunctions::isPingActive(_ipAddress)); } }
void PingThread::stop() { mutex.lock(); _stopped = true; mutex.unlock(); }
|