Classes, Objects, Treewidget and QlistWidget - c++

I'm trying to create a program for a project in school (University).
The program is basically supposed to have a QTreeWidget with a bunch of components, the QTreeWidget will update when you click on a button (for example Chassis-button will change the QTreeWidget into a bunch of different chassis).
From the QTreeWidget, you're then supposed to be able to mark one that you want and click on a "choose-button" which will transfer that row to a QListWidget. One example of a row could be :
"Fractal Design"
"R3"
"100euro"
"ATX"
I have a bunch of classes for each component. One of the classes is Chassis and it has a function named addChassis which looks like this :
void ChassisHandler::addChassis(string manufacturer, string model, int price, string size, string color, int fanSpots) {
Chassis **temp = new Chassis*[this->nrOfChassis + 1];
for (int i = 0; i < nrOfChassis; i++)
{
temp[i] = this->chassis[i];
}
delete[] this->chassis;
this->chassis = temp;
this->chassis[this->nrOfChassis] = new Chassis(manufacturer, model, price, size, color, fanSpots);
this->nrOfChassis++;
}
This function works fine if I want to create a class object and add a few chassis into the object and then print out the object, but I can not use it to add chassis into the treewidget. It needs to be QString instead of string and int and Qt seems to have a problem with me making a class object and then transfer the object to the treewidget. I simply do not have enough knowledge to be able to put all the chassis into the QTreeWidget. Right now I've created an additional function in my .cpp file that belongs to the .ui file which look like this :
void Computer::AddChassi(QString manufacturer, QString model, QString price, QString size, QString color, QString fanSpots){
QTreeWidgetItem *itm = new QTreeWidgetItem(ui->treeWidget);
itm->setText(0, manufacturer);
itm->setText(1, model);
itm->setText(2, price);
itm->setText(3, size);
itm->setText(4, color);
itm->setText(5, fanSpots);
}
But if I try to put this function in the Chassiclass, it says that "UI is not defined". It's very important that we use classes in this project.
So my two problems are :
How to create proper addfunctions to put strings into my TreeWidget?
How to transfer the wanted string from my TreeWidget to my ListWidget?

