Abnormal output when reading from serial port in QT - c++

I am using QT and QCustomPlot to create a real time plotting tool, and the data of the plot is read from the Arduino UNO board. My application succeeded in plotting while the data is a total mess. Here is my code below (Some code is from QCustomPlot website):
void Dialog::realtimeDataSlot()
{
bool currentPortNameChanged = false;
QString currentPortName;
if (currentPortName != portName) {
currentPortName = portName;
currentPortNameChanged = true;
}
QString currentRequest = request;
QSerialPort serial;
if (currentPortNameChanged) {
serial.close();
serial.setPortName(currentPortName);
if (!serial.open(QIODevice::ReadOnly)) {
return;
}
}
static QTime time(QTime::currentTime());
// calculate two new data points:
double key = time.elapsed()/1000.0;
static double lastPointKey = 0;
if (key-lastPointKey > 0.002) // at most add point every 2 ms
{
// add data to lines:
if(serial.waitForReadyRead(-1)){
data = serial.readAll();
QTextStream(stdout) << "HERE:" << data.toDouble() << endl;
customPlot->graph(0)->addData(key, data.toDouble());
customPlot->graph(0)->rescaleValueAxis(); //rescale value (vertical) axis to fit the current data:
lastPointKey = key;
customPlot->xAxis->setRange(key, 8, Qt::AlignRight);
customPlot->replot();
static double lastFpsKey;
static int frameCount;
++frameCount;
if (key-lastFpsKey > 2) // average fps over 2 seconds
{
lastFpsKey = key;
frameCount = 0;
}
}
}
// calculate frames per second:
if (currentPortName != portName) {
currentPortName = portName;
currentPortNameChanged = true;
} else {
currentPortNameChanged = false;
}
}
When I Tried to print out the data I read from the serial port, I found the following:
HERE:1
HERE:15
HERE:150
HERE:149
HERE:149
HERE:149
HERE:150
HERE:150
HERE:15
HERE:150
HERE:149
HERE:49
HERE:150
HERE:150
HERE:1
HERE:150
The values around 150 are normal while the value that are 0, 1 to others are not. Also it is not print out at a stable speed. I don't know what happened to this, and thanks to whoever may help, and I would appreciate it if there is any better ways to implement this.

