Handle multiple Qtcp connections concurrently to show on marble widget - qt

I have an application with qt creator that gets geocoordinate data and shows it on the map.it works fine with just one connection but when it comes to having more than once makes it a riddle.
I want to get data in a loop from several qtcp connections with different addresses and ports then put data in separated lists and after reading the list of each connection geodata add separate layers on a map in different threads which updates every 3 seconds while all of them getting data concurrently from the network packets. suppose I have many different gps receivers to gather data of their location I want to integrate it on the map.
here is my code sample:
1.define list of servers that qtcp client should be connected:
globals.cpp
#define PACKET 50
struct Connects{
static QMap<QString,int> list()
{
QMap<QString,int> m;
m["sender1.com"] = 4456;
m["sender2.com"] = 4457;
m["sender3.com"] = 4458;
return m;
}
static const QMap<QString,int> myMap;
};
QMap<QString, int> const Connects::myMap = list();
2.main window to launch map:
main.cpp
void add_layer(MarbleWidget *mapWidget,MainWindow *window,Client *client,QTimer *timer ){
//get data and append to a list every 3 sec
timer = new QTimer;
QObject::connect(timer,SIGNAL(timeout()),window,SLOT(next_client()));
QObject::connect(client,SIGNAL(Client_Connected()),window,SLOT(online_slot()));
timer->start(3000);
// Add New Layer based on positions in the list
MPaintLayer* layer = new MPaintLayer(mapWidget);
for(int j = 0; j < client->data.count(); ++j){
layer->setPosition(client->data.at(j).Lat,client->data.at(j).Lon);
}
mapWidget->addLayer(layer);
}
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
// Load Marble
MarbleWidget *mapWidget = new MarbleWidget;
mapWidget->show();
//iterate over connections address and port list
QMapIterator<QString,int> i(Connects::myMap);
while (i.hasNext()) {
i.next();
client = new Client;
client->ConnectToServer(i.key(),i.value());
//make thread pool and create new thread for adding each layer
QThreadPool pool;
QFuture<void> tr;
tr = QtConcurrent::run(&pool,add_layer,mapWidget,this,client,timer);
}
}
void MainWindow::online_slot()
{
//fetch data from the buffer and separate each entity based on size
while (client->GetLanBufferSize() >= PACKET) {
client->ReadData((char*)buffer,PACKET);
double lat,lon;
memcpy(&lat,&buffer[8],sizeof(double));
memcpy(&lon,&buffer[16],sizeof(double));
//put buffer data to target list which is defined in client
Target trg;
client->data.append(trg);
}
//remove has-readed data
for (int var = 0; var < client->data.count(); ++var) {
client->data.removeAt(var);
var--;
}
}
3.declare class for add or update a new layer based on recieved data on the map:
paintlayer.cpp
MPaintLayer::MPaintLayer(MarbleWidget* widget)
{
S_N = QPixmap(":/mode-s.png");
}
bool MPaintLayer::render( GeoPainter *painter,ViewportParams *viewport,
const QString& renderPos, GeoSceneLayer * layer )
{
//throw a tiny png file on map based on retrieved locations
for (int var = 0; var < pos.count(); ++var) {
painter->drawPixmap(pos[var],S_N));
}
return true;
}
void MPaintLayer::setPosition(double lat,double lon)
{
pos.append(GeoDataCoordinates(lat,lon,0,GeoDataCoordinates::Degree));
}
5.define a class for the format of received data:
client.h
class Target
{
public:
Target()
{
Lat = Lon = 0;
}
double Lat,Lon;
GeoDataCoordinates Position;
void update(double lat,double lon)
{
Lat = lat;
Lon = lon;
Position = GeoDataCoordinates(Lon,Lat,0,GeoDataCoordinates::Degree);
}
};
6.declare class for making new qtcp connection and receive data:
client.cpp
Client::Client(QObject *parent) : QObject(parent)
{
socket = new QTcpSocket(this);
connect(socket, SIGNAL(connected()),this, SLOT(on_connected()));
}
void Client::on_connected()
{
connect(socket, SIGNAL(disconnected()),this, SLOT(on_Disconnected()));
socket->setReadBufferSize(12e6);
emit Client_Connected();
}
bool Client::ReadData(char *buffer, int Leanth)
{
socket->read(buffer , Leanth);
return true;
}
int Client::GetLanBufferSize()
{
return socket->bytesAvailable();
}
void Client::on_Disconnected()
{
emit Client_DisConnected();
}
void Client::ConnectToServer(QString Address , int Port)
{
socket->connectToHost(Address, Port);
server = Address;
server_Port = Port;
}
the data format which is received from the senders is location of moving object something like this:
35.51243 51.22478
35.51260 51.22667
35.69270 51.03961
what is the best practice solution? because it shows nothing on the map. when I used hard code, it showed layers equivalent to each connection but reading stream of data is challengeable.
any clue would be appreciated.