if I try to put this function in the Chassi class, it says that "UI is
not defined".
Your AddChassi() method looks mostly correct, except of course you are trying to dereference a variable named "ui" and (it appears that) there is no member variable named "ui" that is part of your Computer class, which is why you get that compiler error. Presumably the pointer "ui" is one that is available for use only in other contexts (e.g. because it is a member variable of another class), so making it available for use inside AddChassi() is just a matter of passing it in -- you could pass it every time as one of the arguments to AddChassi() if you want, or you could pass it in to the Computer class's constructor and hold it as a member variable of the Computer class for later use. Or, perhaps better yet, instead of passing in the ui pointer, just pass in the pointer to the QTreeWidget object, since that is the only thing you really need to pass in to the QTreeWidgetItem constructors anyway.
For example:
void Computer::AddChassi(QTreeWidget * tw, QString manufacturer, QString model, QString price, QString size, QString color, QString fanSpots){
QTreeWidgetItem *itm = new QTreeWidgetItem(tw);
[...]
my Second problem is to transwer the wanted string from my TreeWidget
to my ListWidget
The QTreeWidget class has various accessor functions (such as currentItem() and topLevelItem()) that you can use to obtain a pointer to one of the QTreeWidgetItems objects current attached to the QTreeWidget. Once you have that pointer, you can call the QTreeWidgetItem::text(int) method on it to extract the QString representing the text in the nth column of that item's row. Once you have that QString you can use it to create a new QListWidgetItem with that QString as its constructor argument.

Related

UE4 How to use UMG ListView to display a list of Structs?

I apologize if this seems trivial, but I've searched for an answer for a while now, and I can't seem to find a solution.
I have a list of structs (TArray of structs to be exact.) that represent high scores in my game. (Each struct represents a high score, the fields are something like "Name, Date, Player Score, Game Mode etc" I use this SaveGame method to save a load my array of high scores to and from a file.
I used this with mock values and the system works, now I want to create a UMG widget that will display the list of high scores and this is where I ran into a snag.
I want to use a ListView to show each struct as a ListViewEntry. As far as I understand(I was following this tutorial), A UMG List View needs it's entry widgets to implement the IUserObjectListEntry specifically one has to implement the OnListItemObjectSet (UObject* ListItemObject) method. This method is responsible for assigning an object to the Listview entry and mapping its fields to the various visual widgets. You can see my problem now, I have a list of UStructs and this method needs a UObject pointer.
I'm really at a loss at what I need to do to make this work with a UStruct. Short of creating a dummy UObject that's pretty much identical to my struct and before passing the struct to this function I need to copy its fields into the dummy UObject and pass it instead. I think this method is very inelegant. There has to be a better way. Do you know any?
I wanted to avoid creating a dummy UObject just for the sake of passing it to this function.
I tried to use an array of UObjects instead of an array of Structs but the problem is, an array of UObjects is always an array of pointers, and when it gets saved, the pointers getting saved and not the actual data, so when it's loaded the data is useless.
Maybe there is a Struct-specific interface one can implement for a ListViewEntry widget? Or maybe there is a way to dereference the pointers of the array of Uobjects before saving them?
TL;DR
I have the following stuct:
c++
USTRUCT()
class FHighScoreEntry
{
GENERATED_BODY()
public:
//Player name
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString PlayerName;
//Player Score
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 PlayerScore;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FDateTime CurrentDateTime;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TEnumAsByte<EGameType> GameType;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 AccuracyTrialMaxTries;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 TimeTrialMaxTime;
}
In the following array;
c++
TArray<FHighScoreEntry> HighScores;
I want to show the array of high scores in a UMG ListView. The ListView requires its entries to implement the User List Object interface, which has this function:
As you can see, the event only accepts UObjects. Hence my problem.
This was asked 8 months ago, so may no longer be useful to you. But this post is the only thing I could find when searching for this issue on the Internet, so I am posting my solution for the next person researching this.
At a high level, create a UObject wrapper for the struct.
In my USaveGame class I have an array of structs because as you mentioned, an array of UObject pointers does not actually save any data. I created an UObject derived class that simply contains the same struct as the sole UPROPERTY.
UCLASS(Blueprintable, BlueprintType)
class PORTALTEST_API UHighScoreObject : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Score")
FHighScore HighScore;
};
In my Game Instance class, I have an array of pointers to this UObject
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Score")
TArray<UHighScoreObject*> HighScoreArray;
I use this array of UObject pointers for the List View of the widget.
HighScoreWidgetBlueprint
In the Save function of my Game Instance class I clear the struct of arrays and fill it with with the data contained in the array of UObject pointers. (I am only keeping the top ten high scores, so this seemed more efficient than keeping track of changes in both arrays.)
bool UMyGameInstance::SaveHighScore()
{
// Call SaveGameToSlot to serialize and save our SaveHighScoreObject with
//name HighScoreSaveSlot.sav
// Retrieve save values
SaveHighScoreObject->HighScoreArray.Empty();
for (auto HighScore : HighScoreArray)
{
SaveHighScoreObject->HighScoreArray.Add(HighScore->HighScore);
}
// Save game to file
const bool IsSaved = UGameplayStatics::SaveGameToSlot(SaveHighScoreObject,
UNIQUE_HIGHSCORE_SLOT, 0);
return IsSaved;
}
And in the Load function of my game instance I read in the array of structs and populate the array of UObjects.
bool UMyGameInstance::LoadHighScore()
{
// Try to load a saved game file with "HighScoreSaveSlot.sav if it exists
USaveGame* LoadedHighScore =
UGameplayStatics::LoadGameFromSlot(UNIQUE_HIGHSCORE_SLOT, 0);
SaveHighScoreObject = Cast<UHighScoreSaveGame>(LoadedHighScore);
//If the file does not exist, create a new one
if (!SaveHighScoreObject)
{
// Instantiate a new SaveGame object
SaveHighScoreObject = Cast<UHighScoreSaveGame>
(UGameplayStatics::CreateSaveGameObject(UHighScoreSaveGame::StaticClass()));
// Call SaveGameToSlot to serialize and save our game object with name
// "HighScoreSaveSlot.sav"
const bool IsSaved =
UGameplayStatics::SaveGameToSlot(SaveHighScoreObject, UNIQUE_HIGHSCORE_SLOT, 0);
return IsSaved;
}
else
{
for (auto HighScore : SaveHighScoreObject->HighScoreArray)
{
UHighScoreObject* HighScoreObj = NewObject<UHighScoreObject>
((UObject*)GetTransientPackage(), UHighScoreObject::StaticClass());
HighScoreObj->HighScore = HighScore;
HighScoreArray.Add(HighScoreObj);
}
return true;
}
}
ListView is made to represent unique objects, so its items need to be UObject, that’s the way the list view class is made.
That’s because adding/removing/looking up the widget for an item needs to be very fast. An object pointer is just a memory address, so it’s fast to find an item, and you can be sure they’re unique (your list won’t accidentally show two widget for the same object). Structs on the other hand, are any arbitrary data, which can be very long (depends on what they contain). So unless you make a hashing algorithm, it’s very expensive to look up if a struct is already in the list.
So for your needs, you can use objects instead of structs to show high scores. For example, objects for each player, since the players are probably already objects. The widget can then cast to the player class when on item object set, and take the high score variable of the player to show it.
If you want to use structs though, you can create a custom widget to show the high scores. To make a vertical list, just make a VerticalBox in your parent widget and a number of widgets for each item in your list, using the create widget from class node (or NewObject in cpp). Then, add your widgets as children of vertical box using the add child to vertical box function.

QT Creator accessing pointers to objects

Very new to QT but basically I have a class called object and on my GUI is a one button and one text browser. Now in my class object I have one private QString variable called name and its constructor assigns a value (QString) to the variable called name . The object class has one function called QString getName() const; which returns the name:
class object{
private:
QString name;
public:
object(QString name);
QString getName()const;
};
Now in QT in my mainwindow.h file I put
public:
object *o;
and then in the constructor :
object o2("Name");
o = &o2;
Now I want to call the function void MainWindow::on_pushButton_clicked() and all this function will do is set the text in the text browser to the name variable in my object (which would be "Name" btw) so inside the function I put ui->Console->setText(o->getName()); console being the name of my text browser- when i run the code and click the button its saying that Ive referenced memory and giving an error. Keep in mind I moved ui->Console->setText(o->getName()); to the constructor and it worked perfectly (obviously didnt work when the button was clicked but the text was put in the text browser) so what am I doing wrong here ?
The pointer (o) outlives the object that it points to (o2). One way to fix it is by allocating new memory for the object:
o = new object("Name");
And then you'll need to remember to delete that memory later.

Binding instance of C++ object to QML object

I'm new to Qt, and have written a basic application which has one class which inherits from QObject and is bound to a QML file.
I now want that class to contain a Vector of objects (let's say from a class Customers), which contains some data, such as a QString for name, etc.
To make life easier I'll create these objects manually in main, and place some text fields in my QML file.
I now want to be able to bind specific objects to specific text fields in the QML file, such that when a value changes, the value in the text field updates.
How can this be done? It looks like QML statically calls methods of the classes it's bound to, instead of on an assigned object.
I feel like QAbstractList may have some use here, but not too sure. Would rather not have to inherit from anything for my Customers class.
EDIT:
I think I may be able to do what I want with a QObjectList-based Model (https://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html). I notice it says at the bottom that "There is no way for the view to know that the contents of a QList has changed. If the QList changes, it is necessary to reset the model by calling QQmlContext::setContextProperty() again."
Does this mean that if a value inside DataObject (such as name) changes, the model has to be reset, or only when the Qlist itself changes (i.e. new item added, or item deleted)? If the latter, I would think this should be fairly easy to maintain as I would only need to set the context property whenever anything is added or deleted.
This may be usefull if you want to process raw QObject instance in QML script. You can append properties to Element class and modify them from qml.
class Element : public QObject {
Q_OBJECT
private:
QString type;
QLinkedList<Element*> child;
public:
explicit Element(QString type, QLinkedList<Element*> child);
virtual ~Element();
public:
QLinkedList<Element*> getChild() const;
QString getType() const;
public:
static void printTree(Element* root);
};
this is so simple you need to use NOTIFY
define your properties like this :
Q_PROPERTY (QString name READ name WRITE setName NOTIFY nameChanged)
then you need to define each one like this :
public : QString name() const;
signals :
void nameChanged(QString name);
public slots:
void setName(const QString &name);
private QString _name;
and then you should define body in cpp like this :
QString className::name() const{
return _name;
}
void className::setName(const QString &name){
if(name==_name) return;
_name = name;
Q_EMIT nameChanged(_name);
}
after registering it to QML with qmlRegisterType<ClassName>("com.className",1,0,"className");
simply set name it will notify if it changes for example in a textfield set text to that name property

how to create static QLabel in Qt

Is it possible to create some static QLabels in one class, and other classes can access its QLabels variable and apply changes to the QLabels without creating its object?
I found some answers online like if you want to access one class variables without creating its object in another class, you have to make its data static.
So basically what I am trying to do here is accessing and changing one class variables, for me it is QLabels, in another class without creating its object.
I know how to create static variables, but when comes to declare a staic QLabel, I found it difficult to achieve it.
I think you may just make the label accessible, i.e. expose it as a public member. Say you have a Form class, and a label QLabel in its ui. Add this method to the class:
public:
QLabel * label();
the implementation is just:
QLabel *Form::label()
{
return ui->label;
}
If all you need to expose is the label text property, just add these two accessors methods:
public:
QString labelText();
void setLabelText(QString & text);
in implementation file:
QString Form::labelText()
{
return ui->label->text();
}
void Form::setLabelText(QString &text)
{
ui->label->setText(text);
}
These last strategy fits encapsulation better.
About having it static: what if you have more than one instance of the Form class? Which label is supposed to be pointed to by the static member? If you are 100% sure you will have only one instance of the widget, you can add a static public QLabel * member:
public:
static QLabel * label;
in implementation file, on top:
QLabel *Form::label = 0;
in Form constructor:
ui->setupUi(this);
if(label == 0)
{
label = ui->label;
}
Again, this makes sense if you have one Form instance only. Otherwise, the static pointer will point forever to the label of the widget which was created first (and, dangerously, to nothing when that instance gets destroyed).

Qt Dynamically create QWidget from QString

I am trying to create a program that reads information from a database, and accordingly set up the layout. Specifically, I want to read two date fields and depending on the difference between the days, create a day(s) number of elements. Have anyone got an idea on how this could be done? I have tried to create an element using the QString->text() property with no success for obvious reasons and I have managed to write a function to create an element, but my problem is that I cannot control the name of the element, making it impossible for me with my rubbish knowledge about c++ to then interact with the given element.
Thank you for your time,
Cheers.
I think a QHash would be the perfect tool for your needs. It allows storing and lookup of pretty much anything through a unique key. That means you can store the widgets with their title as a key and then later retrieve a widget with a certain title from that hash.
Here is how to define such a hash:
// .h file
#include <QtCore/QHash>
#include <QtGui/QWidget>
class MyWidget : public QWidget
{
// ...
private:
QHash< QString, QWidget* > m_dynamicWidgetHash;
};
The Widgets (or any QWidget subclass) can then be stored in the hash like this, assuming the titles will always be unique:
// .cpp file
void MyWidget::someMethod()
{
QList< QString > widgetTitles = getWidgetTitlesFromSomewhere();
foreach( QString title, widgetTitles )
{
SomeWidgetSubclass* widget = new SomeWidgetSubclass( this );
widget->setTitle( title );
// Note: This will not work if two widgets can have the same title
Q_ASSERT( !m_dynamicWidgetHash.contains( title ) );
m_dynamicWidgetHash.insert( title, widget );
}
}
You can then later find your widgets knowing only the name like this:
// .cpp file
void MyWidget::someOtherMethod( const QString& title )
{
SomeWidgetSubclass* widget = m_dynamicWidgetHash.value( title );
if( !widget )
{
// TODO: Error Handling
return;
}
// Do whatever you want with the widget here
}
Also, it might be interested for you how to create object by class name using QMetaType. There is QMetaType::construct method. It requires that qRegisterMetaType function is should be called before. Detaild description is here.