writeCharacters() of QXmlStreamWriter crashed when writting the content of a big file to QBuffer - qt

The following code use writeCharacters with small buffer size to writing the content of big file, but it seems it only works when writing the content to a xml file, but writeCharacters() will crash when writting the content of a big file to QBuffer, any solution? Thanks.
env: qt opensource 4.8.7; Visual Studio 2010; Windows 10;
big file: https://1drv.ms/u/s!Ap_EAuwC9QkXijbujBQcQk4Hat_O?e=KemgUY
#include <QtCore/QCoreApplication>
#include <QFile>
#include <QXmlStreamWriter>
#include <QBuffer>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray mContentBuffer;
QByteArray arr;
int pos, filesize;
QFile file("C:\\Work\\bigfile.xar"); //Szie: 300Mb
if(!file.open(QFile::ReadOnly))
{
return -1;
}
mContentBuffer = file.readAll();
file.close();
//QFile profile("C:\\Work\\profile.xml");
//if(!profile.open(QFile::WriteOnly|QFile::Truncate))
//{
// return -1;
//}
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QXmlStreamWriter stream(&buffer);
stream.setAutoFormatting(true);
stream.writeStartDocument();
stream.writeStartElement("Profile");
//stream.writeTextElement("Content", mContentBuffer.toBase64());
stream.writeStartElement("Content");
pos = 0;
filesize = mContentBuffer.size();
while(pos<filesize){
arr = mContentBuffer.mid(pos, 2000000);
stream.writeCharacters(arr.toBase64());
pos+=arr.size();
}
stream.writeEndElement();
stream.writeEndElement(); // Profile
stream.writeEndDocument();
return 0;
}
Here is the goal for writing a big file into a QBuffer.
bool profileModified()
{
bool result = true;
QFile profile("C:\\Work\\profile.xml");
if(profile.exists())
{
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QXmlStreamWriter stream(&buffer);
exportProfile(stream);
profile.open(QFile::ReadOnly);
QByteArray profileArr = profile.readAll();
buffer.seek(0);
QByteArray bufferArr = buffer.buffer();
result = (array_compare(profileArr, bufferArr) != 0);
profile.close();
}
return result;
}

Related

Transfer serially into Arduino Uno, the text file read by Qt Creator