You are not using QTcpSocket in its proper way.
In your Client class, you can handle all reading stuff and you won't even need those while loops. Use QIODevice::readyRead() signal and QIODevice::read(char *data, qint64 maxSize) method inside a slot which should read data and parse it.
The problem here is that it's not guaranteed that those data will be received with alignments happening on the sender side. For example, if you send >>>SOME_LENGTHY_DATA<<< from the sender, QTcpSocket may emit readyRead() signal two times and you can read >>>SOME_LENG and THY_DATA<<< in two different calls to your reading slot.
So, you should take care of parsing your data as data is coming in byte by byte.
You are free to buffer the incoming data somewhere in your class or just use the QTcpSocket internal buffer. You should really take care of separating message chunks that are coming in.
Here is an implementation of Client. I use QPointF to store point instead of your Target class because I don't know what it is. You are free to create your own class and append it to Client::data based on parsed lat/lon. I did not test the code but it should work.
I'm not sure if you ever need concurrency with this implementation but you won't need one IMHO.
//Client.h
class Client : public QTcpSocket
{
Q_OBJECT
public:
explicit Client(QObject *parent = nullptr);
private:
QString serverAddress;
int serverPort;
QList<QPointF> data; //you can use your own type, just for test
QByteArray lastLine;
void parseData();
signals:
void Client_Connected();
public slots:
void connectToServer(const QString& address, quint16 port);
void processData();
};
//Client.cpp
#include "client.h"
Client::Client(QObject *parent) : QTcpSocket (parent)
{
// we don't really want Client_Connected signal!
// you can use QTcpSocket::connected signal
connect(this, SIGNAL(connected()),this, SIGNAL(Client_Connected()));
// readyRead will be emitted when there is data in buffer to be read
connect(this, SIGNAL(readyRead()),this, SLOT(processData()));
}
void Client::parseData()
{
QString str(lastLine);
str = str.trimmed();
QStringList parts = str.split(" ",QString::SkipEmptyParts);
if(parts.count()==2){
bool success = true;
double latitude = success ? parts[0].toDouble(&success) : 0;
double longitude = success ? parts[1].toDouble(&success) : 0;
if(success)
data.append(QPointF(longitude,latitude));
}
}
void Client::connectToServer(const QString& address, quint16 port)
{
data.clear();
lastLine.clear();
serverPort = port;
serverAddress = address;
this->connectToHost(address,port);
}
void Client::processData()
{
QByteArray d = this->readAll();
for(int i = 0 ; i < d.count() ; ++i){
if(d.at(i)=='\r')
{
parseData();
lastLine.clear();
}
else
lastLine.append(d.at(i));
}
}

Related

Usage of std::unique_ptr when moving object to worker thread in Qt

