Qt Editing Custom Data Structures - c++

I am trying to utilize the Model/View architecture to accomplish my goal but I am unsure as to whether this is the proper tool to use for this task.
I have a Material System I have been using, which I more recently have created an editor for using Qt, it uses a QGraphicsView to display items which are interconnected to form a final fragment shader code, this works extremely well however, I am having difficulties finding a way to display these custom data structures without hand coding widgets for each type of item.
These custom data structures are simply classes with members which I wish to modify using an interface depending on each members type. For instance if the item is a Value which in my material system represents a single variable, like a texture, I would like to be able to have 2 editable areas, one for the texture's path on disk, and one for the name of the variable.
What is the best approach for interpreting custom structures like this and creating widgets based on their type, and then mapping those widgets to edit/display them. All the google searching I found led me to the Model/View architecture however it seems this is more made for things like SQL databases and XML files.
class Value
{
std::string m_strType, m_strName, m_strValue, m_strLocation;
};

It sounds like you're looking for something like the Property Browser Framework. In short, you'll make all of the members you want to edit properties, and then use the property browser framework to create a model that you can then attach a view to.

Related

testing a component with complex angular components nested inside using PageObjects

Our Application has components which consume components with consume components of varying complexity. So i just want the input on the page, to validate when an object is set that the text is correct. The issue is that it is one of these subcomponents.
My colleague told me that there is 2 ways to do this, The first is to use Page Objects, and Chaining annotation to find it on my page, and then find the next id etc until my input is found. It requires me to look through another teams' Component Markup to narrow it down to the input i want to leverage. I dont believe I should have to go into another component definition, or a definition of a definition to get the appropriate chain to get this arbitrary input. It starts to create issues where if a lateral team creates changes unbeknownst to me, my PO will be broken.
The other option my friend asked was to use fixture.query to find the component. This would be as simple as:
fixture.query((el)=> el.attribute["id"] == "description",
(comp){
expect(comp.value, value);
});`
Using Query looks at the markup but then will automatically componentize it as the appropriate SubComponent. In this case, comp.value is the value stored in the HTML. So, if i did something like:
fixture.update((MainComponent comp) {
comp.myinput.value = new Foo();
});
Then I am setting and getting this programmatically, so i am a bit unsure if it properly would reflect what is on the screen.
Whats the best course of action? It seems PO would be better, but im not sure if there is a way around having to deep query for input boxes outside of the component i am testing.
Thanks
I don't think I have a definitive answer for you but I can tell you how we do it at Google. For pretty much any component we provide the page object alongside the component. This is twofold it is for testing that widget, and also so we can have this as a shareable resource for other tests.
For leaf widgets the page objects are a little less fleshed out and are really just there for the local test. For components that are shared heavily the page object is a bit more flushed out for reusability. Without this much of the API for the widget (html, css, etc) we would need to consider public and changes to them would be very hard (person responsible for making the public breaking change needs to fix all associated code.) With it we can have a contract to only support the page object API and html structure changes are not considered breaking changes. At times we have even gone so far as to have two page objects for a widget. One for the local test, and one to share. Sometimes the API you want to expose for a local test is much more than you want people to use themselves.
We can then compose these page objects into higher level page objects that represent the widget. Good page objects support a higher level of abstraction for that widget. For example a calendar widget would let you go to the next/previous month, get the current selected date, etc. rather than directly exposing the buttons/inputs that accomplish those actions.
We plan to expose these page objects for angular_components eventually, but we are currently working on how to expose these. Our internal package structure is different than what we have externally. We have many packages per individual widget (page_objects, examples, widget itself) and we need to reconcile this externally before we expose them.
Here is an example:
import 'package:pageloader/objects.dart';
import 'material_button_po.dart';
/// Webdriver page object for `material-yes-no-buttons` component.
#EnsureTag('material-yes-no-buttons')
class MaterialYesNoButtonsPO {
#ByClass('btn-yes')
#optional
MaterialButtonPO yesButton;
#ByClass('btn-no')
#optional
MaterialButtonPO noButton;
}

Do I need to redesign my application?

I may be bordering on a discussion type question here so I apologise if the question is not specific enough.
I would like to know if I my current application design is inherently weak/flawed. My background is C so I am not using clever c++ patterns to their fullest extent, of this I am sure.
My application is similar to a 3D modelling package, without geometry creation (e.g setting up animations using existing models). The user imports a geometry and can set various parameters on pre existing geometry, and there are time dependent values that relate to the whole system. The output is a text file to be processed by another application.
I am using a QTreeview to render a QStandardItemModel. I simply store pointers to my core classes in the model's items. These have specific UI for each class type, and are all descended from a common base class. They all have a QWidget which is their "mainwidget"
When the user clicks on part of the treeview, the stored class is retrieved and its mainwidget is displayed on a pane on the UI. So - treeview on the left, pane to the right with the current item's contents, and a 3D view showing the geometry.
Most of my data is stored in the classes UI elements themselves; I don't have a central database which stores anything, and when it's time to save the project, I traverse the tree and let every item write itself to a QSettings file. This feels quite naive but it does work, and the reverse happens on project load. The project class generates new classes based on the type information in the settings file and they then read the contents out of the file themselves.
Similarly when writing the output file, each item knows how to write itself and does so. Where other classes can influence the output of others (for example, start and end times), higher level classes process the children and will set start and end times based on the order and duration of each child.
Should I be storing more data in the QStandardItemModel itself, or defining my own model perhaps? Does it sound like I have set myself up for future problems?
At the moment I have modified this system a couple of times to provide customised applications, but I am about to try and make it more generic. I welcome suggestions for improving my design. Go easy, please!
You should try to avoid creating god objects. Split your tasks and duties into smaller chunks. It makes it much easier to maintain and also much easier to extend if you need to.
Your specific use-case would benefit a lot from a more complete use of the Model-View-Controller pattern.
What doesn't make sense in your design is that your data objects hold a UI element. Since only one item can be shown in the right pane, this seems like a waste of resources. It makes more sense to have a single object that then gets passed a data object to display.
What I suggest for your program is the following:
Splitting your data into classes that only have functions for reporting and modifying values. There should be no knowledge of how to display data or store to a file.
Create separate class that handles the reading and writing from a file. If your model is very simple, you could just use single functions to do this using the method shown in the documentation for QDataStream or QTextStream.
Use your QTreeView and QStandardItemModel as Adaptor class between your data objects and the left pannel.
Create a controller class that gets informed by the QTreeView if data needs to be displayed in the right panel. The controller will then retrieve the data item and pass it to the right panel in order to be displayed.
The right panel should act like another View class with the sole responsibility of graphically displaying the data passed in from the controller class.
One advantage of doing it this way is that, if there are different categories of data that get displayed differently in the right panel, the controller could examine the selected data item, determine what the category is, create a view widget to put in the right panel, and then pass it the data to display.
This pattern is open-ended as far as extendability is concerned because you do not need to change your data classes if you need a new display, you merely need to create a new right-panel widget, and then teach your controller class how to determine when the new view should be used.
Another advantage of this pattern is that you only ever need to have a single widget created to display data in your right panel. If you change your selected item, you can just pass it to the view class that already exists and get it to refresh its display with the newly selected data. You only need to distroy the right-panel view widget if a different category of data object is selected and its data needs to be drawn in a different way. The controller class can determine whether a right-panel view widget can be re-used or whether it needs to be swapped out for a different widget.

Keping history in Winforms C++

I have an application made in winform using C++ (developed in VS 2010). The GUIs have certain text fields, radio button, check boxes etc. To operate the software one has to fill in these fields/buttons/boxes etc.
There are roughly such 50 different GUIs, having roughly 20 fields in each one.
I want that after the application is closed, and restarted, most recent parameters in this fields automatically fill in, so the user do not need to re-enter all those values again.
What is the easiest and simplest way to achieve this?
This is best solved using MVC pattern where the Model contains the data that was filled. The view contains the way it will be presented(such as in Winforms) And lastly the controller which besides doing business logic will need to do some work (for saving the state).
I prefer using serialization for this. You can serialize the model fields and read them back(fields that are necessary only).
If you don't like serialization you can try writing to INI files that will be easy to modify.
Thirdly you can use a database mdf file to store the state.
But to do all this well you will need to modify the App to use MVC architecture.
You can use the Windows Registry with
WindowsFormName (subkey)
---------> ControlName (subkey)
-----------------> ControlValue (subkey)
When the Form is Closing you save the values for each control in the Registry
When the Form is Loading you read the values
If you are satisfied with a quick and dirty solution you can try this:
Implement a save mechanism:
Iterate to all the form controls (recursively if there are panels or
other containers)
If the control is edit (or other type that holds
data) save the control text together with his name
Store the name,
value pairs (INI file for example)
Implement a load mechanism. This assumes that there are no dependencies between values (If there are automated computed fields do not save them)
Load the values from the storage (if you choose files the file name can be the form name)
iterate to all the form controls and if you find its name in loaded data set the value
Once implemented this approach can be used for all the forms and it will cope with adding/removing controls. However if there are business rules maybe is better to use a MVC approach and serialize the model.

Using Qt Model/View with non-table like data and non-table/list UI?

I've been reading about Qt's Model/View framework. I find it really helpful on working with tabled data like tables from a database. My question is: will it be useful for non-table data like property list or just some bunch of data of various types? If so, how should I approach it?
The goal is to come up with an editor for some property list like data. The list is constructed at runtime and the elements are of various types (numbers, strings, booleans, and file paths, to name a few). Each element is basically a name-value pair. The name, type, and restrains (limits for example) for each element are defined at compile time. They will be assembled at runtime into different lists depending on user input. And the list of elements can change during the edit session.
The UI will most likely be combination of various pre-designed widgets assembled according to user input. They may not be list or table views.
Some pointer to design pattern or examples are also much appreciated. Thanks.
I don't see a problem with MVC framework in QT for doing that.
Basically the difference between a standard table display and this is that you create a list dynamically akin to a map of:
QMap<QString, QVariant> property_map;
You can do a:
QList<std::pair<QString, QVariant>> property_list;
which you can then use to display in a table the property. The best way would probably be:
struct {
QString prop_name;
int prop_type;
QVariant prop_value;
};
QVariant basically will provide you with a single abstraction class for data storage and it is actually what's being returned by the data() function inside the QAbstractItemModel which you might be reimplementing.
So basically you will take a property list and boil it down to the same table like data as the database.
AMENDED
If you have a Widget that you want to have this widget populated with other predefined widgets you are quite likely to have multiple problems unless widgets are of same or well defined size.
What you can do is in you Display widget define a layout like: QGridLayout or other possible layouts and then add your other widgets to it using some set of parameters, which could be done but can be somewhat of a pain.
The other approach that you may take is to place all property widgets up front on the display UI and simply turn the ones you need on and the rest off, but this only applicable if you have a well defined limited number of pre-designed widgets.
I've been using Model/View framework for quite some time now and I usually implement my own models with a backend based on Qt containers (vectors, list, etc). Even if data eventually comes from a database, working with (e.g.) a vector of database ids can dramatically improve performance (and sometimes is the only way you can do).
This trivial example from Qt docs (see "Creating a Custom Model) is the point where I started and shows how to use a QStringList as a backend for a custom model.
Once defined your model you can define your custom Views, which will draw arranged widgets based on the content of the model underneath.
When the model change, your view will change accordingly rearranging widgets when necessary.
Leveraging QVariant capabilities you should be able to render the proper widget for every datatype (e.g. a QSpinBox for a float a QComboBox for a QStringList, and so on...)

How can I visually design a component in C++ Builder?

I have been away from C++ for a couple of years now doing AS3/Flex work. I have gotten used to being able to create a component and place it in design mode with very little fuss and I am struggling to get my head around the C++ Builder way of doing the same thing.
I have written many components for C++ Builder in the past, but none of them have been visual. What I would like to do now is create a component for customer search and another for order processing because I want to be able to create a new instance of these on the fly. What I don't want to do is have to place each of the components like the dbgrid and search fields manually in code. I would like to do this (as well as set their properties) in design mode.
How do I go about this? I have browsed the source for other Delphi components and I notice they have dfm files which seems to be what I need. How do I do this in C++ Builder? The only option I see is to add a new form if I want a dfm, but this isn't what I want as my components will be based on TPanel.
Is there a way to do this or do I have to resort to doing it all in code with no visual reference?
Pursuing the DFM idea I did a test this morning where I created a component based on TPanel and added a new form to it which I create and parent in the constructor of the component. In design mode I set the form border to none and placed a grid on it. This all looks OK until I place the component in my application, at that point it looks like a panel with a standard looking form in it and the grid is missing. If I run the app the panel shows as expected, borderless and with a grid. The DFM appears to be ignored in design mode for some reason.
If you know a better way to do this than using components then please give me some pointers.
Any help and advice will be appreciated beyond words
You might want to have a look at frames (look for "Frame objects"). They are "subforms" you can design visually and then place on forms.
Just as an FYI item, you can also drop the panel on a form, put any other controls on it, position them, set captions, etc..
Now, click the panel to select it, and use Component->Create Component Template from the IDE's main menu. This creates a unit you can install as a component which will add the panel and it's sub-controls (as a single component) to the IDE's component palette.
Of course, you can then modify the source for that new component like any other component source.