This is from my previous question of read the text line by line.
Based from #KubaOber answer, I can successfully read and display the content line by line in a certain time interval.
Then, I was trying to transmit the content of the text file serially into Arduino Uno using the source code available on the internet.
Here's the header code:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtSerialPort/QSerialPort>
#include <QSerialPortInfo>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
QSerialPort *arduino;
static const quint16 arduino_uno_vendor_id = 10755;
static const quint16 arduino_uno_product_id = 67;
QString arduino_port_name;
bool arduino_is_available;
QByteArray serialData;
QString serialBuffer;
void updateSpeedometer(QString);
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
Ui::Widget *ui;
private slots:
private:
};
#endif // WIDGET_H
and here's the main.cpp :
#include <QtWidgets>
#include <QSerialPortInfo>
#include <QtSerialPort/QSerialPort>
#include "widget.h"
#include <QDebug>
bool arduino_is_available = false;
QString arduino_port_name = "";
QSerialPort *arduino = new QSerialPort;
QString serialBuffer = "";
static const quint16 arduino_uno_vendor_id = 10755;
static const quint16 arduino_uno_product_id = 67;
class PeriodicReader : public QObject {
Q_OBJECT
QTimer m_timer{this};
QFile m_file{this};
void readLine() {
if (m_file.atEnd()) {
m_timer.stop();
return;
}
QByteArray lineBaru(m_file.readLine());
emit newLine(lineBaru);
qDebug()<<lineBaru;
QString lineString(lineBaru);
qDebug()<<lineString << " converted to QString";
lineString.remove("\n");
qDebug()<<lineString;
lineString.remove(" ");
qDebug()<<lineString;
QRegExp rx("d(\\d+)");
QList<int> list;
int pos = 0;
while ((pos = rx.indexIn(lineString, pos)) != -1) {
list << rx.cap(1).toInt();
pos += rx.matchedLength();
}
qDebug()<<list;
int listi = list.at(0);
qDebug()<<"list[0] :"<<listi;// <<---THIS IS WHERE I CAN GET THE VALUE AFTER THE "d"
updateSpeedometer(lineString);
}
void updateSpeedometer(QString command)
{
if(arduino->isWritable())
{
command.remove(" ");
arduino->write(command.toStdString().c_str());
qDebug() << command.toStdString().c_str() << " is uploaded to Arduino";
}else{
qDebug()<<"Couldn't write to Serial !" ;
}
}
public:
explicit PeriodicReader(QObject * parent = {}) : QObject(parent) {
connect(&m_timer, &QTimer::timeout, this, &PeriodicReader::readLine);
}
void load(const QString & fileName) {
m_file.close(); // allow re-opening of the file
m_file.setFileName(fileName);
if (m_file.open(QFile::ReadOnly | QFile::Text)) {
readLine();
m_timer.start(1000); // <<<---------------HERE IS WHERE I WANT THE DELAY TO BE
}
}
Q_SIGNAL void newLine(const QByteArray &);
};
QString lineToString(QByteArray line)
{
while (line.endsWith('\n') || line.endsWith('\r'))
line.chop(1);
return QString::fromUtf8(line);
}
int main(int argc, char ** argv) {
qDebug()<<"Number of available ports :" <<QSerialPortInfo::availablePorts().length();
foreach(const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()){
qDebug()<<"Has Vendor ID:" << serialPortInfo.hasVendorIdentifier();
if(serialPortInfo.hasVendorIdentifier()){
qDebug()<<"Vendor ID:"<< serialPortInfo.vendorIdentifier();
}
qDebug()<<"Has Product ID:" << serialPortInfo.hasProductIdentifier();
if(serialPortInfo.hasProductIdentifier()){
qDebug()<<"Product ID:"<< serialPortInfo.productIdentifier();
}
}
foreach(const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()){
if(serialPortInfo.hasVendorIdentifier() && serialPortInfo.hasProductIdentifier()){
if(serialPortInfo.vendorIdentifier() == arduino_uno_vendor_id){
if(serialPortInfo.productIdentifier() == arduino_uno_product_id){
arduino_port_name = serialPortInfo.portName();
arduino_is_available = true;
}
}
}
}
if(arduino_is_available){
//open and configure the serialport
arduino->setPortName(arduino_port_name);
arduino->open(QSerialPort::ReadWrite);
arduino->setBaudRate(QSerialPort::Baud9600);
arduino->setDataBits(QSerialPort::Data8);
arduino->setParity(QSerialPort::NoParity);
arduino->setStopBits(QSerialPort::OneStop);
arduino->setFlowControl(QSerialPort::NoFlowControl);
//QObject::connect(arduino,SIGNAL(readyRead()),this,SLOT(readSerial()));
}else{
//show error message
qDebug()<<" Port Error, Couldn't find the Arduino' !";
// QMessageBox::warning(this, "Port Error, Couldn't find the Arduino !");
}
QApplication app{argc, argv};
QWidget window;
QVBoxLayout layout{&window};
QPushButton load{"Load"};
QPlainTextEdit edit;
layout.addWidget(&load);
layout.addWidget(&edit);
window.show();
PeriodicReader reader;
QObject::connect(&load, &QPushButton::clicked, [&]{
auto name = QFileDialog::getOpenFileName(&window);
if (!name.isEmpty()) {
edit.clear(); // allow re-opening of the file
reader.load(name);
}
});
QObject::connect(&reader, &PeriodicReader::newLine, &edit,
[&](const QByteArray & line){ edit.appendPlainText(lineToString(line)); });
return app.exec();
}
#include "main.moc"
It is from the coding of "reading text line by line" I've asked months a go which I include the arduino serial connection coding as well and hey, it worked!
It reads the text line by line and when each line is displayed in textEdit, it also transmitted to Arduino Uno.
So my request is, instead of the delay between line by line is preset at 2 seconds, can the delay duration be controlled by the content of the text file?
Let say.. the first line content inside the text file is 's200 d300' , and the 's200' is processed by arduino to generate a frequency, meanwhile 'd300' is the delay interval in milliseconds before it reads the next line and generate another frequency signal.
So,how to make Qt recognise the 'd300' and use it as the delay value?
QString str = "asa24fsesfd300kslfv0";
QTextDocument document(str);
QTextCursor d = document.find("d");
qDebug()<<d.position();
QString s = str.mid(d.position()-1,6); // How many digits can be after << d >>
int count = 0;
for (int i = 1; i < s.size(); ++i) {
QString d = s.at(i);
if(d.isEmpty()){
break;
}else{
bool ok;
int k = d.toInt(&ok);
if(ok){
count++;
}else{
break;
}
}
}
QString result = str.mid(d.position(),count);
bool ok;
int result_int = result.toInt(&ok);
if(ok){
qDebug()<< result_int;
}
result 300

