Sort QStandardItemModel in c++ Qt - qt

I have a model of type QStandardItemModel which looks like this:
QHash<int, QByteArray> roleNames;
roleNames[Car2goVehicle::NameRole] = "plate_number";
roleNames[Car2goVehicle::DescriptionRole] = "address";
roleNames[Car2goVehicle::FuelRole] = "fuel";
roleNames[Car2goVehicle::InteriorRole] = "interior";
roleNames[Car2goVehicle::ExteriorRole] = "exterior";
roleNames[Car2goVehicle::VinRole] = "vin";
roleNames[Car2goVehicle::LatRole] = "lat";
roleNames[Car2goVehicle::LonRole] = "lon";
roleNames[Car2goVehicle::DistanceRole] = "distance";
d->m_vehiclesmodel = new RoleItemModel(roleNames);
and now I want to sort according to distance like this
d->m_vehiclesmodel->setSortRole(Qt::UserRole);
d->m_vehiclesmodel->sort(Car2goVehicle::DistanceRole, Qt::AscendingOrder);
But the result is wrong. Can somebody tell me how to sort ?
Thanks.

What's wrong with the result?
In most cases, one doesn't sort the model itself, but the view, using a QSortFilterProxyModel. Here's the example from the documentation:
QTreeView *treeView = new QTreeView;
MyItemModel *sourceModel = new MyItemModel(this);
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(sourceModel);
treeView->setModel(proxyModel);
In your example above, you might mix up roles and columns. Your Role enum should look like this:
enum Role {
NameRole=Qt::UserRole,
DistanceRole,
...
};
If you want to sort by distance role, you call:
model->setSortRole( Car2goVehicle::DistanceRole );
Then, sort by some column (which has nothing to do with the role), E.g. column 0:
model->sort( 0, Qt::AscendingOrder );

I wouldn't recommend using QSortFilterProxyModel, if QStandardItemModel::sort() is powerful enough. Instead, I stick use basic Qt signals.
Here, I'm using PyQt, but the code should also work in C++:
self.model = QStandardItemModel()
self.populate_model()
self.model.setHorizontalHeaderLabels(map(str, range(self.model.rowCount())))
self.treeview.setModel(self.model)
self.treeview.header().setSectionsClickable(True)
self.treeview.header().setSortIndicatorShown(True)
self.treeview.header().sortIndicatorChanged.connect(self.model.sort)
Notice the last line connecting QHeaderView::sortIndicatorChanged with QStandardItemModel::sort.

Related

New style casting with QT