I am trying to update old code by removing class destructors. Now I got to the following code situation with a Qt master and a Qt slave (the slave is created, and moved to a second thread afterwards):
My slave was formerly written as:
serial_controller_worker::serial_controller_worker(const QString &portname, int waitTimeout, int BaudRate, int numStopBits, bool parity, bool useParity, bool useHex)
{
this->portName = portname;
this->waitTimeout = waitTimeout;
this->baudrate = BaudRate;
this->numStopBits = numStopBits;
this->useParity = useParity;
this->parity = parity;
this->useHex = useHex;
this->serial = new QSerialPort(this);
this->storage = "";
this->delay_write = 0;
}
serial_controller_worker::~serial_controller_worker()
{
if(this->serial->isOpen())
this->serial->close();
if(this->serial != NULL)
delete this->serial;
}
and was called in the master as
serial_controller_worker *newWorker = new serial_controller_worker(portName, waitTimeout, BaudRate, numStopBits, parity, useParity, useHex);
newWorker->moveToThread(&workerThread);
this->Hex = Hex;
connect(&workerThread, &QThread::finished, newWorker, &QObject::deleteLater);
connect(this, &Serial_port_library::newTransaction, newWorker, &serial_controller_worker::transaction);
connect(this, &Serial_port_library::connect_now, newWorker, &serial_controller_worker::connectToSerial);
connect(newWorker, &serial_controller_worker::response, this, &Serial_port_library::response_slot);
connect(newWorker, &serial_controller_worker::error_Val, this, &Serial_port_library::connectError);
workerThread.start();
emit this->connect_now();
Now I would like to transfer the slave to a constructor-only class, thus removing the destructor in the slave class. Nevertheless I still have to keep the destruction functions. Thus I created a new function for that:
void serial_controller_worker::delete_serial_controller_worker()
{
if(this->serial->isOpen())
this->serial->close();
if(this->serial != NULL)
delete this->serial;
}
and created a std::unique_ptr with a custom destructor function:
struct WorkerFree
{
void operator() (serial_controller_worker *p) const { p->delete_serial_controller_worker(); }
};
class serial_controller_master{
private:
std::unique_ptr<serial_controller_worker, WorkerFree> serial_worker;
public:
serial_controller_master();
}
serial_controller_master::serial_controller_master()
{
serial_worker.reset(serial_controller_worker(portName, waitTimeout, BaudRate, numStopBits, parity, useParity, useHex));
serial_worker->moveToThread(&workerThread);
this->Hex = Hex;
connect(&workerThread, &QThread::finished, serial_worker, &QObject::deleteLater);
}
How can I tell Qt to use my "destructor" when calling QObject::deleteLater() instead of trying to find another destructor, and how do I use the connect-calls later correctly? Currently I get errors like
error: no matching function for call to ‘Serial_port_library::connect(QThread*, void (QThread::*)(QThread::QPrivateSignal), std::unique_ptr<serial_controller_worker, WorkerFree>&, void (QObject::*)())’
connect(&workerThread, &QThread::finished, serial_worker, &QObject::deleteLater);

Subclassing QIODevice: Wrapper for QUdpSocket