How to implement simple audio loopback in QT

I need to loopback audio recorded from the microphone straight to the speaker using QT (qt 5.7 on Windows...) - assume I can't use window's internal mic->speaker loopback (enable "listen to this device" on the microphone panel).
Any direction how to do that?
Based on the discussion on https://forum.qt.io/topic/19960/qaudioinput-redirect-to-qaudiooutput/3
#include <iostream>
#include <QCoreApplication>
#include <QAudioInput>
#include <QAudioOutput>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
const auto decideAudioFormat = [](const QAudioDeviceInfo& devInfo)
{
QAudioFormat format;
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
if (devInfo.isFormatSupported(format))
{
return format;
}
else
{
std::cerr << "Raw audio format not supported by backend, cannot play audio.\n";
throw 0;
}
};
QAudioInput audioInput(decideAudioFormat(QAudioDeviceInfo::defaultInputDevice()));
QAudioOutput audioOutput(decideAudioFormat(QAudioDeviceInfo::defaultOutputDevice()));
audioOutput.start(audioInput.start());
return app.exec();
}
Adding a buffer in-between
I've encountered this issue before. If I'm not mistaken, the read() and write() functions share the same pos() tracker. This is what happens:
QBuffer buffer;
buffer.open(QBuffer::ReadWrite); // buffer.pos() == 0
buffer.write(someData, 5); // Writes in positions 0 -- 4, buffer.pos() == 5;
buffer.write(someData, 5); // Writes in positions 5 -- 9, buffer.pos() == 10;
buffer.read(otherBuffer.data(), 10); // Tries to read from position 10 onwards. No data found
2 solutions:
Implement your own, separate read-position and write-position trackers. Then, call seek() in readData() and writeData() to set pos() -- point to the end of the data before writing, but point to the middle of the data before reading
Use 2 separate buffers, and copy bytes from the input buffer into the output buffer
So, with separate buffers:
#include <iostream>
#include <cassert>
#include <QCoreApplication>
#include <QAudioInput>
#include <QAudioOutput>
#include <QBuffer>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QBuffer rdBuff;
QBuffer wrBuff;
wrBuff.open(QBuffer::WriteOnly);
rdBuff.open(QBuffer::ReadOnly);
QObject::connect(&wrBuff, &QIODevice::bytesWritten, [&wrBuff, &rdBuff](qint64)
{
// remove all data that was already read
rdBuff.buffer().remove(0, rdBuff.pos());
// set pointer to the beginning of the unread data
const auto res = rdBuff.seek(0);
assert(res);
// write new data
rdBuff.buffer().append(wrBuff.buffer());
// remove all data that was already written
wrBuff.buffer().clear();
wrBuff.seek(0);
});
const auto decideAudioFormat = [](const QAudioDeviceInfo& devInfo)
{
QAudioFormat format;
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
if (devInfo.isFormatSupported(format))
{
return format;
}
else
{
std::cerr << "Raw audio format not supported by backend, cannot play audio.\n";
throw 0;
}
};
QAudioInput audioInput(decideAudioFormat(QAudioDeviceInfo::defaultInputDevice()));
QAudioOutput audioOutput(decideAudioFormat(QAudioDeviceInfo::defaultOutputDevice()));
audioInput.start(&wrBuff);
audioOutput.start(&rdBuff);
return app.exec();
}

How can I use Qt to encrypt/decrypte/play a video?

