QChart realtime performance - c++

I am using QChart for an application. The application need to show some data realtime. There will be one chart and 24 series in the chart. The data rate is 400pts for every channel.
I used another thread for receiving and processing received data and emit the processed data to a slot for append datas to update the chart series.
I refered to https://doc.qt.io/qt-5/qtcharts-audio-example.html. In my case, each series is limited to 2000 points, if the number of points in the series is less than 2000, add the new point to the series, if the number of points in the seried is more than 2000, delete first point, move the rest of datas to left and add the new point in the end. This will make the chart look like move from right to left.
For good performance, I also used series->replace() and series->setUseOpenGL(true).
My problem is that the application will get freezed soon when it started. I tried delete the codes of update the chart, everything looked fine. Could anyone help me how to improve the performance of update the chart?
Thanks!

I have the same problem. The main problem I think is, that QLineSeries sends the signal pointAdded() and triggers a repaint. Additionally append() and remove are performance sinks. QtChart does only support QList and no form of a ring buffer as far as I know.
I tried the way putting new data into a QQueue<QPointsF> and copy the data within a timer hanler set to 20 Hz. To avoid updating I disable these:
void
MyGraph::handle_timer_timeout()
{
_chartView->setUpdatesEnabled(false);
// _chart->removeSeries(_series);
while(_buf->count()>0){
_series->append(_buf->dequeue());
_series->remove(0);
}
// _chart->addSeries(_series);
_axisX->setRange( _series->at(0).x(),
_series->at(_seriesSize-1).x());
_axisY->setRange(-1,1);
_chartView->setUpdatesEnabled(true);
}
This results in roughly 20-30% less processor usage.
I also found the hint that temporary removing the series (removeSeries(),addseries()) can result in some improvements but I can't confirm that.
This may be better but not really good enough. I hope someone finds a better solution.
or use QLineSeries::replace(). For that I use a double buffer QVector<QVector<QPointF>> *_lists:
void
MyGraph::handle_timer_timeout()
{
_chartView->setUpdatesEnabled(false);
auto listsother = (_listsCrurrent+1)%2;
auto bufcnt = _buf->count();
//
QVector<QPointF> *newData = &_lists->data()[listsother];
int idx;
for(idx=0; idx<_seriesSize-bufcnt;idx++){
newData->replace(
idx,
_lists->at(_listsCrurrent).at(idx+bufcnt));
}
for(; idx<_seriesSize;idx++){
newData->replace(
idx,
_buf->dequeue());
}
_listsCrurrent = listsother;
_series->replace(_lists->at(_listsCrurrent));
_axisX->setRange( _series->at(0).x(),
_series->at(_seriesSize-1).x());
_axisY->setRange(-1,1);
_chartView->setUpdatesEnabled(true);
}
This is more performant on my computer.
Alternatively you can take a look on QWT.

Related

Qt QTreeWidget looping over top level items slows down after sorting

I've got a Qt related question (for c++) regarding the QTreeWidget and hope someone can help.
I am working with the QTreeWidget and have run into a performance issue that I can't seem to solve. I basically have made a simple extension of it that allows me to easily filter the tree (hide items depending on filters). Running through all top level items and hiding/showing them depending on the filter is very easy, however it slows down whenever the tree is allowed to be sorted (e.g. by calling setSortingEnabled(true) on the QTreeWidget). Does anyone know a way around this or a better way to do this?
The way I filter the the items is as follows:
setUpdatesEnabled(false);
for(int i = 0; i < topLevelItemCount(); ++i)
{
// Retrieve item
const auto pItem = topLevelItem( i );
bool hide = false;
for(filter : mFilters){
// Stop if a filter already hides the item
if(hide) break;
// Check filter
hide = filter.checkFilter(pItem);
}
pItem->setHidden(false);
}
setUpdatesEnabled(true);
This function itself is very fast, it can run in one or two seconds for a test with 1 million items. However once setSortingEnabled(true) is called to also allow sorting it slows down significantly. I've checked with debugging and it's not the sorting that slows things down, the function above will now take ages to complete. It will now take a few minute at least compared to the mere seconds it took previously (when sorting is not allowed).
To me it appears that if sorting is enabled even once then the call
const auto pItem = topLevelItem( i );
or
pItem->setHidden(hide)
slows down significantly.
Can anyone shed some light on this issue or suggest a faster/better way to apply filters and/or loop through all top level items in the QTreeWidget?
I've looked online for faster ways to loop through the top level items, but most suggest topLevelItem(int i). I've not seen any thread regarding slowdown after sorting is enabled.
I've tried a few things, namely
Using QTreeWidgetIterators rather than topLevelItem(int i)
Calling setUpdatesEnabled(false) and setUpdatesEnabled(true) before and after the filter check respectively
Calling setSortingEnabled(false) and setSortingEnabled(true) before and after the filter check respectively
Retrieving all items in a QList findItems when searching for a wildcard string and looping over the returned QList
Looping over the QList<QTreeWidgetItem*> with pointers to items used when the tree was populated via addTopLevelItems(list)
None of these have had any impact on the performance unfortunately. If sorting has ever been enabled, then looping over items slows down significantly.