I am trying to implement my own wrapper over QUdpSocket because of it is uncomfortable to use. I can use it, but anyway I need to implement some intermediate buffer for access to QDataStream operations. In additional:
I sublass QIODevice,
header (bit simplified):
class BerUdp : public QIODevice
{
Q_OBJECT
void startup();
public:
void setHostToWrite(const QHostAddress &address, quint16 port);
void setPortToRead(quint16 port);
qint64 bytesAvailable() const;
protected: // Reimplement:
qint64 readData(char *data, qint64 maxSize);
qint64 writeData(const char *data, qint64 maxSize);
private:
QUdpSocket* dev_write; // udp socket to write
QUdpSocket* dev_read; // udp socket to read
QBuffer m_buffer; // buffer to store received datagrams
};
.cpp file:
void BerUdp::startup()
{
m_buffer.open(QIODevice::ReadWrite);
open(QIODevice::ReadWrite);
}
void BerUdp::setHostToWrite(const QHostAddress &address, quint16 port)
{
dev_write->connectToHost(address, port);
dev_write->waitForConnected();
}
void BerUdp::setPortToRead(quint16 port)
{
dev_read->bind(port);
dev_read->open(QIODevice::ReadOnly);
bool ok = connect(dev_read, &QIODevice::readyRead,
this, &BerUdp::onReceive);
}
// Read new received data to my internal buffer
void BerUdp::onReceive()
{
bool av = dev_read->hasPendingDatagrams();
if (av)
{
int dSize = dev_read->pendingDatagramSize();
QByteArray dtg(dSize, 0);
dev_read->readDatagram(dtg.data(), dtg.size());
// write new data to the end
int buf_read_pos = m_buffer.pos();
m_buffer.seek(m_buffer.size());
m_buffer.write(dtg);
m_buffer.seek(buf_read_pos);
}
}
From the Qt documuntation on QIODevice::readData()
..When reimplementing this function it is important that this function
reads all the required data before returning. This is required in
order for QDataStream to be able to operate on the class. QDataStream
assumes all the requested information was read and therefore does not
retry reading if there was a problem...:
// Main read data function. There are only 4 bytes required, but maxSize == 0x4000 here:
qint64 BerUdp::readData(char *data, qint64 maxSize)
{
int n = m_buffer.read(data, maxSize);
// clear the data which has already read:
QByteArray& ba = m_buffer.buffer();
ba.remove(0, n); // m_buffer.size() == 0 after this
m_buffer.seek(0);
return n;
}
The problem is that after the first read I have empty buffer, and therefore my bytesAvailable() method returns 0:
qint64 BerUdp::bytesAvailable() const
{
qint64 my_size = m_buffer.size();
qint64 builtin_size = QIODevice::bytesAvailable();
return (my_size + builtin_size); // == 0 after 1st read
}
So I can not find out how many bytes are available, when use the class, for example:
BerUdp udp;
QDataStream stream;
stream.setDevice(&udp);
...
QIODevice* dev = stream.device(); // 19 bytes available here
if (dev->bytesAvailable() > 0) // == true
{
quint32 val;
stream >> val;
}
if (dev->bytesAvailable() > 0) // == false
{
//...
}
How can own wrapper of QUdpSocket be written correctly?
The idea of using intermediate buffer worked well, until I decided to move the logic to the separate QIODevice-derived class.
In the process of debugging with the Qt sources, it was found out that I should set the isSequential() property to true. Now my class works correct.
bool BerUdp::isSequential() const
{
return true;
}
Full class:
BerUdp.h
#pragma once
#include <QIODevice>
#include <QBuffer>
class QUdpSocket;
class QHostAddress;
class BerUdp : public QIODevice
{
Q_OBJECT
public:
BerUdp(QObject *parent = 0);
void startup();
void setHostToWrite(const QHostAddress &address, quint16 port);
void setPortToRead(quint16 port);
bool flush();
qint64 bytesAvailable() const;
bool waitForReadyRead(int msecs);
bool isSequential() const;
protected: // Main necessary reimplement
qint64 readData(char *data, qint64 maxSize);
qint64 writeData(const char *data, qint64 maxSize);
private slots:
void onReceive();
private:
void read_udp_datagram();
void write_new_data_to_buffer(QByteArray dtg);
private:
QUdpSocket* dev_write; // One udp socket to write
QUdpSocket* dev_read; // Another udp socket to read
// intermediate buffer to store received datagrams
// and to provide access to read- and QDataStream- operations
QBuffer m_buffer;
};
BerUdp.cpp
#include "BerUdp.h"
#include <QUdpSocket>
BerUdp::BerUdp(QObject *parent)
: QIODevice(parent)
{
startup();
}
// Initialization
void BerUdp::startup()
{
dev_write = new QUdpSocket(this);
dev_read = new QUdpSocket(this);
m_buffer.open(QIODevice::ReadWrite);
open(QIODevice::ReadWrite);
}
// Set a virtual connection to "host"
void BerUdp::setHostToWrite(const QHostAddress &address, quint16 port)
{
dev_write->connectToHost(address, port);
dev_write->waitForConnected();
}
// Bind a port for receive datagrams
void BerUdp::setPortToRead(quint16 port)
{
dev_read->bind(port);
dev_read->open(QIODevice::ReadOnly);
connect(dev_read, &QIODevice::readyRead,
this, &QIODevice::readyRead);
connect(dev_read, &QIODevice::readyRead,
this, &BerUdp::onReceive);
}
// Flush written data
bool BerUdp::flush()
{
return dev_write->flush();
}
// Returns the number of bytes that are available for reading.
// Subclasses that reimplement this function must call
// the base implementation in order to include the size of the buffer of QIODevice.
qint64 BerUdp::bytesAvailable() const
{
qint64 my_size = m_buffer.size();
qint64 builtin_size = QIODevice::bytesAvailable();
return (my_size + builtin_size);
}
bool BerUdp::waitForReadyRead(int msecs)
{
return dev_read->waitForReadyRead(msecs);
}
// Socket device should give sequential access
bool BerUdp::isSequential() const
{
return true;
}
// This function is called by QIODevice.
// It is main function for provide access to read data from QIODevice-derived class.
// (Should be reimplemented when creating a subclass of QIODevice).
qint64 BerUdp::readData(char *data, qint64 maxSize)
{
int n = m_buffer.read(data, maxSize);
// clear the data which has already been read
QByteArray& ba = m_buffer.buffer();
ba.remove(0, n);
m_buffer.seek(0);
return n;
}
// This function is called by QIODevice.
// It is main function for provide access to write data to QIODevice-derived class.
// (Should be reimplemented when creating a subclass of QIODevice).
qint64 BerUdp::writeData(const char *data, qint64 maxSize)
{
return dev_write->write(data, maxSize);
}
// Read new available datagram
void BerUdp::read_udp_datagram()
{
int dSize = dev_read->pendingDatagramSize();
QByteArray dtg(dSize, 0);
dev_read->readDatagram(dtg.data(), dtg.size());
write_new_data_to_buffer(dtg);
}
// Write received data to the end of internal intermediate buffer
void BerUdp::write_new_data_to_buffer(QByteArray dtg)
{
int buf_read_pos = m_buffer.pos();
m_buffer.seek(m_buffer.size());
m_buffer.write(dtg);
m_buffer.seek(buf_read_pos);
}
// Is called on readyRead signal
void BerUdp::onReceive()
{
bool available = dev_read->hasPendingDatagrams();
if (available)
{
read_udp_datagram();
}
}