I am looking for a way to encrypt an video file and then use decrypt it to ram and play it directly.
setMedia takes a QIOdevice as second argument:
#include <QMediaPlayer>
#include <QApplication>
#include <QFile>
int main(int argc, char ** argv)
{
QApplication app(argc,argv);
QString fileName = app.arguments().at(1);
QFile io(fileName);
io.open(QFile::ReadOnly);
QMediaPlayer player;
player.setMedia(QUrl("test.mp3"), &io);
player.play();
return app.exec();
}
but just in case you really mean QDataStream:
QDataStream is buffered io, QIODevice is direct io, they're not compatible, so you have to double buffer like this:
#include <QMediaPlayer>
#include <QApplication>
#include <QFile>
#include <QBuffer>
#include <QDataStream>
int main(int argc, char ** argv)
{
QApplication app(argc,argv);
QString fileName = app.arguments().at(1);
// our double buffer
QByteArray bufferSpace;
// our stream on which we can put "media" data
QDataStream stream(&bufferSpace, QIODevice::WriteOnly);
// this is just demo data
QFile io(fileName);
io.open(QFile::ReadOnly);
stream << io.readAll();
// open an IO Device on our buffer
QBuffer buffer(&bufferSpace);
buffer.open(QBuffer::ReadOnly);
// give the IO to the media player
QMediaPlayer player;
player.setMedia(QUrl("test.mp3"), &buffer);
player.play();
return app.exec();
}
edit
here's a faster version of that "crypto" code you posted without using a buffer for the entire file:
#include <QMediaPlayer>
#include <QApplication>
#include <QFile>
#include <QBuffer>
#include <QDataStream>
static const unsigned char key = 0xAB;
class MyFunnyCrypto : public QFile /*or subclass any other IO*/
{
protected:
virtual qint64 readData(char *data, qint64 maxSize)
{
qint64 r = QFile::readData(data, maxSize);
if (r > 0) {
for (qint64 i = 0; i < r; i++) {
data[i] = data[i]^key;
}
}
return r;
}
};
int main(int argc, char ** argv)
{
QApplication app(argc,argv);
QString fileName = app.arguments().at(1);
MyFunnyCrypto io;
io.setFileName(fileName);
io.open(QFile::ReadOnly);
// give the IO to the media player
QMediaPlayer player;
player.setMedia(QUrl("test.mp3"), &io);
player.play();
return app.exec();
}
Thanks to #aep .
It doesn't have anything to do with QDataStream, and it's quite simple.
Encryption:
unsigned char key = 0xAB;
QFile file("test.mp3");
file.open(QIODevice::ReadOnly);
QByteArray byteArray = file.readAll();
for(int i=1024;i<byteArray.length();i++){
byteArray[i] = byteArray[i]^key;
}
file.close();
QFile encrypted("encrypted.mp3");
encrypted.open(QIODevice::WriteOnly);
encrypted.write(byteArray);
encrypted.close();
And decryption:
unsigned char key = 0xAB;
QFile file("encrypted.mp3");
file.open(QIODevice::ReadOnly);
auto byteArray = file.readAll();
for(int i=1024;i<byteArray.length();i++){
byteArray[i] = byteArray[i]^key;
}
QBuffer buffer(&byteArray);
buffer.open(QBuffer::ReadOnly);
// give the IO to the media player
QMediaPlayer player;
player.setMedia(QUrl(), &buffer);
player.play();
In the for-loop, I want to declare i as qint64 rather than int. But the compiler shows QByteArray::​operator[](int i).

Qt Error segmentation fault when trying to show window

I have a class ClientWindow. I have created several instances of it and appended the their pointers to a a list. If i try to show any of the windows however, I get "Segmentation fault (core dumped)" I keep the list of windows in a class called controller.
Here is my controller header file:
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include "clientwindow.h"
class Controller
{
public:
Controller();
void createClients(int num);
void showWindows();
private:
QList<ClientWindow*> clWList;
int size;
};
#endif // CONTROLLER_H
this is the cpp file:
#include "controller.h"
Controller::Controller()
{
}
void Controller::createClients(int num)
{
size = num;
for(int i = 0; i < size; i++)
{
ClientWindow cw;
clWList.append(&cw);
}
}
void Controller::showWindows()
{
for(int i = 0; i < size; i++)
{
ClientWindow* cw = clWList.at(0);
cw->show();
}
}
this is my main:
#include <QtGui/QApplication>
#include "clientwindow.h"
#include "controller.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// ClientWindow w;
// w.show();
QString temp = argv[1];
bool ok;
int tempI = temp.toInt(&ok, 10);
Controller c;
c.createClients(tempI);
c.showWindows();
return a.exec();
}
This is where it goes wrong:
for(int i = 0; i < size; i++)
{
ClientWindow cw;
clWList.append(&cw);
}
A local variable cw is created on the stack in each iteration. It is deallocated at the end of each iteration. Meaning the data is gone. So you end up storing pointers pointing to junk.
Calling a member function of a junk typically results in crash. :) Do this instead:
for(int i = 0; i < size; i++)
{
ClientWindow * cw = new ClientWindow();
clWList.append(cw);
}
You'll have to go through the list and delete the objects after you are done with them.

