LogikSim / LogikSimPython

Python prototype of a digital logic simulator
http://www.logiksim.org/
GNU General Public License v3.0
2 stars 1 forks source link

Copy&Past of circuit elements #40

Closed christianbrugger closed 9 years ago

hacst commented 9 years ago

I guess the easiest way to implement this is using the same method we would plan on using to save/load things from files. Meaning getting all the metadata for the elements to copy and using that to instantiate copies.

One complication is that we will have to rewrite the data so it has new IDs and maybe even new positions. Can't think of a better way though.

christianbrugger commented 9 years ago

For that we would need a function to serialize logicitems to metadata. This will also be necessary for the Drag&Drop mechanism. Currently I have in mind:

class SimpleElementGuiItem:
    ...
    def serialize(self):
        return self._cached_metadata
    ...

Would that be sufficient, or would we poll something from the backend?

For the positions I would change them, once the elements are unpacked again in the MouseMode via the normal selection move mechanism. No need to touch serialized metadata.

hacst commented 9 years ago

Depends on how we specify the contract. Ideally you would be able to completely reconstruct item state from only metadata. As in: Hand the complete metadata you got from one item when requesting the creation of another item and you get back exactly that item. For copy and paste there are three complications I see thoug:

While it would be great to be able to do this cleanly in the frontend I favor a "if it has child elements better ask the backend to do this for you" approach. This will also be more stable in terms of future changes to backend logic. Basically you ask the backend to serialize a list of ids (or everything) and get back a special message for that with a metadata tree you can use to recreate it faithfully. Basically I'm imagining the "mount" the full metadata of each child into the parents option as the structure of said tree (a list would be nice but hard to re-instantiate properly). When instantiating you'd ask for a "copy" and hand over the tree resulting in an atomic re-creation with re-written IDs but same metadata apart from that.

Unfortunately this would preclude the current "instantiate in frontend first" approach for such operations.

Thoughts?

christianbrugger commented 9 years ago

I like the idea of completely reconstructing items from metadata.

[bullet point 1] I see no reason why we should be able to create object (QGraphicsItem) with the old id, new object means new id. See 898348ca105d41e1d12d80d2c9cbacbaa37a1e3d, newly constructed items will always get a new id.

[bullet point 3] Furthermore I still think touching metadata is a bad thing and should be kept to a minimum. In an ideal word, objects would handle all of that themselves. For connectivity I think items can figure this out themselves: deleting all connections on creation from the given metadata dict and then asking the scene / their connectors to which other objects their are connected.

[bullet point 2] For compound elements my first idea would be to not copy the whole tree, but just the compound element itself. So a metadata list would be sufficient, instead of a tree. For compound elements (for which it must be possible to create them with a single GUID in the first place), they would simply reconstruct themselves on insertion (probably the backend will create all subitems with correct connectivity and then report them to the scene). One implication of that would be that compound elements loose their state on cut&paste. I am not sure if we want that. Currently I would say, this is not a major limitation, however my feeling says that this needs more thoughts, as this has so many implications. For now I would go for that solution and see where this leads us, because it fits perfectly to the current system and we can change it at any time (and make it more complicated / elaborate).

hacst commented 9 years ago
  1. If you want to preserve relationships you have to remember the old structure and remap it onto the new IDs
  2. Items themselves can't reconstruct their connections on their own because when they are created their connected elements might not yet exist. Also they have no way of knowing what the object they were connected to will be after deserialization (neither the id nor the object is known at this point)
  3. That would require some quite complicated interaction with the component library. Each (truly unique) compound element would have to receive a GUID on creation used to track it which the backend library always knows how to reconstruct its full structure from scratch as it is currently present in the simulation.

1 and 2 simply mean more logic in the frontend. You'd do something close to what I implemented in https://github.com/LogikSim/LogikSim/commit/4e4cfd1ee53cfa9d47498e9ce09b2338a763d321#diff-d13a4903e4a48163df56d5ea4ff1a3d4R143 . You basically remember all connections while re-instantiating but don't try to immediately re-create them. Once you are done instantiating you re-add them with fixed mapping. If we do this in the front-end this won't be atomic though. Simulation time can pass between these actions and other frontends won't be aware this is a "single operation" which might be a problem.

For 3 we would have to do some conceptual changes to the instantiation system which special case the CompoundElement. Basically dynamically create CompoundElement derived types with new GUIDs every time a compound element is instantiated and register it with the library. Then instantiate the new type and make sure the instance id somehow remains in the new type so when we are asked to instantiate it again we can find it in the simulation. When asked to instantiate the derived type you would derive another (if you want a true copy anyways...) and re-create the current structure of the original in it. This kind of blurs the line between types and elements which I don't really like. Makes it hard to figure out what we actually want to save to disk later on. Also what happens if you cut or delete the element? Does the type remain?

Not quite sure how we want to proceed here.