Store objects to file
-
Hello guys,
I'm new to this forum but I'm working with Qt for 5 years now.
On my actual project, I'm facing a problem.
I'm writing a software able to draw different kind of electrical components, linked to each others.
I decided to create, for each element, a model, a view and a controller.C_Component_A
- *M_Component_A model
- *V_Component_A view
- *C_Component_B controller
When user draw his elements, I want my app to be able to store the drawing (with all elements - MVC) to be stored to a file.
If user Save, he should be able to Open his draw again.But I dont know how can I realize that...
I think QSettings is not designed for that. I noticed QDataStream could do the work but how can I store pointers adresses ?
Each element can be linked to another element.I'd appreciate help on that point.
Thank you !
Cheers,
Jérôme -
@jsavin said in Store objects to file:
I think QSettings is not designed for that. I noticed QDataStream could do the work but how can I store pointers adresses ?
Each element can be linked to another element.QDataStream is the way to go.
You need to implement the stream operators for your custom types.Then you need to write all the contents of your data types to the stream which is needed to be able correctly rearrange it. Inside your custom stream operators you can also stream other types additionally to the stream. This can be done recursively down your custom type hierachy.
Then you will have to do the exact same procedure (keep the order!) for reading from the data stream (which then has a file for input).
So implementing the stream operators is rather easy. The hardest part is what data you need to be able to recreate your drawings.
As you stated you must not store pointer values, but rather all data that defines a type.For example a part of writing a list of elements could be:
- write what type of data will follow now
- write number of elements (n)
- write n elements
- ... proceed to next data of type
For reading then
- you first read in the type of data which is about to be read
- according to the type (what maybe is the list of n elements)
- read element count (n) to an local variable
- read n times the data to the element type
- ...
So the better you optimize your read/write operators and data needed for reconstructing the data-structures the smaller the size of the file of course.
For most Qt types there are streaming operators available. Means you do not need to take care about much, just your custom types. -
@jsavin said in Store objects to file:
how can I store pointers adresses
You can't. The memory addresses aren't valid anymore once the program terminates. You need to come up with something else like IDs or something like that.
I think QSettings is not designed for that. I noticed QDataStream
You're right, QSettings was not designed for that. QDataStream will do but I wouldn't recommend that. Best practice is using JSON or XML.
-
@raven-worx said in Store objects to file:
As you stated you must not store pointer values, but rather all data that defines a type.
Yes, I understand. But....an object can be linked multiples times by others objects
On this picture, there are 8 items (and so 8x3 : 24 objects). Item "Poste_2", is linked 3 times.
I think you gave me the key :@Wieland said in Store objects to file:
You need to come up with something else like IDs or something like that.
I'll create all independents items first, with unique ID. And then I'll create links between items thanks to their IDs.
Thank you guys for your fast answer.
I'll give it a try ! -
@Wieland said in Store objects to file:
QDataStream will do but I wouldn't recommend that. Best practice is using JSON or XML.
Why dont you recommend QDataStream ? What are XML strengths opposed to QDataStream ?
Thank you !
-
@jsavin
Using ID is what i normally do as then order and how many that are linked can be freely changed.
When/after reading in, i use a std::map to look up the actual objects and perform the pointer link.
This works really well with smaller amount of objects and the lookup overhead is negligible.
For ID, i have used guids and sometimes hashing.If you plan on allowing copy & paste from other "Diagrams" plan ahead with the ID to avoid clashes.
-
@jsavin I'd say you may need to base your work on graph theory, this way you can model an electric circuit/diagram as a graph, with your components (resistors, transistors, capacitors, switches and so on) as nodes and electric connections between them as edges. You won't have any problem having a node with more than a connection as you can see in this example.
For a node you can have properties (id, type of component and so on) so now your problem is reduced to save and restore a graph in C++, for which you may pick several graph libraries, for instance Open-GraphTheory which supports saving and reading graphs from files (in GXL format).
Happy coding!
-
@jsavin said in Store objects to file:
Why dont you recommend QDataStream ? What are XML strengths opposed to QDataStream ?
When you write your data with QDataStream you'll end up with files that use a binary format for that only one parser (that one that comes with Qt) exists. So you'll give everyone who wants to process the files with other software than yours a hard time. JSON and XML are plain text and parsers for virtually all programming languages exist.
-
Thank you very much guys !
You helped me a lot, and you're super fast ;-)I decided to use a XML file to store datas. It'll be easier than binary file.
@Pablo-J-Rogina : I took a look at graph theory but it seems to be hard :-oBut this theory helped me building my XML file structure.
Now, I have an other question.
As I said, I created multiple classes, one by item.
With QDataSteam, I could implement operator << and >>. But, with QXmlStreamReader and QXmlStreamWriter how can I ask each class to read and write its own XML datas ?My XML file (tags are french, sorry :o ) :
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <Elements> <Postes> <Poste id="1" nom="Poste_1" nb_barre="1" > <M_Poste> <angle>150</angle> <tension>220</tension> </M_Poste> <V_Poste x="10" y="15" z="0"></V_Poste> <Demibarres> <Demibarre> <V_Demibarre width="105"/> <Connecteurs> <Connecteur id="15" rempli="oui"/> <Connecteur id="16" rempli="non"/> <Connecteur id="17" rempli="oui"/> </Connecteurs> </Demibarre> <Demibarre> <V_Demibarre width="105"/> <Connecteurs> <Connecteur id="18" rempli="oui"/> <Connecteur id="19" rempli="non"/> <Connecteur id="20" rempli="oui"/> </Connecteurs> </Demibarre> </Demibarres> <Disjoncteurs> <Disjoncteur état="ouvert" type="ParDefaut"/> </Disjoncteurs> </Poste> <Poste id="2" nb_barre="2" nom="Poste_2"> <M_Poste> <tension>400</tension> </M_Poste> <V_Poste x="10" y="15" z="0"></V_Poste> <Demibarres> <Demibarre> <V_Demibarre width="105"/> <Connecteurs> <Connecteur id="21" rempli="oui"/> <Connecteur id="22" rempli="non"/> <Connecteur id="23" rempli="oui"/> </Connecteurs> </Demibarre> <Demibarre> <V_Demibarre width="105"/> <Connecteurs> <Connecteur id="24" rempli="oui"/> <Connecteur id="25" rempli="non"/> <Connecteur id="26" rempli="oui"/> </Connecteurs> </Demibarre> <Demibarre> <V_Demibarre width="105"/> <Connecteurs> <Connecteur id="27" rempli="oui"/> <Connecteur id="28" rempli="non"/> <Connecteur id="29" rempli="oui"/> </Connecteurs> </Demibarre> <Demibarre> <V_Demibarre width="105"/> <Connecteurs> <Connecteur id="30" rempli="oui"/> <Connecteur id="31" rempli="non"/> <Connecteur id="32" rempli="oui"/> </Connecteurs> </Demibarre> </Demibarres> <Disjoncteurs> <Disjoncteur état="ouvert" type="ParDefaut"/> </Disjoncteurs> </Poste> </Postes> <Transformateurs> <Transformateur id="12" type="YYn" nom="Transfo_1"> <M_Transformateur> <xcc>10</xcc> <sn>10</sn> <rd_t>10</rd_t> <xd_t>10</xd_t> <xnHT>10</xnHT> <rnHT>10</rnHT> <x01>10</x01> <x02>10</x02> <x03>10</x03> <xnBT>10</xnBT> <rnBT>10</rnBT> <xpn>10</xpn> <rpn>1</rpn> </M_Transformateur> <V_Transformateur x="10" y="15" z="0"/> <Enroulement> <Connecteur id="33" rempli="oui"/> <Disjoncteur état="ouvert" type="ParDefaut"/> </Enroulement> <Enroulement> <Connecteur id="34" rempli="non"/> <Disjoncteur état="ouvert" type="ParDefaut"/> </Enroulement> </Transformateur> </Transformateurs> <Lignes> <Ligne id="15" id_départ="15" id_fin="23" nom="LigneAB"> <num_ligne>0</num_ligne> <M_Ligne> <rd>0</rd> <xd>1</xd> <ro>0</ro> <xo>1</xo> <ir_m>0</ir_m> <ir_v>1</ir_v> <id_m>0</id_m> <id_v>0</id_v> </M_Ligne> <V_Ligne/> <Disjoncteurs> <Disjoncteur état="ouvert" type="ParDefaut"/> <Disjoncteur état="ouvert" type="ParDefaut"/> </Disjoncteurs> <Connecteur id="34" rempli="non"/> </Ligne> <Ligne id="10" id_départ="28" id_fin="16" nom="LigneBC"> <num_ligne>1</num_ligne> <M_Ligne> <rd>0</rd> <xd>1</xd> <ro>0</ro> <xo>1</xo> </M_Ligne> <V_Ligne/> </Ligne> </Lignes> <Liaisons> <Liaison id="154" id_départ="16" id_fin="23"> <M_Liaison> <rd>1</rd> <xd>1</xd> <ro>1</ro> <xo>1</xo> <ir_m>1</ir_m> <ir_v>1</ir_v> <id_m>1</id_m> <id_v>1</id_v> </M_Liaison> <V_Liaison/> </Liaison> </Liaisons> <Réseaux_amont> <Réseau_amont id="12" nom="Reseau Amont 4" id_connecteur="24"> <M_Reseau_amont> <rd>0</rd> <xd>0</xd> <ro>0</ro> <xo>0</xo> <pccTri>0</pccTri> <pccMono>0</pccMono> <iccTri>0</iccTri> <iccMono>0</iccMono> </M_Reseau_amont> <V_Reseau_amont></V_Reseau_amont> <Disjoncteur état="ouvert" type="ParDefaut"/> </Réseau_amont> </Réseaux_amont> <Défauts> <Défaut id="147" id_connecteur="27"> <M_Défaut> <type>mono</type> <position>0.5</position> <rd>0</rd> </M_Défaut> <V_Défaut/> </Défaut> </Défauts> </Elements>
Main items are Poste, Transformateur, Réseaux_amont, Liaison, Lignes, Défaut.
Thank you guys !
-
Hi
you can just use a common base class and have each child
have the same virtual function
virtual bool SaveYourSelf( QXmlStreamWriter & WriteToThis );Then The diagram calls each Objects SaveYourSelf and it will work even for all classes and subclasses.
-
@jsavin said in Store objects to file:
With QDataSteam, I could implement operator << and >>. But, with QXmlStreamReader and QXmlStreamWriter how can I ask each class to read and write its own XML datas ?
actually the same way. Implement the same stream operators just taking the QXmlStream* classes. And use it the same way.
(This a C++/compiler feature and thus Qt-independent) -
@raven-worx @mrjj @Wieland @Pablo-J-Rogina
Thank you guys !
It worked perfectly. I'm now able to read and write a XML file and load items to my drawing.Thank you again
Happy coding !