How do I get the new index of a moved column in a wxGrid?

I'm using wxWidgets 3.1.0 and I'm developing a Windows app in C++.
I'm using the base wxGrid and I've enabled column re-ordering via dragging them with the mouse (EnableDragColMove(true)). My problem now is that I need to get the moved column's new position/index after the column has been dragged to its new position.
Unfortunately, I could not find a way to do that from the available APIs.
I've tried catching the wxGridEvent wxEVT_GRID_COL_MOVE then using GetCol() and GetColPos() to check the column's new index:
gridDataList->Bind(wxEVT_GRID_COL_MOVE, &FormData::OnList_ColumnMove, this);
...
void FormData::OnList_ColumnMove(wxGridEvent& event)
{
int movedCol = event.GetCol();
int movedColPos = gridDataList->GetColPos(movedCol );
...
}
But it seems the event is triggered BEFORE the column is actually moved, so GetColPos() will still return the current column index, NOT the new index.
There seems to be no event to catch AFTER the column is moved.
My current solutions/workarounds are to:
Manually handle the column movement after catching the wxEVT_GRID_COL_MOVE event (as suggested in the wxWidgets docs) so that I can properly track the before and after index of the moved column.
Manually trigger a callback or a timer event after the column moves to its new position, similar to a wxPython workaround suggested in another SO post.
Though, I would like to know if there is a cleaner, less complicated way without resorting to the workarounds above.
Any suggestions are appreciated.
Yes, this wxEVT_GRID_COL_MOVE is generated before moving the column because it can be vetoed, preventing the move from happening. And it's true that it would be convenient if it carried the new column position, but unfortunately currently it doesn't (it would be simple to fix this and any patches doing this would be welcome!).
The standard workaround of using CallAfter() to execute your code at later time should work just fine without changing wxWidgets however. I.e., assuming you use C++11, you should be able to just write
void FormData::OnList_ColumnMove(wxGridEvent& event)
{
const int movedCol = event.GetCol();
CallAfter([movedCol]() {
int movedColPos = gridDataList->GetColPos(movedCol);
...
});
}

QTableView slow scrolling when many cells are visible at once