I want to update my code from old style casting to new style one. I have some problems with understanding the different type of casts.
1 case
if (((QDataItem*)(*it))->GetType()==QDataItem::AudioTrack){
Here I have a class "QDataItem". This contain an enum of track types, like AudioTrack. Basing on a QTreeWidget I iterate through the QTreeWidget items. Each Item represents a QDataItem. Now with new casting I want to do:
if ((static_cast<QDataItem*>(*it))->GetType()==QDataItem::AudioTrack){
Is this there right way to do?
2 case
In old style I have a twice casting
QAudioTrackItem *audio_track = (QAudioTrackItem*)(QDataItem*)(*it);
QAudioTrackItem is like QDataItem a class. I want to do here:
QAudioTrackItem *audio_track = reinterpret_cast<QAudioTrackItem*>(*it)
But I am not sure that this is correct in case of the missing QDataItem cast.
Is my result ok or do I have a bug?
Is this there right way to do?
Yes, (QDataItem*)(*it) and static_cast<QDataItem*>(*it) should be identical with your code.
But if your classes have such inheritance structure:
class QDataItem : QObject {};
class QAudioTrackItem : QDataItem {};
you should really consider using qobject_cast<>() instead:
if (auto item = qobject_cast<QAudioTrackItem *>(*it)) {
....
}
Is my result ok or do I have a bug?
Maybe, it depends on how your inheritance structure look like.
If they inherit just like my example above, it's totally OK to use reinterpret_cast<>() to convert any pointers between QObject, QDataItem and QAudioTrackItem.
But if your classes have multiple inheritance:
class QDataItem {};
class QAudioTrackItem : QObject, QDataItem {};
reinterpret_cast could kick your ass badly:
auto item = new QAudioTrackItem;
auto p1 = reinterpret_cast<QObject *>(item); // OK
auto p2 = reinterpret_cast<QAudioTrackItem *>(p1); // OK
auto p3 = reinterpret_cast<QDataItem *>(item); // Undefined Behavior
auto p4 = reinterpret_cast<QDataItem *>(p1); // Undefined Behavior

Xamarin grid, column and row amounts

Hi im relatively new to c# code and i was wondering if there is any way to get the amount of columns and rows in a grid and store that amount in a variable
Something like:
var columnamount = grid.columnamount;
But i could not find anything that works
Thanks
You can use the following code to get a count of the columns and rows directly via the ColumnDefinitions and RowDefinitions properties. No need to enumerate the children of the grid because you may not have views in every column/row.
var columnCount = grid.ColumnDefintions.Count;
var rowCount = grid.RowDefinitions.Count;
For reference the documentation.
You might be able to do it this way, purely based on what I see in the docs:
var countColumns = grid.Children.Where( c => c.Column).Max();
var countRows = grid.Children.Where( c => c.Row).Max();
But I'm not sure if you can access Row anf Column properties on the child element.
This is not the best way to check, I guess, but it's working (same thing for columns):
EDIT: nope, for columns it doesn't work
int GetRowsCount(Grid grid)
{
var item = grid.Children.FirstOrDefault();
return item != null ? Grid.GetRow(item) + 1 : 0;
}

Q3DSurface - define a simple Graph

I am really new to Qt-coding (and coding in general) and would like to plot a numeric function. I thought the best tool/class to use would be Q3DSurface.
Unfortunately I can not even define a simple graph. I tried to use that example
https://doc.qt.io/qt-5/qtdatavisualization-surface-example.html
but I am not even able to write the following line from the main.cpp
Q3DSurface *graph = new Q3DSurface();
Obviously there is no constructor without arguments in the version 5.9.
This is my attempt so far:
using namespace QtDataVisualization;
Surface::Surface()
{
QSurfaceFormat* format= new QSurfaceFormat();
QWindow * window= new QWindow(Q_NULLPTR);
Q3DSurface* m_pGraph= new Q3DSurface(format, window);
}
The constructor is defined as
Q3DSurface(const QSurfaceFormat *format = Q_NULLPTR, QWindow *parent = Q_NULLPTR).
This attempt isn't working. Do you have an idea how I could create a new object of Q3DSurface?

Using GraphView Library for functions to display multiple graphs

I'm currently developing an android app for reading out multiple sensor values via Bluetooth and display them in a graph. When I stumbled upon jjoe64's GraphViewLibrary, I knew this would fit my purposes perfectly. But now I'm kind of stuck. Basically, I wrote a little function that would generate and display the values of three sensors in 3 different graphs one under the other. This works just fine when the activity is started first, all three graphs a nicely rendered and displayed. But when I want to update the graphs with different values using the resetData()-method to render the new values in each graph, only the last of the three graphs is updated. Obviously, because it's the last graph generated using this rather simple function. My question is: Is there any other elegant way to use a function like mine for generating and updating all three graphs one after the other? I already tried to set the GraphView variable back to null and different combinations of removing and adding the view. Passing the function a individual GraphView-variable like graphView1, graphView2... does also not work.
Here is the function:
private GraphView graphView;
private GraphViewSeries graphViewSerie;
private Boolean graphExisting = false;
...
public void makeGraphs (float[] valueArray, String heading, int graphId) {
String graphNumber = "graph"+graphId;
int resId = getResources().getIdentifier(graphNumber,"id", getPackageName());
LinearLayout layout = (LinearLayout) findViewById(resId);
int numElements = valueArray.length;
GraphViewData[] data = new GraphViewData[numElements];
for (int c = 0; c<numElements; c++) {
data[c] = new GraphViewData(c+1, valueArray[c]);
Log.i(tag, "GraphView Graph"+graphId+": ["+(c+1)+"] ["+valueArray[c]+"].");
}
if (!graphExisting) {
// init temperature series data
graphView = new LineGraphView(
this // context
, heading // heading
);
graphViewSerie = new GraphViewSeries(data);
graphView.addSeries(graphViewSerie);
((LineGraphView) graphView).setDrawBackground(true);
graphView.getGraphViewStyle().setNumHorizontalLabels(numElements);
graphView.getGraphViewStyle().setNumVerticalLabels(5);
graphView.getGraphViewStyle().setTextSize(10);
layout.addView(graphView);
}
else {
//graphViewSerie = new GraphViewSeries(data);
//graphViewSerie.resetData(data);
graphViewSerie.resetData(new GraphViewData[] {
new GraphViewData(1, 1.2f)
, new GraphViewData(2, 1.4f)
, new GraphViewData(2.5, 1.5f) // another frequency
, new GraphViewData(3, 1.7f)
, new GraphViewData(4, 1.3f)
, new GraphViewData(5, 1.0f)
});
}
And this is the function-call depending on an previously generated array (which is being monitored to be filled with the right values):
makeGraphs(graphData[0], "TempHistory", 1);
makeGraphs(graphData[1], "AirHistory", 2);
makeGraphs(graphData[2], "SensHistory", 3);
graphExisting = true;
Any help and / or any feedback in general is greatly appreciated! Lots of thanks in advance!
EDIT / UPDATE:
Thanks to jjoe64's answer I was able to modify the function to work properly. I was clearly having a mistake in my thinking, since I thought I'd also be changing a GraphViewSeries-object I would handle my function as additional parameter (which I tried before). Of course this does not work. However, with this minor Improvements I managed to make this work using a Graphviewseries Array. To give people struggling with a similar problem an idea of what I had to change, here the quick-and-dirty draft of the solution.
I just changed
private GraphViewSeries graphViewSerie;
to
private GraphViewSeries graphViewSerie[] = new GraphViewSeries[3];
and access the right Series using the already given parameter graphId within the function (if-clause) like this:
int graphIndex = graphId - 1;
graphViewSerie[graphIndex] = new GraphViewSeries(data);
In the else-clause I'm updating the series likewise by calling
graphViewSerie[graphIndex].resetData(data);
So, once again many thanks for your support, jjoe64. I'm sorry I wasn't able to update the question earlier, but I did not find time for it.
of course it is not working correct, because you save always the latest graphseries-object in the member graphViewSerie.
First you have to store the 3 different graphviewseries (maybe via array or map) and then you have to access the correct graphviewseries-object in the else clause.

How can I pass controls as reference in Bada?

In the big picture I want to create a frame based application in Bada that has a single UI control - a label. So far so good, but I want it to display a number of my choosing and decrement it repeatedly every X seconds. The threading is fine (I think), but I can't pass the label pointer as a class variable.
//MyTask.h
//...
result Construct(Label* pLabel, int seconds);
//...
Label* pLabel;
//MyTask.cpp
//...
result
MyTask::Construct(Label* pLabel, int seconds) {
result r = E_SUCCESS;
r = Thread::Construct(THREAD_TYPE_EVENT_DRIVEN);
AppLog("I'm in da constructor");
this->pLabel = pLabel;
this->seconds = seconds;
return r;
}
//...
bool
Threading::OnAppInitializing(AppRegistry& appRegistry)
{
// ...
Label* pLabel = new Label();
pLabel = static_cast<Label*>(pForm->GetControl(L"IDC_LABEL1"));
MyTask* task = new MyTask();
task->Construct(&pLabel); // HERE IS THE ERROR no matching for Label**
task->Start();
// ...
}
The problem is that I have tried every possible combination of *, &, and just plain pLabel, known in Combinatorics...
It is not extremely important that I get this (it is just for training) but I am dying to understand how to solve the problem.
Have you tried:
task->Construct(pLabel, 0);
And by that I want to point out that you are missing the second parameter for MyTask::Construct.
No, I haven't. I don't know of a second parameter. But this problem is solved. If I declare a variable Object* __pVar, then the constructor should be Init(Object* pVar), and if I want to initialize an instance variable I should write
Object* pVar = new Object();
MyClass* mClass = new MyClass();
mClass->Construct(pVar);

Resources