Qt Unique connection

I was wondering either there is a possibility in Qt, co create a signal-slot connection, that will automatically break all other connections to this particular slot/ signal? I would appreciate all help.
Qt doesn't provide such functionality directly. Moreover, it's impossible to iterate signal-slot connections, so you can't even implement it yourself in general.
What you should be doing is keeping track of the connections that you initiate yourself, and removing them as appropriate.
For example:
enum class ConnectionDisposal { Dispose, Keep };
class UniqueConnector {
Q_DISABLE_COPY(UniqueConnector)
QMetaObject::Connection m_conn;
ConnectionDisposal m_cd;
public:
explicit UniqueConnector(ConnectionDisposal cd = ConnectionDisposal::Dispose) :
m_cd(cd) {}
~UniqueConnector() { if (m_cd == ConnectionDisposal::Dispose) disconnect(); }
template <typename T, typename R>
QMetaObject::Connection connect(const QObject * tx, T txf,
const QObject * rx, R rxf,
Qt::ConnectionType type = Qt::AutoConnection) {
QObject::disconnect(m_conn);
return m_conn = QObject::connect(tx, txf, rx, rxf, type);
}
template <typename T, typename R>
QMetaObject::Connection connect(const QObject * tx, T txf, R rxf) {
QObject::disconnect(m_conn);
return m_conn = QObject::connect(tx, txf, rxf);
}
bool disconnect() { return QObject::disconnect(m_conn); }
};
The UniqueConnector allows only one connection to exist on its instance. So, for each unique connection, you need one UniqueConnector instance. The connection is removed upon destruction of the connector, unless you specify otherwise.
So, you can use following scenario:
if (!connect(senderObject, SIGNAL(signalName()), receiverObject, SLOT(slotName()), Qt::UniqueConnection))
{
QMetaObject::disconnect(senderObject, senderObject->metaObject()->indexOfSignal(SIGNAL(signalName())),
NULL, receiverObject->metaObject()->indexOfSlot(SLOT(slotName())));
connect(senderObject, SIGNAL(signalName()), receiverObject, SLOT(slotName()));
}
I wrote this function very quickly and tested it, it seems that it really works! Yes, algorithm is not perfect, it probably can be improved, but it requires more time. Try this solution and tell result:
QMetaObject::Connection uniqueConnect(const QObject *sender, const char *signal, const QObject *receiver , const char *slot, Qt::ConnectionType type = Qt::AutoConnection)
{
const QMetaObject * metaSender = sender->metaObject();
const QMetaObject * metaReceiver = receiver->metaObject();
int signalIndex = metaSender->indexOfSignal(signal);
int slotIndex = metaReceiver->indexOfSlot(slot);
//iterate throw all methods! and discover only signals and slots
for (int i = 0; i < metaSender->methodCount(); ++i)
{
for (int j = 0; j < metaReceiver->methodCount(); ++j)
{
if(metaSender->method(i).methodType() == QMetaMethod::Signal)
{
if(metaReceiver->method(j).methodType() == QMetaMethod::Slot)
{
//immitate SIGNAL SLOT macro, see more in the end of the answer.
QByteArray finalSignal = "2" + metaSender->method(i).methodSignature();
QByteArray finalSlot = "1" + metaReceiver->method(j).methodSignature();
QObject::disconnect(sender,finalSignal.data(),receiver,finalSlot.data());
}
}
}
}
return QObject::connect(sender,signal,receiver,slot,type);
}
Test:
QObject *obj = new QObject;
connect(obj,SIGNAL(objectNameChanged(QString)),this,SLOT(testFunc()));
connect(obj,SIGNAL(destroyed()),this,SLOT(testFunc()));
obj->setObjectName("newNAme");
uniqueConnect(obj,SIGNAL(objectNameChanged(QString)),this,SLOT(showMaximized()));
obj->setObjectName("more");
Output:
testFunc called once!!!
...maximized window...
How to immitate SIGNAL SLOT macro.