Background: I'm developing application using Qt 5.5.1, compiling with msvc2013. In this app I use my own implementation of QTableView, along with custom QStyledItemDelegate (needed custom cell editing) and QAbstractTableModel. I intend this view to work with massive amount of data that I wrap inside mentioned model. I allow the user few data editing options, custom sorting, 'invalid' rows windup etc.
The problem: scrolling speed of my QTableView subclass is slow - it gets slower the more table is shown (by resizing window), e.g. ~250 cells shown (in fullscreen) = slow, ~70 cells shown (small window) = fast.
Whad did I try so far:
First was to check if my model is slowing things down - I have measured times (using QTime::elapsed()) reading 10k samples and it shown 0 or 1ms. Then I have simply altered QTableView::data method to always return predefined string and not acquire any real data.
QVariant DataSet_TableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::ItemDataRole::DisplayRole) {
return QVariant("aRatherLongString"); //results in slow scrolling
//return QVariant("a"); // this instead results in fast scrolling
}
else return QVariant();
}
As you can see, the speed seems to be affected by number of characters vieved per cell, and not by underlying connections to data source.
In my custom implementation of QStyledItemDelegate I have tried same 'trick' as above - this time overriging displayText method:
QString DataSet_TableModel_StyledItemDelegate::displayText(const QVariant &value, const QLocale &locale) const
{
return "a" //fast
// return "aRatherLongString"; //slow
// return QStyledItemDelegate::displayText(value, locale); //default
}
After some thought with a friend we concluded that maybe we could disable drawing/painting/updating of cells until whole scroll action is done. It might cause some flickering, but it's worth a try. Unfortunately we dont really know how to aproach this. We have everriden QTableView methods: scrollContentsBy(int dx, int dy) and verticalScrollbarAction(int action) - we have captured scroll action properly (either method intercepts it) and tried to somehow disable repainting like this:
void DataSet_TableView::verticalScrollbarAction(int action) {
this->setUpdatesEnabled(false);
QTableView::verticalScrollbarAction(action);
this->setUpdatesEnabled(true);
}
...but it did not have any visible effect.
How should we approach it? Do we need to use setUpdatesEnabled() on items that are put inside cells directly? (not sure what those are - widgets?)
Here are screenshots taken as part of testing this problem:
Predefined text, no calls to underlying data structure - slow scrolling, 'full screen'
Predefined text, no calls to underlying data structure - fast scrolling, windowed
Request: Could you kindly help me pinpoint the cause of this and suggest solution if possible? Is it limitation of the classes that I use?
First of all, you should also run your application in release mode to check your perfomance, in my experience, the performance decreases greatly when using debug mode.
Secondly, you need to be aware that the model data method and delegates methods are called every time you resize, scroll, focus out, right click etc. These actions trigger those methods to be called for each displayed cell, therefore you would need to make sure that you don't do any unnecessary processing.
The items inside cells are delegates that call their own methods (eg: paint).
Some C++ specific optimisations would be helpful in the implementation of these methods, like using a switch instead of an if statement, see explanation here and here. The usage of Conditional (Ternary) Operators might also speed up the things, more information here, here and some information about expensive checkings here.
Also, QVariant handles text in different ways, as exemplified below, you should try both ways and check if there is any difference in speed. Some conversions are more expensive than others.
v = QVariant("hello"); // The variant now contains a QByteArray
v = QVariant(tr("hello")); // The variant now contains a QString

How to show "waiting" while files are loaded in a Qt application?

I'm selecting and loading some big Dicom files on my program. The whole loading process takes a long time(depends on the number of files, but the whole process can take more than minutes if the files are many). I want show a "waiting symbol" or something like that when the file uploading is going on. I searched for it, but I didn't get anything definite.
My code for the selection and uploading part is as below:
void MainWindow::showTheSelectedList()
{
QFileDialog * fileDialog = new QFileDialog(this);
fileDialog->setFileMode(QFileDialog::ExistingFiles);
QListView* list = fileDialog->findChild<QListView*>("listView");
if(list)
{
list->setSelectionMode(QAbstractItemView::MultiSelection);
}
QTreeView* tree = fileDialog->findChild<QTreeView*>();
if(tree)
{
tree->setSelectionMode(QAbstractItemView::MultiSelection);
}
if(fileDialog->exec())
{
if(fileDialog->selectedFiles().size()>0)
{
int listsize=stringList.size();
for(int i=0;i<listsize;i++)
{
// get the name of the file
// check if the file is dicom
// upload if the file is dicom
// after uploading, get the pixel data of that file
// use the pixel data and make a icon out of it
//then insert the icon in an a QTablewView
}
}
}
//show the QtableView
}
Could you please instruct me where and how I can show the waiting sign or symbol while the uploading part is running?
Thanks
I think you are looking for the QProgressBar class. The documentation makes it clear below. You will need to set up the minimum and maximum values, and it will do the job for you.
The QProgressBar widget provides a horizontal or vertical progress bar.
A progress bar is used to give the user an indication of the progress of an operation and to reassure them that the application is still running.
The progress bar uses the concept of steps. You set it up by specifying the minimum and maximum possible step values, and it will display the percentage of steps that have been completed when you later give it the current step value. The percentage is calculated by dividing the progress (value() - minimum()) divided by maximum() - minimum().
You can specify the minimum and maximum number of steps with setMinimum() and setMaximum. The current number of steps is set with setValue(). The progress bar can be rewound to the beginning with reset().
If minimum and maximum both are set to 0, the bar shows a busy indicator instead of a percentage of steps. This is useful, for example, when using QNetworkAccessManager to download items when they are unable to determine the size of the item being downloaded.
I do not think much more details can be provided based on the question as the worker loop seems to be commented without actual code being provided in there, but this documentation should make it clear either way.
Note that I would personally even move the worker loop into an own worker thread if it is that hefty that it deserves a progressbar. As for the progressbar, you would probably write something like this:
QProgressBar bar(this);
bar.setRange(maximum, maximum);
bar.setValue(minimum);
bar.show();
Dialog box:
My novice suggestion would be to use progress bar inside your for loop and increment the progress bar as each file finishes loading.
Let me know if you need more detail.

