showing a lot of widgets in qt5 is slow - c++

I'm developing an program that allows you to communicate with an Engine control unit of a small turbine. The ECU is configured using 255 8-bit numbers stored in an EEPROM (ECU communicates over RS232). each number represents one of the engine's settings.
So to make sense of this we have a file that describes the meaning of all these settings and this is loaded into my program, which generates a row of 15 standard QWidgets per setting.
I also implemented a way to categorize these settings, for example, all fuel consumption related settings belong to one category. there's a Qcombobox in which you can select which category you want to filter. clicking it calls the following function:
void MainWindow::on_listCategories_currentTextChanged(const QString &currentcategory) { //filter categories
//ui->checkBoxShowDifferences->setCheckState(Qt::Unchecked); //ugly hack!!
setUpdatesEnabled(false);
if(currentcategory == "ALL") {
for(int i=0; i<RegistrySettings.size(); i++) {
if(RegistrySettings[i].Policy < static_cast<int>(TMC_Program.CurrentActiveAccessPolicy)) {
hideOptionWidgets(RegistrySettings[i].address);
continue;
}
if(ui->checkBoxShowDifferences->isChecked()) {
if(RegistrySettings[i].value == RegistrySettings[i].compareValue) {
hideOptionWidgets(RegistrySettings[i].address);
continue;
}
}
showOptionWidgets(RegistrySettings[i].address);
}
} else {
for(int i=0; i<RegistrySettings.size(); i++) {
if(RegistrySettings[i].Policy < static_cast<int>(TMC_Program.CurrentActiveAccessPolicy)) {
hideOptionWidgets(RegistrySettings[i].address);
continue;
}
if(RegistrySettings[i].category != currentcategory) {
hideOptionWidgets(RegistrySettings[i].address);
continue;
}
if(ui->checkBoxShowDifferences->isChecked()) {
if(RegistrySettings[i].value == RegistrySettings[i].compareValue) {
hideOptionWidgets(RegistrySettings[i].address);
continue;
}
showOptionWidgets(RegistrySettings[i].address);
} else {
hideOptionWidgets(RegistrySettings[i].address);
}
}
}
setUpdatesEnabled(true);
while hiding works perfectly fine, when selecting "ALL" it needs to show all the hidden widgets, this is incredibly slow! it takes around 5 seconds to display all the widgets!
The code responsible for showing all the widgets is this:
void MainWindow::showOptionWidgets(int addr) {
if(addr < 0 || addr > 255) {return;}
QStringList WidgetNames = generateOptionRowWidgetNames(addr);
for(int i=1; i<WidgetNames.size(); i++) { //number 0 is a qhboxlayout and not a qwidget so it won't find it
QWidget* widgetToShow = ui->scrollAreaOptions->findChild<QWidget*>(WidgetNames[i]);
if(widgetToShow && widgetToShow->isHidden()) {
widgetToShow->show(); //extremely slow!
}
}
}
is there a way to optimize this?
note: all widgets are standard ones! just labels, checkboxes and buttons, generate optionRowWidgetNames only generates a simple QStringlist with fixed strings that only have the corrosponding settings number attached to them.

Related

Tab to next visible column in QTableView

I have a custom QTableView and QAbstractTableModel. My QTableView hides some of the columns from the QAbstractTableModel as they aren't needed.
When I hit Tab, I would like to select the next available (editable) column. My current implementation is to grab the next index from the QAbstractTableModel, but this index includes columns that are hidden. (So when hitting Tab it may be a couple presses before you see the "next" column selected.)
How can I tell Tab to jump to the next visible column?
The language is C++. Below is the code within my QTableView:
void keyPressEvent(QKeyEvent* event)
{
if((event->modifiers() == Qt::KeyboardModifier::NoModifier) && (event->key() == Qt::Key::Key_Tab))
{
this->moveToNextCell();
}
else
{
this->QTableView::keyPressEvent(event);
}
}
void moveToNextCell()
{
const QModelIndex index = this->currentIndex();
int nextColumn = index.column() + 1;
if(index.column() <= lastEditableCol)
{
this->setCurrentIndex(model->index(index.row(), nextColumn));
}
}
It's not elegant, but I've solved the problem by using isColumnHidden() from QTableView. I just iterate through the columns until I find one that isn't hidden.
for(int i = nextColumn; i <= numOfColumns && nextColumn <= numOfColumns; i++)
{
if(this->isColumnHidden(nextColumn) == true)
{
nextColumn += 1;
}
else
{
i = numOfCol + 1;
}
}

RTS style of drag and select system

I'm trying to make a click and drag selection system in c++ and SDL2 like the kind used in real time strategy games. You click with the mouse and drag it over what you want to select. How can I go about making it?
Edit: I know how to handle the mouse inputs. I have used a Rect structure to track the size of the selection zone. However while the zone draws correctly, the objects inside the zone don't react at all. However individually clicking on them works fine every time.
I guess my question is what is the best way to check for a group selection vs individual object selection?
You have to check what's inside the rect you are gonna drag (the test depends on the shapes that are included) and the drag those shapes aswell
I can describe how i have implemented this.
In the event handling routine, do something like the code below. I think the method names explain quite well what's happening and how i'm thinking (this is copied from my hobby-hack-RTS-engine which is based on SDL2):
case SDL_MOUSEBUTTONDOWN:
{
// Calculate index, x and y for the tile that was clicked in the map.
int iClick = m_Map.getTileIndex(event.button.x, event.button.y);
if(iClick >= 0)
{
int xClick = m_Map.getTileX(iClick);
int yClick = m_Map.getTileY(iClick);
if((int)event.button.button == 1)
{
// Unmark all MO..
for(std::list<MGMovingObject>::iterator it = m_MO.begin(); it != m_MO.end(); it++)
{
it->unMark();
}
activateFraming(event.button.x, event.button.y);
}
else
{
...
}
}
break;
}
case SDL_MOUSEBUTTONUP:
{
if((int)event.button.button == 1)
{
int endClickX = m_Map.getTileX(m_Map.getTileIndex(getFrameEndX(), getFrameEndY()));
int endClickY = m_Map.getTileY(m_Map.getTileIndex(getFrameEndX(), getFrameEndY()));
int startClickX = m_Map.getTileX(m_Map.getTileIndex(getFrameStartX(), getFrameStartY()));
int startClickY = m_Map.getTileY(m_Map.getTileIndex(getFrameStartX(), getFrameStartY()));
if(endClickX > 0 && endClickY > 0 && startClickX > 0 && startClickY > 0)
{
for(int x = std::min(startClickX, endClickX); x <= std::max(startClickX, endClickX); x++)
{
for(int y = std::min(startClickY, endClickY); y <= std::max(startClickY, endClickY); y++)
{
for(std::list<MGMovingObject>::iterator it = m_MO.begin(); it != m_MO.end(); it++)
{
if(it->getTileX() == x && it->getTileY() == y)
{
it->mark();
}
}
}
}
}
deactivateFraming();
}
else
{
...
}
break;
}
My selectable objects are stored as std::list<MGMovingObject> in m_MO.
The idea is that i save tile coordinates of the start of the selection frame and the end of the selection frame. I then iterate over the selectable objects and detect the ones inside the selection frame. I select these (mark()) and when i iterate over the objsects at a later stage, say during rendering, i can read out if they are selected (isMarked()).
If you want to steal code or ideas, here is the actual source file i copied it from: https://github.com/qmargyl/mgframework/blob/master/src/mgframework/mgframework.cpp

Qt: How to properlly use setUpdatesEnabled to help GUI performance?

How to properlly use setUpdatesEnabled to help GUI performance?
Here is my code:
void uavTabGroup::updateMgr()
{
setUpdatesEnabled(false);
for (updateCtr = 0; updateCtr < max_num; updateCtr++)
{
if (infoView[updateCtr] != NULL)// its a child widget to the current widget
{
infoView[updateCtr]->updateDisplay();
}
}
setUpdatesEnabled(true);
}
However, there is no improvement compared to don't use setUpdatesEnalbed() function, am I doing this wrong. BTW, all the infoView[i] are in a girdlayout of the current widget.
Now I am doing the updating in this way:
void uavTabGroup::updateMgr()
{
if (updateCtr >= max_num)
updateCtr = 0;
if (infoView[updateCtr] != NULL)
{
infoView[updateCtr]->updateDisplay();
}
updateCtr++;
}
And trigger the updateMgr at a higher frequency, and the child widgets are getting updated one by one sequentially.
But someone told me this is wrong... So which is the correct solution?

Qt: What is the best approach to update the widgets in the GUI?

I used a QTimer to control when should I update my widgets.
Many people suggested to do:
void uavTabGroup::updateMgr()
{
setUpdatesEnabled(false);
for (updateCtr = 0; updateCtr < max_num; updateCtr++)
{
if (infoView[updateCtr] != NULL)// its a child widget to the current widget
{
infoView[updateCtr]->updateDisplay();
}
}
setUpdatesEnabled(true);
}
But I found that to update the widgets sequentially give me better results:
void uavTabGroup::updateMgr()
{
if (updateCtr >= max_num)
updateCtr = 0;
if (infoView[updateCtr] != NULL)
{
infoView[updateCtr]->updateDisplay();
}
updateCtr++;
}
In this case the updateMgr() is called at higher frequency, but for each time only one of its child widget is updated.
What is the best way?

Parent-dependent QTreeWidgetItem checkboxes in dynamically generated QTreeWidget

I'm writing an application that has a QTreeWidget that is populated by parsing an XML file containing the tree levels. If I select a top level checkbox, I need all of the sub-level checkboxes to be checked also.
I already have the XML parser working and populating the QTreeWidget with QTreeWidgetItems that have checkboxes but they can only be individually checked.
To do this, keep the code you have to generate the tree with your XML. Then connect to the itemChanged() signal and update the check states in a slot. It should look something like:
connect(treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
this, SLOT(updateChecks(QTreeWidgetItem*, int)));
void ClassName::updateChecks(QTreewidgetItem* item, int column)
{
// Checkstate is stored on column 0
if(column != 0)
return;
recursiveChecks(item);
}
void ClassName::recursiveChecks(QTreeWidgetItem* parent)
{
Qt::CheckState checkState = parent->checkState(0);
for(int i = 0; i < parent->childCount(); ++i)
{
parent->child(i)->setCheckState(0, checkState);
recursiveChecks(parent->child(i));
}
}
A few notes to consider:
You may be tempted to use the itemClicked signal instead of the itemChanged signal. This usually works, but will not work when the user uses the arrow keys and the space bar to change checkstates.
You will need to think about what will happen when you uncheck one of the sub-items that have been checked by clicking on its parent. Usually this means you need to make all ancestors either uncheck or partially-checked. This may not be true for your case.
itemUpdated will also get fired for other changes to the item (like the text changing), so be aware that this is not a super efficient way of doing this.
I just worked on this a little and got nice results based on Rick's answer. Maybe it can help other out there.
It updates the state of parents and children with a tristate status for parents only (checked, unchecked, partially-checked).
void ClassName::updateChecks(QTreeWidgetItem *item, int column)
{
bool diff = false;
if(column != 0 && column!=-1)
return;
if(item->childCount()!=0 && item->checkState(0)!=Qt::PartiallyChecked && column!=-1){
Qt::CheckState checkState = item->checkState(0);
for (int i = 0; i < item->childCount(); ++i) {
item->child(i)->setCheckState(0, checkState);
}
} else if (item->childCount()==0 || column==-1) {
if(item->parent()==0)
return;
for (int j = 0; j < item->parent()->childCount(); ++j) {
if(j != item->parent()->indexOfChild(item) && item->checkState(0)!=item->parent()->child(j)->checkState(0)){
diff = true;
}
}
if(diff)
item->parent()->setCheckState(0,Qt::PartiallyChecked);
else
item->parent()->setCheckState(0,item->checkState(0));
if(item->parent()!=0)
updateChecks(item->parent(),-1);
}
}
Doesn't need recursiveChecks() anymore. Connect between the treeWidget and updateChecks still active.
This appears still quite high in search engines, and is outdated.
Just set the flag Qt::ItemIsAutoTristate on your top-level item.