The problem here is that it is not guaranteed that the serial transmission is received all at once. So it is better to let the serial to be processed somewhere else, for instance:
// in the class definition
QSerialPort serialPort;
private slots:
void handleReadyRead();
private:
QByteArray serialBuffer;
volatile double lastSerialValue;
// In the initialization part (not the realtimeDataSlot function)
lastSerialValue = qQNaN();
serialPort.setPortName(currentPortName);
connect(&serialPort, &QSerialPort::readyRead, this, &Dialog::handleReadyRead, Qt::UniqueConnection);
if (!serialPort.open(QIODevice::ReadOnly)) {
return;
}
serialBuffer.clear();
// Other functions:
void Dialog::realtimeDataSlot()
{
...
if (key-lastPointKey > 0.002) // at most add point every 2 ms
{
if (!qIsNaN(lastSerialData))
{
// use lastSerialValue as the data.toDouble() you had before, then, at the end
lastSerialValue = qQNaN();
}
...
}
void Dialog::handleReadyRead()
{
serialBuffer.append(serialPort.readAll());
int serPos;
while ((serPos = serialBuffer.indexOf('\n')) >= 0)
{
bool ok;
double tempValue = QString::fromLatin1(serialBuffer.left(serPos)).toDouble(&ok);
if (ok) lastSerialValue = tempValue;
serialBuffer = serialBuffer.mid(serPos+1);
}
}
Explanation: whenever you receive something from the arduino the bytes are appended to a buffer. Then the byte array is parsed looking for a terminator, and if found the byte array is split and analysed. When the other function needs the data, it simply pulls the most recent one saved in the variable.
NOTE 1: I saw that you used a binary transmission. The problem is that you do not have any way to determine where the data begins and end in this way. For instance, if you receive 0x01 0x02 0x03 0x04 and you know that there are 3 bytes, are they 01..03 or 02..04 or 03, 04 and a missing one or...? The version I implemented requires you to send data in string format with a new-line terminator (simplest version, you just have to write Serial.println(doubleValue); in the arduino code), but if you need the binary version I can give you some hints
NOTE 2: The code I wrote is NOT thread safe. It will work only if the realtimeDataSlot and the handleReadyRead are called in the same thread. Note that if they belong to the same object and are called through signals this is guaranteed.
Now, this should work. But I highly discourage you from doing this. I don't know who needs to call the realtimeDataSlot(), but I think that the most correct version is something like this:
// in the class definition
QSerialPort serialPort;
private slots:
void handleReadyRead();
void receivedData(double val);
private:
QByteArray serialBuffer;
signals:
void newData(double data);
// In the initialization part (not the realtimeDataSlot function)
serialPort.setPortName(currentPortName);
connect(&serialPort, &QSerialPort::readyRead, this, &Dialog::handleReadyRead, Qt::UniqueConnection);
connect(this, &Dialog::newData, this, &Dialog::receivedData, Qt::UniqueConnection);
if (!serialPort.open(QIODevice::ReadOnly)) {
return;
}
serialBuffer.clear();
// Other functions:
void Dialog::receivedData(double val)
{
double key = time.elapsed()/1000.0;
static double lastPointKey = 0;
if (key-lastPointKey > 0.002) // at most add point every 2 ms
{
QTextStream(stdout) << "HERE:" << data.toDouble() << endl;
customPlot->graph(0)->addData(key, data.toDouble());
customPlot->graph(0)->rescaleValueAxis();
...
}
}
void Dialog::handleReadyRead()
{
serialBuffer.append(serialPort.readAll());
int serPos;
while ((serPos = serialBuffer.indexOf('\n')) >= 0)
{
bool ok;
double tempValue = QString::fromLatin1(serialBuffer.left(serPos)).toDouble(&ok);
if (ok) emit newData(tempValue);
serialBuffer = serialBuffer.mid(serPos+1);
}
}
So keep the graph responsive to events (received a new data) instead of to a timer.
One more thing: I removed the port change on purpose. I suggest you to handle it in another way: put a button to start and stop the serial, and when the serial port is started prevent the user from changing the port name. This way the user will explicitely need to shut it down when he needs to change the port. If you want your version, however, don't include it in your code, but make a slot on its own to call whenever you need to change the port name:
void changeSerialPortName(QString newName)
{
if (newName != serialPort.portName()) {
if (serialPort.isOpen())
serialPort.close();
serialPort.setPortName(newName);
if (!serialPort.open(QIODevice::ReadOnly)) {
return;
}
}
}

Related

Xdnd drop support faulty implementation

I have implemented a Xdnd drop support implementation in VTK some time ago. It was working great except with Thunar file manager. All other file managers were working fine at the time. We dismissed this limitation a Thunar bug at the time.
The feature I implemented was very simple:
Set the window of the application to be XdndAware
Receive the position message and respond that we are ready to receive
Receive the drop mesage and request a selection
Receive the selection notify and recover the URI
Convert the URI into something we can work with
Nothing fancy, I did not even touch the list type.
Fast forward a few years and now dolphin users cannot drop files correctly into our application. The URI is always the first file dropped since dolphin was started. Restarting our application has no effect. No bug at all with pcmanfm.
This is not a dolphin bug and files can be dropped on blender or firefox from dolphin without issues.
So there must be a bug in our implementation, but I've been staring at the code for some time and everything I tried had no effect, except for breaking Xdnd support completely.
Here are the interesting part of the implementation:
//------------------------------------------------------------------------------
vtkXRenderWindowInteractor::vtkXRenderWindowInteractor()
{
this->Internal = new vtkXRenderWindowInteractorInternals;
this->DisplayId = nullptr;
this->WindowId = 0;
this->KillAtom = 0;
this->XdndSource = 0;
this->XdndPositionAtom = 0;
this->XdndDropAtom = 0;
this->XdndActionCopyAtom = 0;
this->XdndStatusAtom = 0;
this->XdndFinishedAtom = 0;
}
[...]
//------------------------------------------------------------------------------
void vtkXRenderWindowInteractor::Enable()
{
// avoid cycles of calling Initialize() and Enable()
if (this->Enabled)
{
return;
}
// Add the event handler to the system.
// If we change the types of events processed by this handler, then
// we need to change the Disable() routine to match. In order for Disable()
// to work properly, both the callback function AND the client data
// passed to XtAddEventHandler and XtRemoveEventHandler must MATCH
// PERFECTLY
XSelectInput(this->DisplayId, this->WindowId,
KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask |
StructureNotifyMask | EnterWindowMask | LeaveWindowMask | PointerMotionHintMask |
PointerMotionMask);
// Setup for capturing the window deletion
this->KillAtom = XInternAtom(this->DisplayId, "WM_DELETE_WINDOW", False);
XSetWMProtocols(this->DisplayId, this->WindowId, &this->KillAtom, 1);
// Enable drag and drop
Atom xdndAwareAtom = XInternAtom(this->DisplayId, "XdndAware", False);
char xdndVersion = 5;
XChangeProperty(this->DisplayId, this->WindowId, xdndAwareAtom, XA_ATOM, 32, PropModeReplace,
(unsigned char*)&xdndVersion, 1);
this->XdndPositionAtom = XInternAtom(this->DisplayId, "XdndPosition", False);
this->XdndDropAtom = XInternAtom(this->DisplayId, "XdndDrop", False);
this->XdndActionCopyAtom = XInternAtom(this->DisplayId, "XdndActionCopy", False);
this->XdndStatusAtom = XInternAtom(this->DisplayId, "XdndStatus", False);
this->XdndFinishedAtom = XInternAtom(this->DisplayId, "XdndFinished", False);
this->Enabled = 1;
this->Modified();
}
[...]
//------------------------------------------------------------------------------
void vtkXRenderWindowInteractor::DispatchEvent(XEvent* event)
{
int xp, yp;
switch (event->type)
{
[...]
// Selection request for drag and drop has been delivered
case SelectionNotify:
{
// Sanity checks
if (!event->xselection.property || !this->XdndSource)
{
return;
}
// Recover the dropped file
char* data = nullptr;
Atom actualType;
int actualFormat;
unsigned long itemCount, bytesAfter;
XGetWindowProperty(this->DisplayId, event->xselection.requestor, event->xselection.property,
0, LONG_MAX, False, event->xselection.target, &actualType, &actualFormat, &itemCount,
&bytesAfter, (unsigned char**)&data);
// Conversion checks
if ((event->xselection.target != AnyPropertyType && actualType != event->xselection.target) ||
itemCount == 0)
{
return;
}
// Recover filepaths from uris and invoke DropFilesEvent
std::stringstream uris(data);
std::string uri, protocol, hostname, filePath;
std::string unused0, unused1, unused2, unused3;
vtkNew<vtkStringArray> filePaths;
while (std::getline(uris, uri, '\n'))
{
if (vtksys::SystemTools::ParseURL(
uri, protocol, unused0, unused1, hostname, unused3, filePath, true))
{
if (protocol == "file" && (hostname.empty() || hostname == "localhost"))
{
// The uris can be crlf delimited, remove ending \r if any
if (filePath.back() == '\r')
{
filePath.pop_back();
}
// The extracted filepath miss the first slash
filePath.insert(0, "/");
filePaths->InsertNextValue(filePath);
}
}
}
this->InvokeEvent(vtkCommand::DropFilesEvent, filePaths);
XFree(data);
// Inform the source the the drag and drop operation was sucessfull
XEvent reply;
memset(&reply, 0, sizeof(reply));
reply.type = ClientMessage;
reply.xclient.window = event->xclient.data.l[0];
reply.xclient.message_type = this->XdndFinishedAtom;
reply.xclient.format = 32;
reply.xclient.data.l[0] = this->WindowId;
reply.xclient.data.l[1] = itemCount;
reply.xclient.data.l[2] = this->XdndActionCopyAtom;
XSendEvent(this->DisplayId, this->XdndSource, False, NoEventMask, &reply);
XFlush(this->DisplayId);
this->XdndSource = 0;
}
break;
case ClientMessage:
{
if (event->xclient.message_type == this->XdndPositionAtom)
{
// Drag and drop event inside the window
// Recover the position
int xWindow, yWindow;
int xRoot = event->xclient.data.l[2] >> 16;
int yRoot = event->xclient.data.l[2] & 0xffff;
Window root = DefaultRootWindow(this->DisplayId);
Window child;
XTranslateCoordinates(
this->DisplayId, root, this->WindowId, xRoot, yRoot, &xWindow, &yWindow, &child);
// Convert it to VTK compatible location
double location[2];
location[0] = static_cast<double>(xWindow);
location[1] = static_cast<double>(this->Size[1] - yWindow - 1);
this->InvokeEvent(vtkCommand::UpdateDropLocationEvent, location);
// Reply that we are ready to copy the dragged data
XEvent reply;
memset(&reply, 0, sizeof(reply));
reply.type = ClientMessage;
reply.xclient.window = event->xclient.data.l[0];
reply.xclient.message_type = this->XdndStatusAtom;
reply.xclient.format = 32;
reply.xclient.data.l[0] = this->WindowId;
reply.xclient.data.l[1] = 1; // Always accept the dnd with no rectangle
reply.xclient.data.l[2] = 0; // Specify an empty rectangle
reply.xclient.data.l[3] = 0;
reply.xclient.data.l[4] = this->XdndActionCopyAtom;
XSendEvent(this->DisplayId, event->xclient.data.l[0], False, NoEventMask, &reply);
XFlush(this->DisplayId);
}
else if (event->xclient.message_type == this->XdndDropAtom)
{
// Item dropped in the window
// Store the source of the drag and drop
this->XdndSource = event->xclient.data.l[0];
// Ask for a conversion of the selection. This will trigger a SelectioNotify event later.
Atom xdndSelectionAtom = XInternAtom(this->DisplayId, "XdndSelection", False);
XConvertSelection(this->DisplayId, xdndSelectionAtom,
XInternAtom(this->DisplayId, "UTF8_STRING", False), xdndSelectionAtom, this->WindowId,
CurrentTime);
}
else if (static_cast<Atom>(event->xclient.data.l[0]) == this->KillAtom)
{
this->ExitCallback();
}
}
break;
}
}
[...]
And header:
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderingUIModule.h" // For export macro
#include <X11/Xlib.h> // Needed for X types in the public interface
class vtkCallbackCommand;
class vtkXRenderWindowInteractorInternals;
class VTKRENDERINGUI_EXPORT vtkXRenderWindowInteractor : public vtkRenderWindowInteractor
{
public:
static vtkXRenderWindowInteractor* New();
vtkTypeMacro(vtkXRenderWindowInteractor, vtkRenderWindowInteractor);
void PrintSelf(ostream& os, vtkIndent indent) override;
/**
* Initializes the event handlers without an XtAppContext. This is
* good for when you don't have a user interface, but you still
* want to have mouse interaction.
*/
void Initialize() override;
/**
* Break the event loop on 'q','e' keypress. Want more ???
*/
void TerminateApp() override;
/**
* Run the event loop and return. This is provided so that you can
* implement your own event loop but yet use the vtk event handling as
* well.
*/
void ProcessEvents() override;
///#{
/**
* Enable/Disable interactions. By default interactors are enabled when
* initialized. Initialize() must be called prior to enabling/disabling
* interaction. These methods are used when a window/widget is being
* shared by multiple renderers and interactors. This allows a "modal"
* display where one interactor is active when its data is to be displayed
* and all other interactors associated with the widget are disabled
* when their data is not displayed.
*/
void Enable() override;
void Disable() override;
///#}
/**
* Update the Size data member and set the associated RenderWindow's
* size.
*/
void UpdateSize(int, int) override;
/**
* Re-defines virtual function to get mouse position by querying X-server.
*/
void GetMousePosition(int* x, int* y) override;
void DispatchEvent(XEvent*);
protected:
vtkXRenderWindowInteractor();
~vtkXRenderWindowInteractor() override;
/**
* Update the Size data member and set the associated RenderWindow's
* size but do not resize the XWindow.
*/
void UpdateSizeNoXResize(int, int);
// Using static here to avoid destroying context when many apps are open:
static int NumAppInitialized;
Display* DisplayId;
Window WindowId;
Atom KillAtom;
int PositionBeforeStereo[2];
vtkXRenderWindowInteractorInternals* Internal;
// Drag and drop related
Window XdndSource;
Atom XdndPositionAtom;
Atom XdndDropAtom;
Atom XdndActionCopyAtom;
Atom XdndStatusAtom;
Atom XdndFinishedAtom;
///#{
/**
* X-specific internal timer methods. See the superclass for detailed
* documentation.
*/
int InternalCreateTimer(int timerId, int timerType, unsigned long duration) override;
int InternalDestroyTimer(int platformTimerId) override;
///#}
void FireTimers();
/**
* This will start up the X event loop and never return. If you
* call this method it will loop processing X events until the
* application is exited.
*/
void StartEventLoop() override;
private:
vtkXRenderWindowInteractor(const vtkXRenderWindowInteractor&) = delete;
void operator=(const vtkXRenderWindowInteractor&) = delete;
};
#endif
The complete file can be seen here:
https://gitlab.kitware.com/vtk/vtk/-/blob/master/Rendering/UI/vtkXRenderWindowInteractor.cxx
You can follow my train of thoughts and debugs here:
https://gitlab.kitware.com/f3d/f3d/-/issues/228
To test this code, a simple way is to use F3D has it is using the dropped file, but a simple VTK application should work as well:
https://gitlab.kitware.com/f3d/f3d
Current Dolphin issue
From some testing, the issue is with the preparation and sending of the XdndFinished ClientMessage back to the drag and drop source when handling the SelectionNotify event.
Instead of:
reply.xclient.window = event->xclient.data.l[0];
the line should be:
reply.xclient.window = this->XdndSource;
This aligns the XClientMessageEvent window member with the target window argument to XSendEvent. This is probably a simple copy-paste error, as xclient isn't valid for the SelectionNotify event type. Most likely the actual value of window was not previously being checked, but that has been changed recently, hence the error.
The spec covers this quite well, and also raises a couple of other things to consider:
For data.l[1]: "Bit 0 is set if the current target accepted the drop and successfully performed the accepted drop action. (new in version 5)", so using itemCount as a value will technically be incorrect whenever the count is even
If the handling of XdndPosition doesn't need to actually track where the current position is (i.e. if you are just using the entire window as a drop target), you may be able to get away with sending the XdndStatus in response to the XdndEnter message
Previous Thunar issue
Looking into this further, I did some troubleshooting regarding the previous issue with Thunar, and it boils down to the code handling XdndDrop assuming that the format of the incoming data can be converted to UTF8_STRING. This diff for GLFW handles almost the exact same issue.
If, when handling the XdndEnter message, you inspect the values of xclient.data.l[2] through xclient.data.l[4] you can see that Dolphin reports supporting the following formats:
text/uri-list
text/x-moz-url
text/plain
whereas Thunar only supports the following:
text/uri-list
The simplest solution is to:
Keep track of a supported format when handling XdndEnter
Provide this format to XConvertSelection when handling XdndDrop (instead of UTF8_STRING)
Handle the format appropriately when handling the SelectionNotify event
To be more complete, if bit 0 of xclient.data.l[1] is set on the XdndEnter message, you should get the XdndTypeList property of the drag and drop source window and based the format selection on that, instead of the formats contained in the message itself.

checking for double threshold

I've a basic question to implement an algorithm to detect a threshold for a realtime signal.
For ex. If I've a signal as shown below, I need to check if the signal crossing two thresholds in a fixed period of time, say 3 seconds.
My question here is, What will be set of IF statements to check for two thresholds in a fixed time period?
What I've already got:
I can check for a single threshold, as below:
if(signal > onThreshold)
{
ui->rdo_btn_vertical->setStyleSheet(StyleSheetOn1); // change appearance of a radio button
QSound::play(":/res/beep1.wav"); // make a beep sound as well
}
else
{
ui->rdo_btn_vertical->setStyleSheet(StyleSheetOff1);
}
First, we create a helper that detects upwards crossings:
class DetectUpwardsCrossing : public QObject {
public:
DetectUpwardsCrossing(double threshold, QObject *parent = nullptr) : QObject(parent), threshold(threshold), current(qSNaN()) {}
signals:
void crossing();
public slots:
void onSignal(double value) {
if (qIsNaN(current)) {
current = value;
return;
}
if (current == value) {
return;
}
bool before = current <= threshold;
current = value;
bool after = current >= threshold;
if (before && after) {
emit crossing();
}
}
private:
double threshold, current;
};
Now create an instance of DetectUpwardsCrossing, hook up your incoming signal value to onSignal and hook its crossing signal to the following method:
void onCrossing() {
QSound::play(":/res/beep1.wav"); // make a beep sound
if (crossingTimer->isActive()) { // The timer is still running, so we are within the 3s period
ui->rdo_btn_vertical->setStyleSheet(StyleSheetOn1); // change appearance of a radio button
}
crossingTimer->restart();
}
void crossingTimerExpire() {
ui->rdo_btn_vertical->setStyleSheet(StyleSheetOff1);
crossingTimer->stop();
}
The final piece is a QTimer *crossingTimer with an interval of 3 seconds, whose timeout signal is hooked up to crossingTimerExpire.
By using a timer like this, you can ensure that the display turns "off" if you suddenly no longer receive signals.

In GTKMM, on_draw method stops being called after invalidate occurs in separated thread

Using GTKMM, I'm extending the DrawingArea widget with the idea that an external process provides it with images. My CameraDrawingArea will then display the images at the right size using Cairo.
Each time an image arrives, I store it and I call the invalidate method, which eventually ends up in a call to on_draw, where I can resize and display the image.
My problem is the following:
The first 10 or 20 images are displayed as I expected.
After a while, the images keep coming from the provider process, I keep calling invalidate
but on_draw is not called any more.
To show it here, I've simplified the code so that there is nothing external to the class, and no link with other libraries. I've replaced the process providing the images by a method with for-loops, and the display of the image by printing a simple text in the middle of the widget area:
In the constructor I launch a new std::thread to call the doCapture method in the same instance. I also set up a font description, to use it later.
The doCapture method is a silly CPU eater, that does nothing except calling from time to time the refreshDrawing method, as long as keepCapturing is not false.
refreshDrawing invalidates the whole window's rectangle via a call to invalidate.
Gtk's magic is suppose to call on_draw and provide a Cairo context to draw whatever. In my case, for tests purposes, I draw a brownish centered integer.
The class destructor stops the thread by set keepCapturing to false, and waits for termination with a join.
#include "camera-drawing-area.hpp"
#include <iostream>
CameraDrawingArea::CameraDrawingArea():
captureThread(nullptr) {
fontDescription.set_family("Monospace");
fontDescription.set_weight(Pango::WEIGHT_BOLD);
fontDescription.set_size(30 * Pango::SCALE);
keepCapturing = true;
captureThread = new std::thread([this] {
doCapture();
});
}
void CameraDrawingArea::doCapture() {
while (keepCapturing) {
float f = 0.0;
for (int n = 0; n < 1000; n++) {
for (int m = 0; m < 1000; m++) {
for (int o = 0; o < 500; o++) {
f += 1.2;
}
}
}
std::cout << "doCapture - " << f << std::endl;
refreshDrawing();
}
}
void CameraDrawingArea::refreshDrawing() {
auto win = get_window();
if (win) {
win->invalidate(false);
std::cout << "refreshDrawing" << std::endl;
}
}
bool CameraDrawingArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
std::cout << "on_draw" << std::endl;
static char buffer[50];
static int n = 0;
sprintf(buffer, "-%d-", n++);
Gtk::Allocation allocation = get_allocation();
const int width = allocation.get_width();
const int height = allocation.get_height();
auto layout = create_pango_layout(buffer);
layout->set_font_description(fontDescription);
int textWidth, textHeight;
layout->get_pixel_size(textWidth, textHeight);
cr->set_source_rgb(0.5, 0.2, 0.1);
cr->move_to((width - textWidth) / 2, (height - textHeight) / 2);
layout->show_in_cairo_context(cr);
cr->stroke();
return true;
}
CameraDrawingArea::~CameraDrawingArea() {
keepCapturing = false;
captureThread->join();
free(captureThread);
}
And this is my header file:
#ifndef CAMERA_DRAWING_AREA_HPP
#define CAMERA_DRAWING_AREA_HPP
#include <gtkmm.h>
#include <thread>
class CameraDrawingArea : public Gtk::DrawingArea {
public:
CameraDrawingArea();
virtual ~CameraDrawingArea();
protected:
bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override;
private:
bool keepCapturing;
void doCapture();
void refreshDrawing();
std::thread* captureThread;
Pango::FontDescription fontDescription;
};
#endif
The problem manifests itself as follows:
When starting the application, it faithfully displays 1, 2, 3...
Between 5th and 20th iteration (it's random, but rarely outside these ranges), it stops refreshing.
Because of the cout, I can see that refreshDrawing is called be sure that invalidate is also called, but on_draw isn't.
Also, if I stop the application before it stops refreshing, then it ends up nicely. But, if I stop the application after it stops refreshing, then I see this message below (the ID value varies):
GLib-CRITICAL **: 10:05:04.716: Source ID 25 was not found when attempting to remove it
I'm quite sure that I do something wrong, but clueless about what. Any help would be appreciated.
I also checked the following questions, but they don't seem to be related with my case:
Draw signal doesn't get fired in GTKMM, when derived class doesn't call a superclass's constructor
You can't use GTK methods from any other thread than the one in which you started the GTK main loop. Probably the win->invalidate() call is causing things to go wrong here.
Instead, use Glib::Dispatcher to communicate with the main thread, or use gdk_threads_add_idle() for a more C-style solution.
Based on the answer form #ptomato, I've rewritten my example code. The golden rule is do not call GUI functions from another thread, but if you do, then acquire some specific GDK locks first. That's the purpose of Glib::Dispatcher :
If a Glib::Dispatcher object is constructed in the main GUI thread (which will therefore be the receiver thread), any worker thread can emit on it and have the connected slots safely execute gtkmm functions.
Based on that, I've added a new private member Glib::Dispatcher refreshDrawingDispatcher that will allow threads to safely the invalidate the windows area:
#ifndef CAMERA_DRAWING_AREA_HPP
#define CAMERA_DRAWING_AREA_HPP
#include <gtkmm.h>
#include <thread>
class CameraDrawingArea :
public Gtk::DrawingArea {
public:
CameraDrawingArea();
virtual ~CameraDrawingArea();
protected:
bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override;
private:
bool keepCapturing;
void doCapture();
void refreshDrawing();
Glib::Dispatcher refreshDrawingDispatcher;
std::thread* captureThread;
Pango::FontDescription fontDescription;
};
#endif
Then, I've connected the dispatcher to the refreshDrawing method. I do this in the class constructor, which is called during GUI start up and therefore in the main GUI thread:
CameraDrawingArea::CameraDrawingArea():
refreshDrawingDispatcher(),
captureThread(nullptr) {
fontDescription.set_family("Monospace");
fontDescription.set_weight(Pango::WEIGHT_BOLD);
fontDescription.set_size(30 * Pango::SCALE);
keepCapturing = true;
captureThread = new std::thread([this] {
doCapture();
});
refreshDrawingDispatcher.connect(sigc::mem_fun(*this, &CameraDrawingArea::refreshDrawing));
}
Finally, the thread has to call the dispatcher:
void CameraDrawingArea::doCapture() {
while (keepCapturing) {
float f = 0.0;
for (int n = 0; n < 1000; n++) {
for (int m = 0; m < 1000; m++) {
for (int o = 0; o < 500; o++) {
f += 1.2;
}
}
}
std::cout << "doCapture - " << f << std::endl;
refreshDrawingDispatcher.emit();
}
}
And now, this works without further problems.

tbb concurrent_bounded_queue multiple threads access

I have the following code:
tbb::concurrent_bounded_queue<Image> camera_queue_;
camera_queue_.set_capacity(1);
struct Image
{
int hour_;
int minute_;
int second_;
int msec_;
QImage image_;
Image(){hour_ = -1; minute_ = -1; second_ = -1; msec_ = -1; image_ = QImage();}
Image& operator=(Image const& copy)
{
this->hour_ = copy.hour_;
this->minute_ = copy.minute_;
this->second_ = copy.second_;
this->msec_ = copy.msec_;
this->image_ = copy.image_;
return *this;
}
};
In a Qt Thread :
ThreadA:
tbb::concurrent_bounded_queue<Image> image_queue_;
image_queue_.set_capacity(1);
Image cur_image_;
void Worker::process() {
while(1)
{
if(quit_)
break;
{
camera_queue_.pop(cur_image_);
image_queue_.push(cur_image_);
}
emit imageReady();
}
emit finished();
}
Image Worker::getCurrentImage()
{
Image tmp_image;
image_queue_.pop(tmp_image);
return tmp_image;
}
In Another Thread:
ThreadB:
Producer::Producer(){
work_ = new Worker();
work_->moveToThread(workerThread_);
QObject::connect(workerThread_, &QThread::finished, work_, &QObject::deleteLater);
QObject::connect(this, &Producer::operate, work_, &Worker::process);
QObject::connect(work_, &Worker::imageReady, this, &Producer::displayImage);
QObject::connect(this, &Producer::stopDecode, work_, &Worker::stop);
workerThread_->start();
emit operate();
}
void Producer::process() {
while(1)
{
if(quit_)
break;
{
camera_queue_.push(GetImage());
}
}
}
void Producer::displayImage()
{
Image tmp = std::move(work_->getCurrentImage());
widget_->showImage(tmp.image_);
}
However, In main thread, I have a function that enables user to click a button to get current image:
bool Producer::SaveImage()
{
Image img = std::move(work_->getCurrentImage());
std::string fileName = std::to_string(img.hour_) + "-" + std::to_string(img.minute_) + "-" + std::to_string(img.second_) + "-" + std::to_string(img.msec_/1000) + ".jpg";
std::string outFileName = folder + "/" + fileName;
return img.image_.save(QString::fromStdString(outFileName));
}
The problem is:
When user does not click the button to invoke Producer::SaveImage(), the Image Decoding and Showing runs smoothly. But when user invoke Producer::SaveImage(), the whole program will get stuck (Caton phenomenon ?). The GUI response becomes not that smooth. The more user invokes SaveImage, the slower the GUI response becomes.
Can anyone help to explain why ? Is there a way to solve that ?
Why do you want to use concurrent queue? It looks like there is a syncronization mechanism in place and you rely mostly on it instead of using concurrent_queue for synchronisation and communication as it is supposed for.
The issue is that when you set capacity = 1, both operations of concurrent_bounded_queue will block until there is enough space of items in the queue. E.g. if the queue contains an item already, the push operation will block. And since you control your threads with another notification mechanism, you might catch a deadlock.
In particular, try to swap the operations like below:
camera_queue_.pop(cur_image_);
emit imageReady();
image_queue_.push(cur_image_);
This should prepare the thread which receives images (if I understand this correctly) and it will block on its image_queue_.pop() method, then this thread will put new image and unblocks the recipient. There might be other issues similar to this, so, please rethink all of your synchronization.

how to display large data in wxListCtrl with using concept of wxThread

I'm capable to fill the database table in wxListCtrl,
my problem is to handle high range of data, I want to do this with the help of thread concept , perhaps it will save to hang the frame because of high amount of data.
I'm new in thread concept so your single lines will be a book for me.
Update:
My question was- how to display large data in wxListCtrl with using concept of wxThread
so for this I used thread concept I add two more files thread.c and thread.cpp
my entry thread code is shown below
void *MyThread :: Entry()
{
int i=1,j,k=0;
while(i!=400)
{
long index=this->temp->data_list_control->InsertItem(i,wxT("amit"));
for(j=1; j<3; j++) {
this->temp->data_list_control->SetItem(index,j,wxT("pathak"));
}
k++;
if(k==30) {
this->Sleep(1000);
k=0;
}
i++;
}
}
It is sometimes working fine but when I try to increase the value of i, it shows an error like
-*showingdatainwxlistctrl: ../../src/XlibInt.c:595: _XPrivSyncFunction: Assertion `dpy->synchandler == _XPrivSyncFunction' failed.*
or sometime it gives error like
***[Xcb] xcb_io.c:378: _XAllocID: Assertion `ret != inval_id' failed***
Why it is happening to me?
You can define your own thread object in wxWidgets in the following way:
class MyThread : public wxThread
{
private:
wxListCtrl* m_pListCtrl;
public:
MyThread(wxListCtrl* pListCtrl, wxThreadKind kind = wxTHREAD_DETACHED) :
wxThread(kind), m_pListCtrl(pListCtrl) {
}
virtual ~MyThread() {
}
virtual void* Entry() {
// here you have to place your code that will be running in separate thread
// m_pListCtrl-> ...
}
};
And this is the way how you can start your thread (assume you have your pListCtrl pointer here):
MyThread * pMyThread = new MyThread (pListCtrl);
wxThreadError ThreadError = pMyThread->Create();
if (wxTHREAD_NO_ERROR!=ThreadError) {
wxLogError(L"Can not create thread, wxThreadError '%d'", (int)ThreadError);
delete pMyThread;
return false;
}
ThreadError = pMyThread->Run();
if (wxTHREAD_NO_ERROR!=ThreadError) {
wxLogError(L"Can not run thread, wxThreadError '%d'", (int)ThreadError);
delete pMyThread;
return false;
}
// here, everything is ok.
Anyway, this is not the best solution for your problem. As far as I've understood, you need to display large amount of data in your wxListCtrl. To do this, you can use virtual ctrl (created with flag wxLC_VIRTUAL) and provide data source:
class MyListCtrl : public wxListCtrl
{
public:
MyListCtrl( ...) { ... }
virtual ~MyListCtrl();
protected:
virtual int OnGetItemImage(long item) const {
// You need this only if you want to provide specific image for your item.
// If you do not need it, just do not overload this method.
}
virtual wxString OnGetItemText(long item, long column) const {
// This is where you have to provide data for [item, column].
// Suppose, you have matrix A[n,m] which represents actually the data
// you want to display. The elements of this matrix can be of any type
// (strings, doubles, integers etc).
// You should return here wxString object that contains
// representation of the matrix's element A[item, column].
return ToWxString(A[item, column]);
// where ToWxString is your method that converts data to string
// So, you do not need to load all the data from A to wxListCtrl.
// Instead of it, wxListCtrl will determine which rows of the matrix should be
// displayed based on sizes and scroll position of wxListCtrl, and will
// call this method to obtain corresponding strings.
}
};
To create, you may use:
m_pListCtrl = new MyListCtrl( ..., ..., wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_VIRTUAL | wxSUNKEN_BORDER | wxLC_VRULES | wxLC_HRULES);
Best regards!
When you are performing high range of data you are bound to use WXThread in your program
Firstly was trying to fill wxListCtrl from wxEntry point, it was wrong u can not hit any main thread control from entry point, it does not give error, but it is a wrong concept
Here u need to pass the data to handler, handler will use it to fill wxListCtrl
code look like this->
void *MyThread :: Entry()
{
int a;
Handler handler_obj;
char *database_name=DATABASE_NAME;
connection =handler_obj.handler(101,database_name);
if(connection==NULL)
{
wxMessageBox(wxT("CAN NOT CONNECT TO DATABASE"), wxT("Message"), wxOK | wxICON_INFORMATION, NULL, -1, -1);
}
else
{
List_Ctrl_Data list_ctrl_data_object;
table_data=list_ctrl_data_object.fetch_table(connection);
MYSQL_ROW row;
while((row=mysql_fetch_row(table_data))!=NULL)
{
wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, 100000 );
void *row_data;
row_data=(void *)row;
event.SetClientData(row_data);
temp->GetEventHandler()->AddPendingEvent( event );
this->Sleep(1000);
}
}
}
to handle the row data we will use
void Id_Search_Report::onNumberUpdate(wxCommandEvent& evt)
{
int j;
void* hold_row;
hold_row=(void *)evt.GetClientData();
MYSQL_ROW row;
row=(MYSQL_ROW)hold_row;
const char* chars1 = row[0];
wxString mystring1(chars1, wxConvUTF8);
long index=data_list_control->InsertItem(this->counter,mystring1);
this->counter++;
for(j=1;j<12;j++)
{
const char* chars2=row[j];
wxString mystring2(chars2,wxConvUTF8);
data_list_control->SetItem(index,j,mystring2);
}
}
thread is returning a row , this method will handle the row and fill ListCtrl , it is a proper way to fill wxListCtrl.