How do I progressively load a widget in QT?

I have a custom widget which displays many items in rows:
void update(){ //this is a SLOT which is connected to a button click
QVBoxLayout *layout = this->layout();
if (layout == NULL){
layout = new QVBoxLayout;
this->setLayout(layout);
} else {
QLayout_clear(layout); //this is a function that I wrote that deletes all of the items from a layout
}
ArrayList *results = generateData(); //this generates the data that I load from
for (int i = 0; i < results->count; i++){
layout->addWidget(new subWidget(results->array[i]));
}
}
The problem is that there are about 900 items and a profile reveals that simply adding the child object to the layout takes 50% of the time (constructing takes the other 50%). Overall it takes about 3 seconds to load all of the items.
When I click on the button to load more data, the entire UI freezes for the 3 seconds and then all of the items appear together when everything is done. Is there a way to progressively load more items as they are being created?
The first trick is, as Pavel Zdenek said, to process only some of the results. You want to process as many together so that the overhead (of what we're going to do in the next step) is low, but you don't want to do anything that would make the system seem unresponsive. Based on extensive research, Jakob Nielsen says that "0.1 seconds is about the limit for having the user feel that the system is reacting instantaneously", so as a rough estimate you should cut your work into roughly 0.05 second chunks (leaving another 0.05 seconds for the system to actually react to the user's interactions).
The second trick is to use a QTimer with a timeout of 0. As the QTimer documentation says:
As a special case, a QTimer with a timeout of 0 will time out as soon
as all the events in the window system's event queue have been
processed. This can be used to do heavy work while providing a snappy
user interface.
So that means that a timer with a timeout of 0 will be executed next, unless there is something else in the event queue (for instance, a mouse click). Here's the code:
void update() {
i = 0; // warning, this is causes a bug, see below
updateChunk();
}
void updateChunk() {
const int CHUNK_THRESHOLD = /* the number of things you can do before the user notices that you're doing something */;
for (; i < results->count() && i < CHUNK_THRESHOLD; i++) {
// add widget
}
// If there's more work to do, put it in the event queue.
if (i < results->count()) {
// This isn't true recursion, because this method will return before
// it is called again.
QTimer::singleShot(0, this, SLOT(updateChunk()));
}
}
Finally, test this a little bit because there's a gotcha: now the user can interact with the system in the "middle" of your loop. For instance, the user can click the update button while you're still processing results (which in the above example means that you would reset the index to 0 and reprocess the first elements of the array). So a more robust solution would be to use a list instead of an array and pop each element off the front of the list as you process it. Then whatever adds results would just append to the list.
#Adri is generally right, the twist is that the "another thread" must be the UI thread again. The point is to allow UI thread's event loop to keep spinning. The fast and dirty way is to put QCoreApplication::processEvents() in your for() cycle. Dirty because, as the doc says, it should be called "ocassionally". It might have some overhead even if there are no UI events, and you are messing Qt's performance optimization as to when and how often spin the loop. Slightly less dirty would be to call it only ocassionally, after chunks of result.
Cleaner and proper way is to create a private slot, which pops one result element (or chunk, to speed up), adds to the layout and increments index. Then it will recall itself until end of results. The gotcha is to define connect() with forced connection type Qt::QueuedConnection, so it will get deferred after already queued UI events (if any).
And because you run in only one thread, you don't need any locking over results.
Adding example per OP's request:
While #TomPanning solution is correct, it kind of hides the real solution behind QTimer which you don't need - you don't need any timing, you just need a specific non-timer behavior upon specific parameter value. This solution does the same thing, minus the QTimer layer. On the other hand, #TomPanning has a very good point about the plain ArrayList not being very good data storage, when interaction can happen in between.
something.h
signals: void subWidgetAdded();
private slots: void addNextWidget();
ArrayList* m_results;
int m_indexPriv;
something.cpp
connect(this,SIGNAL(subWidgetAdded()),
this,SLOT(addNextWidget(),
Qt::QueuedConnection);
void addWidget() {
// additional chunking logic here as you need
layout->addWidget(new subWidget(results->array[m_indexPriv++]));
if( m_indexPriv < results->count() ) {
emit subWidgetAdded(); // NOT a recursion :-)
}
}
void update() {
// ...
m_results = generateData();
m_indexPriv = 0;
addNextWidget(); // slots are normal instance methods, call for the first time
}