Open QDialog and run the QProcess simultaneously

This is my btconnect.h file
#ifndef BTCONNECT_H
#define BTCONNECT_H
#include "scandialog.h"
namespace Ui {
class BTConnect;
}
class BTConnect : public QWidget
{
Q_OBJECT
public:
explicit BTConnect(QWidget *parent = 0);
~BTConnect();
private slots:
void on_ScanButton_clicked();
void ScanBTDevices();
//some slots here
void ScanDialogShow();
void ScanDialogClose();
public slots:
//some slots here
private:
Ui::BTConnect *ui;
QProcess BTscan_Process;
scanDialog *scan;
};
#endif // BTCONNECT_H
btconnect.cpp
BTConnect::BTConnect(QWidget *parent) :
QWidget(parent),
ui(new Ui::BTConnect)
{
//set the userinterface as BTConnect.ui
ui->setupUi(this);
scan = new scanDialog(this);
}
void BTConnect::ScanDialogShow()
{
scan->show();
}
void BTConnect::ScanDialogClose()
{
scan->close();
}
void BTConnect::ScanBTDevices()
{
ScanDialogShow();
//Command to scan nearby bluetooth devices
//"hcitool scan"
QString cmd("hcitool scan");
//start the process
BTscan_Process.start(cmd);
//Wait for the processs to finish with a timeout of 20 seconds
if(BTscan_Process.waitForFinished(20000))
{
//Clear the list widget
this->ui->listWidget->clear();
//Read the command line output and store it in QString out
QString out(BTscan_Process.readAllStandardOutput());
//Split the QString every new line and save theve in a QStringList
QStringList OutSplit = out.split("\n");
//Parse the QStringList in btCellsParser
btCellsParser cp(OutSplit);
for(unsigned int i = 0; i<cp.count(); i++)
{
//writing in listwidget
}
}
ScanDialogClose();
}
void BTConnect::on_ScanButton_clicked()
{
//Scan for available nearby bluetooth devices
ScanBTDevices();
}
if I use the above code, the qdialog scandialog does open when the process begins and closes when the data is loaded in qlistwidget, but the contents of qdialog scandialog are not displayed. If I were to change show() to exec(), the contents will be shown but the QProcess does not run until the dialog is closed.
I want the dialog to open when the Qprocess starts and close when the qlistwidget is loaded with data from the scan. And I want the contents of scandialog to be displayed. It has two labels. One with .GIF file and another with text saying scanning.
Any help is appreciated.
you never return to the event loop when you do show (because of waitForFinished) and you never continue to the processing code when you do exec
instead of the waitForFinished you should connect to the finished signal and handle it there and use a single shot timer that will cancel it:
void BTConnect::on_BTscanFinished()//new slot
{
//Clear the list widget
this->ui->listWidget->clear();
//Read the command line output and store it in QString out
QString out(BTscan_Process.readAllStandardOutput());
//Split the QString every new line and save theve in a QStringList
QStringList OutSplit = out.split("\n");
//Parse the QStringList in btCellsParser
btCellsParser cp(OutSplit);
for(unsigned int i = 0; i<cp.count(); i++)
{
//writing in listwidget
}
ScanDialogClose();
}
void BTConnect::ScanBTDevices()
{
ScanDialogShow();
//Command to scan nearby bluetooth devices
//"hcitool scan"
QString cmd("hcitool scan");
//start the process
connect(BTscan_Process, SIGNAL(finished()), this, SLOT(on_BTscanFinished()));
BTscan_Process.start(cmd);
QTimer::singleShot(20000, scan, SLOT(close()));
}
The problem is that QDialog::exec and QProcess::waitForFinished functions block event loop. Never ever block event loop. So you just need to do things more asynchronously.
QProcess class can be handled asynchronously using signals like readReadStandardOutput. And QDialog can be shown asynchronously using open slot.
The example:
void ScanBTDevices() {
// Open dialog when process is started
connect(process, SIGNAL(started()), dialog, SLOT(open()));
// Read standard output asynchronously
connect(process, SIGNAL(readyReadStandardOutput()), SLOT(onReadyRead()));
// Start process asynchronously (for example I use recursive ls)
process->start("ls -R /");
}
void onReadyRead() {
// Write to list widget
dialog->appendText(QString(process->readAllStandardOutput()));
}
The data will be appended to the dialog during generating by process.
Also using QProcess::finished signal and you can close the dialog.

