I'm new in Qt and I'm really stuck with threading. I know that this is question answered many times, but I can't figure out how to solve my problem. I have widget application with several labels and I have a class that reads data from serial port. I need to read data continuously and show them in labels. I've found many different answers about threading in Qt, but I cant get any of them to work. Can anyone point me to the right direction.
This code shows approximately what I want to achieve:
serial port class:
SerialPort *port;
int value1;
int value2;
int value3;
void Port::ReadData()
{
// First I send data to serial port as a QByteArray
QByteArray data = port.readAll();
value1 = data[0];
value2 = data[1];
value3 = data[3];
// Of course it's not really like this but I process data and assign them to
variables
}
Variables value1, value2 and value3 are public and I use label1->setText(portClass.value1) to show data. When I use this with a button click ti works but I want to close it to a loop and read data continuously.
labels have slots you can call from any thread using invokeMethod:
QMetaObject::invokeMethod (label1, "setText",
Q_ARG(QString,data[0]);
QMetaObject::invokeMethod (label2, "setText",
Q_ARG(QString,data[1]);
QMetaObject::invokeMethod (label3, "setText",
Q_ARG(QString,data[2]);
Related
I would like to know how to efficiently extract data from a QAudioBuffer object. I have a wav audio file that I am decoding with a QAudioDecoder object. I want to extract the results of the decoding contained in the QAudioBuffer object to apply filtering operations on it and finally send the filtered data to a subclass of QIODevice to listen to the result in real time.
At first, I just do a test to make sure that the extraction works well. To do this, I save the data contained in the QAudioBuffer object in a txt file. But I encounter 2 problems.
the resulting TXT file contains only characters, no numbers.
With MATLAB, when I plot the signal represented by the data contained in the TXT file, I get the shape of the original audio signal (the one in the WAV file) but the amplitudes are too big and should be between -1 and 1.
Can you please tell me how to extract the data so that I get a result on which I can apply a filter and how to have data between -1 and 1?
I use Qt6.4
thanks in advance
the code and the slot
QAudioFormat *format_decoder;
format_decoder = new QAudioFormat;
format_decoder->setSampleRate(44100);
format_decoder->setChannelCount(1);
format_decoder->setSampleFormat(QAudioFormat::Int16);
QAudioDecoder decoder;
decoder.setSource(filenameSource);
decoder.setAudioFormat(*format_decoder);
decoder.start();
QObject::connect(&decoder, &QAudioDecoder::bufferReady, this, &MainWindow::test_copy_to_txt)
the slot
void MainWindow::test_copy_to_txt(){
QAudioBuffer audioBuffer = decoder.read();
const qint16* samples = audioBuffer.constData<qint16>(); // Signal shape ok, but not the amplitudes
QFile outputFile(filenameTest1);
if(!outputFile.open(QIODevice::WriteOnly|QIODevice::Append)){
qDebug() << "ERROR";}
QTextStream out(&outputFile);
for (int i = 0; i < audioBuffer.sampleCount(); ++i) {
out << samples[i] << "\n"; // only characters, no numbers.
}
outputFile.close();
}
another question: Can you recommend a documentation other than the one on the Qt site to have more details on audio processing with Qt? How do the classes react to each other? An example so that you understand why I am looking for such documentation is the pure virtual function quint64 readData(char *data, quint64 Len) from QIODevice. For my project, I will have to reimplement it, but I would like to know what function calls it and how to determine the Len parameter.
Thank you for your answers. I followed your recommendations and everything is ok now.
Here is the corrected code
out << static_cast< float >(samples[i]) / std::numeric_limits<qint16>::max() << "\r\n";
I've made a horizontal slider, the value is being saved in a text file. After the app is closed slider resets to the initial value of "1". Using -
ui->horizontalSlider->setValue(read());
I want to read the value of the file using read() function and set it as the initial value for the slider which results in the value being restored / saved.
This is read(); function-
int read(){
std::fstream readfile;
readfile.open("bin\ram.dat");
std::string value;
readfile << value;
int value2 = atoi(value.c_str());
return value2;
}
After a lot of different variable types being used and few conversion it does not work.
Question- how to set the initial value of slider to int located in "bin\ram.dat"? I'm a beginner so any tips or clues are more than welcome. Thanks! (I've googled it before posting and found few codes which did not work here)
I use Qt 5.4 on Ubuntu Gnome 14.04 LTS to reads a string line from serial port. Everything was OK, but when I re-installed Ubuntu Gnome 14.04 and Qt 5.4, my code of serial port does not work well. When the Arduino send "0" the code of Qt reads it like this "�" and the other numbers that sends over serial the Qt reads it as a letters and symbols. I think the problem with unicode of my Qt. The unicode of my ubuntu is en.US.UTF-8 and the QT unicode is setted to "system". Please help me :(
This is my code that read the data from serial port:
QByteArray input;
if (serial->canReadLine()) //chect if data line is ready
input.clear();
input = serial->readLine(); //read data line from sreial port
ui->label->setText(input);
qDebug ()<<input<<endl;
this code of Arduino it is working fine with CuteCom and Arduino serial monitor
const int analogInPin = A0;
unsigned int sensorValue = 0; // value read from the pot
void setup() {
Serial.begin(19200);
}
void loop() {
Serial.print("\n");
Serial.print("#");
for (int i=0; i < 5; i++) {
// read the analog in value:
sensorValue = analogRead(analogInPin);
Serial.print(sensorValue);
Serial.print("#");
};
sensorValue = analogRead(analogInPin);
Serial.print(sensorValue);
Serial.print("# \n");
}
Sorry for my English
If the parity or data/stop bit parameters are different you can still write and read, but you get "funny" output similar to the one you showed us above, and this is not a problem of the unicode setting (especially not with '0', which is a character of the ASCII set).
Try setting the same port parameters explicitly on both ends just before starting the communication.
There are several problems:
You didn't post enough code to know the context of how you use it. I'm assuming that you handle the data in a method attached to the readyRead signal.
You are only reading one line, where you should be reading lines until no more are available. The readyRead signal can be emitted with any number of bytes available for reading: these may make up no complete lines, or several complete lines! If you don't keep on reading lines until no more are available, you'll be severely lagging behind incoming data.
You are using implicit QByteArray to QString conversions. These are a bad code smell. Be explicit about it.
You have fairly verbose code. You don't need to clear a QByteArray before setting its value. You also should declare it at the point of use. Better yet, use type inference that C++11 brought.
Thus:
class MyWindow : public QDialog {
QSerialPort m_port;
void onData() {
while (m_port->canReadLine())
auto input = QString::fromLatin1(m_port->readLine());
ui->label->setText(input);
qDebug() << input;
}
}
...
public:
MyWindow(QWidget * parent = 0) : QDialog(parent) {
...
connect(&m_port, &QIODevice::readyRead, this, &MyWindow::onData);
}
};
I am currently working on a project that involves serial communication between a Arduino and a laptop. I know the Arduino is indeed sending the data that I need, see this picture: http://s1.postimg.org/w5wisaetr/Help.png
Now on the other end my laptop is connected to the Arduino and running a program that I made using QT Creator. However, when reading data from the serial Port I can't get the program to display this information.
I connected my readData() function to be executed when data is received like this:
connect(m_serialPort, SIGNAL(readyRead()), m_dataGathering, SLOT(newData()));
This works and the newData() function is called whenever something in transmitted from the Arduino. However the function newData() does not display the data that I need.
newData():
void DataGathering::newData()
{
QByteArray rMsg = m_serial->readAll();
qDebug() << rMsg.constData();
}
This only sends empty message to the display. Like this: http://s2.postimg.org/dkcyip2u1/empty.png
The following code however works:
void DataGathering::newData()
{
QByteArray rMsg("\nTest...");// = m_serial->readAll();
qDebug() << rMsg.constData();
}
This code display the message like it should.
However, another difference in the output display is that when the working code is executed my console also displays a lot of Framing errors, I assumed this is because the baudrate of the unwanted characters differs from the data that I need.
That is why i started questioning the readAll() function.
It is also obvious that the Arduino is not only sending the data that I need but also some unwanted characters (see image in first link), but I don't see this as a problem since I will filter this out later.
All help is very much appreciated.
Update: I found out that the readAll() function is returning QByteArrays with size() equals to 0.
Looks like the serial port QIODevice does not implement bytesAvailable, if it returns 0. This may also be why readAll() fails, depending on how it is implemented. But at least readAll() has the problem of not being able to report error.
Try using read method instead for better diagnostics, like this (untested):
void DataGathering::newData()
{
QByteArray rMsg;
for(;;) {
char buf[256]; // read data in this size chunks
qint64 len = m_serial->read(buf, sizeof buf);
if (len <= 0) {
if (len < 0) {
qDebug() << "newData() read error" << m_serial->errorString();
}
break; // for(;;)
}
rMsg.append(buf, len);
}
qDebug() << "newData() got byte array" << rMsg.size() << ":" << rMsg;
}
It may not solve your problem, but with luck it will give you error message.
Update
void MainWindow::readXml()
{
QDomDocument Champions;
QFile xmlFile("champions.xml");
xmlFile.open(QIODevice::ReadOnly);
Champions.setContent(&xmlFile);
QDomElement root = Champions.firstChildElement();
QDomNodeList ChampionNames = root.elementsByTagName("Champion");
for(int i = 0; i < ChampionNames.count(); i++)
{
QDomNode championNode = ChampionNames.at(i);
if(championNode.isElement())
{
QDomElement Champion = championNode.toElement();
ui->comboBox->addItem(Champion.attribute("Name"));
}}}
Managed to get something like this so I've the names in the comboBox now :)
I'm new to this community so I'm happy to meet all of You!
First I want to inform You that I'm pretty new to Qt programming however I've had some basic c++ lessons in school though it was only console programming and I've never worked on stuff like that. Why I've started with Qt? It looked easy to me and hell it was compared to visual studio! So here is my problem.
Basiclly I have a comboBox where I would read my "Name="" " attributes in. There will be around 100 maybe abit less maybe a bit more don't know Yet. I don't know how to start with everything this since I have never done such thing before. What i want the software to do is basiclly when i select a "name" in the combobox, i want all the attributes ("Q" "W" "E" "R") to be printed out in the 4 labels as You can see on the little image I've added.
I don't know if I need to first read the file into some strings arrays or data structures. Do I need to search the XML file for the "Name" which is selected in ComboBox and then somehow print it out? I spent some time on this but I cant find a way to achive the thing I want. I would really appriciate some code exemples specially using the ComboBox since as said I'm new to this.
XML File looks like this in case image is blurry:
<ROOT>
<Champ Name="XXX1">
<Q>QQ1</Q>
<W>WW1</W>
<E>EE1</E>
<R>RR1</R>
</Champ>
<Champ Name="XXX2">
<Q>QQ2</Q>
<W>WW2</W>
<E>EE2</E>
<R>RR2</R>
</Champ>
</ROOT>
I'm really bad on describing things so I've made a small ilustration using a pen to let You understand me better :)
My beautiful sketch.
Thanks for Your support in advance! Hope I'm clear enought with my question. Have a great day.
First you should represent your XML data as a C++ class/struct:
class Champ {
public:
// A constructor using QDomElement as argument
Champ(QDomElement element);
QString name;
QString q, w, e, r;
};
Second you should load the XML file, parse it, and populate a vector (or map) with Champ objects.
QVector<Champ> loadChampsObjects(const QString& xmlPath)
{
QVector<Champ> champObjects;
QFile file(xmlPath);
if (!file->open(QIODevice::ReadOnly | QIODevice::Text))
return champObjects;
/* QDomDocument takes any QIODevice. as well as QString buffer*/
QDomDocument doc("mydocument");
if (!doc.setContent(file))
return champObjects;
//Get the root element
QDomElement docElem = doc.documentElement();
// get the nodes we need
QDomNodeList nodeList = docElem.elementsByTagName("champ");
// Check each node create a Champ object and add it the vector...
for(int i = 0; i < nodeList.count(); i++)
champObjects.append(Champ(nodeList.at(i).toElement()));
return champObjects;
}
Third populate the QComboBox. We will use the index in vector as userData:
QVector<Champ> champObjects = loadChampsObjects("path.to.xml");
for (unsigned i=0; i<champObjects.count(); i++)
pComboBox->addItem(champObjects[i].name, QVariant(i));
Finally in the slot connected to the currentIndexChanged signal of combo box you can easily access the properties of the selected object using the userData that indicated the index in the vector:
void champObjectChanged()
{
unsigned vectorIndex = pComboBox->itemData(pComboBox->currentIndex())->toInt();
Champ c = champObjects[vectorIndex];
// do whatever you want with it
}
Use Qt's DOM components to do all the parsing and tree building for you (see the examples here for their use).
For the QComboBox, once the QDomDocument is complete you can search for all your Champ nodes and read the Name attributes off of them. For each one, just use QComboBox::addItem(const QString& text) to add them.
Then connect the QComboBox::currentIndexChanged (const QString&) signal to a method in your XML handling class that searches the DOM tree for a Champ node with a Name attribute that matches it. Once found read off each one of it's child node values (that's the 'Q', 'W', 'E', 'R' values).