Save QList<int> to QSettings

I want to save a QList<int> to my QSettings without looping through it.
I know that I could use writeArray() and a loop to save all items or to write the QList to a QByteArray and save this but then it is not human readable in my INI file..
Currently I am using the following to transform my QList<int> to QList<QVariant>:
QList<QVariant> variantList;
//Temp is the QList<int>
for (int i = 0; i < temp.size(); i++)
variantList.append(temp.at(i));
And to save this QList<Variant> to my Settings I use the following code:
QVariant list;
list.setValue(variantList);
//saveSession is my QSettings object
saveSession.setValue("MyList", list);
The QList is correctly saved to my INI file as I can see (comma seperated list of my ints)
But the function crashes on exit.
I already tried to use a pointer to my QSettings object instead but then it crashes on deleting the pointer ..
QSettings::setValue() needs QVariant as a second parameter. To pass QList as QVariant, you have to declare it as a Qt meta type. Here's the code snippet that demonstrates how to register a type as meta type:
#include <QCoreApplication>
#include <QDebug>
#include <QMetaType>
#include <QSettings>
#include <QVariant>
Q_DECLARE_METATYPE(QList<int>)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaTypeStreamOperators<QList<int> >("QList<int>");
QList<int> myList;
myList.append(1);
myList.append(2);
myList.append(3);
QSettings settings("Moose Soft", "Facturo-Pro");
settings.setValue("foo", QVariant::fromValue(myList));
QList<int> myList2 = settings.value("foo").value<QList<int> >();
qDebug() << myList2;
return 0;
}
You might have to register QList as a meta-type of its own for it to work. This is a good starting point to read up on meta-types in Qt: http://qt.nokia.com/doc/4.6/qmetatype.html#details .
I was also struggling with this ... and I believe I now have a decent solution.
I hope this saves someone the trouble, it caused me.
#include <QCoreApplication>
#include <QSettings>
#include <QList>
#include <QDataStream>
#include <QVariant>
#include <QVariantList>
#include <QDebug>
#include <deque>
template <class T> static QVariant toVariant(const QList<T> &list)
{
QVariantList variantList;
variantList.reserve(list.size());
for (const auto& v : list)
{
variantList.append(v);
}
return variantList;
}
template <class T> static QList<T> toList(const QVariant &qv)
{
QList <T> dataList;
foreach(QVariant v, qv.value<QVariantList>()) {
dataList << v.value<T>();
}
return dataList;
}
void Gen()
{
QList<QString> data {"hello", "world","how", "are", "you"};
QList<int> ages {10,20,30,40};
QSettings setup("stuff.ini", QSettings::IniFormat);
setup.beginWriteArray("rules");
for (int groups=0;groups<3;groups++)
{
setup.setArrayIndex(groups);
setup.setValue("rule",QString("Rule-%1").arg(groups));
setup.setValue("data", toVariant (data));
setup.setValue("ages", toVariant (ages));
}
setup.endArray();
setup.sync();
}
void Read()
{
QSettings setupR("stuff.ini", QSettings::IniFormat);
int rule_count = setupR.beginReadArray("rules");
for (int groups=0;groups<rule_count;groups++)
{
setupR.setArrayIndex(groups);
QString nameRead = setupR.value("rule").toString();
QList<QString> dataRead = toList<QString>(setupR.value("data"));
qDebug() << "Rule " << groups;
qDebug() << "Rule Read" << nameRead;
qDebug() << "Data Read2" << dataRead;
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Write
Gen();
// Read
Read();
exit(0);
//return a.exec();
}
You should end up with an INI file which looks like this ...
[rules]
1\ages=10, 20, 30, 40
1\data=hello, world, how, are, you
1\rule=Rule-0
2\ages=10, 20, 30, 40
2\data=hello, world, how, are, you
2\rule=Rule-1
3\ages=10, 20, 30, 40
3\data=hello, world, how, are, you
3\rule=Rule-2
size=3
The nice thing here is you can edit this outside of QT (Just be careful) ...

Resources