How to update data in QAbstractTableModel with different row count

I am developing an application that updates the data in QTableView from apache server once per second. The server sends data as XML table. Number of columns is constant, but the number of rows changes each time. The data in the rows may also vary.
To convert the XML into the data, I created a class TxTableData, which is used in TxTableModel (child of QAbstractTableModel). Also TxTableModel uses QTimer to update data from the server.
The problem is that if the number of lines decreases - QTableview did not react to it. When the number of rows increases - it's all right.
I need remove all rows from QTableView and fill it with new data, but QTableView does not do this. Can you
class TxTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
TxTableModel(QObject *parent = 0);
void refreshData();
void parseXml(const QByteArray &xml);
public slots:
void httpDone(bool error);
void timerDone();
protected:
HttpConnect http;
TxTableData m_Data;
QTimer * timer;
};
TxTableModel::TxTableModel(QObject *parent) :
QAbstractTableModel(parent)
{
timer = new QTimer(this);
connect(&http, SIGNAL(done(bool)), this, SLOT(httpDone(bool)));
connect(timer, SIGNAL(timeout()), this, SLOT(timerDone()));
timer->start(1000);
}
void TxTableModel::refreshData()
{
TxRequest request;
request.setObject("order");
request.setMethod("getlist");
request.addParam("begin_time", 60*60*4);
request.addParam("end_time", 60*4);
http.queryAsync(request);
}
void TxTableModel::parseXml(const QByteArray &xml)
{
//qDebug() << xml;
int count = m_Data.getRowCount();
QXmlInputSource inputSource;
QXmlSimpleReader reader;
TxSaxTableHandler handler(&m_Data, false);
inputSource.setData(xml);
reader.setContentHandler(&handler);
reader.setErrorHandler(&handler);
beginResetModel();
reader.parse(inputSource);
endResetModel();
}
void TxTableModel::httpDone(bool error)
{
if (error) {
qDebug() << http.errorString();
} else {
parseXml(http.readAll());
}
}
void TxTableModel::timerDone()
{
refreshData();
}
It looks like you're not providing the full source of TxTableModel model, as it's missing implementation of rowCount, columnCount, data, setData, etc methods.
As for the problem, my guess would be:
As it was already suggested you can try cleaning the model before reloading it by calling removeRows(0, rowCount());
in your removeRows implementation, you should call beginRemoveRows before updating the rows collection and endRemoveRows after you're done. This should notify views about the model content change.
There is an example on how to implement the QAbstractTableModel here: Address Book Example
hope this helps, regards

Resources