-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Graph display #53
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# [RFC Template] Title | ||
|
||
**Authors:** | ||
|
||
- @nickname | ||
- @nickname | ||
|
||
## 1 Executive Summary | ||
|
||
*A short paragraph or bullet list that quickly explains what you're trying to do.* | ||
|
||
## 2 Motivation | ||
|
||
*What motivates this proposal, and why is it important?* | ||
|
||
*Here, we aim to get comfortable articulating the value of our actions.* | ||
|
||
## 3 Proposed Implementation | ||
|
||
*This is the core of your proposal, and its purpose is to help you think through the problem because [writing is thinking](https://medium.learningbyshipping.com/writing-is-thinking-an-annotated-twitter-thread-2a75fe07fade).* | ||
|
||
*Consider:* | ||
|
||
- *using diagrams to help illustrate your ideas.* | ||
- *including code examples if you're proposing an interface or system contract.* | ||
- *linking to project briefs or wireframes that are relevant.* | ||
|
||
## 4 Metrics & Dashboards | ||
|
||
*What are the main metrics we should be measuring? For example, when interacting with an external system, it might be the external system latency. When adding a new table, how fast would it fill up?* | ||
|
||
## 5 Drawbacks | ||
|
||
*Are there any reasons why we should not do this? Here we aim to evaluate risk and check ourselves.* | ||
|
||
## 6 Alternatives | ||
|
||
*What are other ways of achieving the same outcome?* | ||
|
||
## 7 Potential Impact and Dependencies | ||
|
||
*Here, we aim to be mindful of our environment and generate empathy towards others who may be impacted by our decisions.* | ||
|
||
- *What other systems or teams are affected by this proposal?* | ||
- *How could this be exploited by malicious attackers?* | ||
|
||
## 8 Unresolved questions | ||
|
||
*What parts of the proposal are still being defined or not covered by this proposal?* | ||
|
||
## 9 Conclusion | ||
|
||
*Here, we briefly outline why this is the right decision to make at this time and move forward!* | ||
|
||
## 10 RFC Process Guide, remove this section when done | ||
|
||
*By writing an RFC, you're giving insight to your team on the direction you're taking. There may not be a right or better decision in many cases, but we will likely learn from it. By authoring, you're making a decision on where you want us to go and are looking for feedback on this direction from your team members, but ultimately the decision is yours.* | ||
|
||
This document is a: | ||
|
||
- thinking exercise, prototype with words. | ||
- historical record, its value may decrease over time. | ||
- way to broadcast information. | ||
- mechanism to build trust. | ||
- tool to empower. | ||
- communication channel. | ||
|
||
This document is not: | ||
|
||
- a request for permission. | ||
- the most up to date representation of any process or system | ||
|
||
**Checklist:** | ||
|
||
- [ ] Copy template | ||
- [ ] Draft RFC (think of it as a wireframe) | ||
- [ ] Share as WIP with folks you trust to gut-check | ||
- [ ] Send pull request when comfortable | ||
- [ ] Label accordingly | ||
- [ ] Assign reviewers (ask your manager if in doubt) | ||
- [ ] Merge yourself with two approved reviews | ||
|
||
**Recommendations** | ||
|
||
- Tag RFC title with [WIP] if you're still ironing out details. | ||
- Tag RFC title with [newbie] if you're trying out something experimental or you're not entirely convinced of what you're proposing. | ||
- Tag RFC title with [SWARCH] if you'd like to schedule a SWARCH review to discuss the RFC. | ||
- If there are areas that you're not convinced on, tag people who you consider may know about this and ask for their input. | ||
- If you have doubts, ask your manager for help moving something forward. | ||
- As the author/s, this is _your decision_. You are empowered to choose to move forward despite dissenting comments. We're not looking for consensus-driven decision-making. | ||
- The success of the implementation of your proposal depends on how this decision relates to our company's objectives and priorities. |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,241 @@ | ||||||||
# WIP: Graph Display Architecture for QML | ||||||||
|
||||||||
**Authors:** | ||||||||
|
||||||||
- Adam Washington | ||||||||
|
||||||||
## 1 Executive Summary | ||||||||
|
||||||||
This document specifies an architecture for a graph display library | ||||||||
for the Dissolve QML GUI and requests discussion and suggestions for | ||||||||
improvement. | ||||||||
|
||||||||
## 2 Motivation | ||||||||
|
||||||||
Our current system for creating layers and generators has multiple issues with the user experience: | ||||||||
|
||||||||
- Modules are presented in a linear list for what might be a highly | ||||||||
parallel process. | ||||||||
- It is only possible to examine one module at a time. | ||||||||
- Values shared between nodes are contained in a keyword system that | ||||||||
can be both opaque (hard to determine keyword values) and brittle | ||||||||
(long but similar keyword names that must be typed exactly). | ||||||||
- It is difficult for users to create complicated layers without | ||||||||
outside help. | ||||||||
Comment on lines
+23
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
- There is no method of gaining a high level overview of the | ||||||||
activities of a layer. Each module much be read in sequence and a | ||||||||
mental modal of the entire, possibly non-linear, process must be | ||||||||
constructed from this. | ||||||||
|
||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
A graphical display of a connected nodes provides an alternative that | ||||||||
ameliorates most of these issues. | ||||||||
|
||||||||
- All modules can be seen at once with the relations between modules | ||||||||
explicitly declared by the connection. | ||||||||
- All modules and their values are present on the workspace at the same time. | ||||||||
- Values are shared by drawing connections between modules instead of | ||||||||
variables from a global namespace. | ||||||||
- The graphical interface provides an intuitive framework for treating | ||||||||
the modules as individual building blocks. | ||||||||
- One glance at the workflow gives an overview of all of the modules | ||||||||
and their relations | ||||||||
|
||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||
|
||||||||
## 3 Proposed Implementation | ||||||||
|
||||||||
The base of the implementation is an `AbstractGraphModel` class. | ||||||||
|
||||||||
```mermaid | ||||||||
classDiagram | ||||||||
class AbstractGraphModel { | ||||||||
+nodes() : AbstractListModel~NodeInterface~ | ||||||||
+edges() : AbstractListModel~EdgeInterface~ | ||||||||
#createNode(string) | ||||||||
#deleteNode(int) | ||||||||
#createEdge(int, int, int, int) | ||||||||
#deleteEdge(int) | ||||||||
} | ||||||||
<<Abstract>> AbstractGraphModel | ||||||||
class NodeInterface { | ||||||||
+ x : float | ||||||||
+ y : float | ||||||||
+ name: String | ||||||||
+ icon: Icon | ||||||||
+ type: String | ||||||||
} | ||||||||
<<Interface>> NodeInterface | ||||||||
class EdgeInterface { | ||||||||
+ startHandle : Point2D | ||||||||
+ start : Point2D | ||||||||
+ end : Point2D | ||||||||
+ endHandle : Point2D | ||||||||
+ style : int | ||||||||
} | ||||||||
<<Interface>> EdgeInterface | ||||||||
AbstractGraphModel *-- NodeInterface | ||||||||
AbstractGraphModel *-- EdgeInterface | ||||||||
``` | ||||||||
|
||||||||
### 3.1 AbstractGraphModel | ||||||||
|
||||||||
The `AbstractGraphModel` defines the interface to our graph. The | ||||||||
internal representation of the graph is both beyond the scope of this | ||||||||
document[^1] and independent of the behaviour of the library. All | ||||||||
that matters is that the class which represents the graph contains two | ||||||||
Qt properties. The first property is `nodes`, which returns an object | ||||||||
that is a subclass of `AbstractListModel` and each node returned | ||||||||
implements the `NodeInterface` in the Qt properties. The other | ||||||||
property, `edges`, is similar, except that the mandatory interface is | ||||||||
the `EdgeInterface`. | ||||||||
|
||||||||
The `AbstractGraphModel` also requires four slots for accepting | ||||||||
signals from the interface. The `createNode` slot takes a string that | ||||||||
corresponds to the `type` property of the Node Interface and appends a | ||||||||
new Node of the correct type. Correspondingly, `deleteNode` takes the | ||||||||
index of a node and removes that node from the model. In the same | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be wise to continue demanding unique names for nodes / modules, so we don't need to access by |
||||||||
manner, `deleteEdge` takes an edge index and removes the corresponding | ||||||||
edge. Finally, the `createEdge` slot requires four indices, | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, since we will still use named keywords we could use named connections to data instead of indexing which feels more robust. |
||||||||
corresponding to the index of the source node, the index of the output | ||||||||
within that source node, the index of the destination node, and the | ||||||||
index of the input within that destination. | ||||||||
|
||||||||
[^1]: Although the author does have some strong, possibly | ||||||||
contraversial opinions. | ||||||||
|
||||||||
### 3.2 NodeInterface | ||||||||
|
||||||||
Each node must implement five properties. The first two, `x` and `y`, | ||||||||
define the position of the node on the screen. The `name` and `icon` | ||||||||
provide the label and image for the top of the node. Finally, the | ||||||||
`type` describe what kind of information is contained in the node. | ||||||||
The repeater delegate can use a | ||||||||
[DelegateChooser](https://doc.qt.io/qt-6/qml-qt-labs-qmlmodels-delegatechooser.html) | ||||||||
to select the correct QML file to display the node after dispatching | ||||||||
on the `type`. | ||||||||
|
||||||||
### 3.3 EdgeInterface | ||||||||
|
||||||||
Each edge implements five properties to do describe the spline | ||||||||
connecting the nodes. The `start` and `end` properties describe the | ||||||||
exact starting and ending positions of the spline. The `startHandle` | ||||||||
and `endHandle` are the positions of the spline handels for the | ||||||||
corresponding endpoints. Finally, the `style` gives an index of the | ||||||||
type of line being drawn. For example, curves containing `int` values | ||||||||
might have an index of 1 and `string` values an index of 2. This | ||||||||
provides the View with the information to display different edges | ||||||||
while still deferring to the View about the exact display | ||||||||
(e.g. allowing different colours to be chosen in a dark mode). | ||||||||
|
||||||||
### 3.4 Implementation | ||||||||
|
||||||||
In the actual QML, we will create a `GraphNodeView` that takes two | ||||||||
parameters. First first parameter is the `AbstractGraphModel` that | ||||||||
contains the graph information. The second is a delegate that accepts | ||||||||
the `NodeInterface` and returns a group of widgets. This delegate | ||||||||
will *not* be responsible for adding the title bar to the node or | ||||||||
drawing the node border. | ||||||||
|
||||||||
The implementation of this QML will be two `Repeaters`. The first | ||||||||
will iterate over the `nodes`, create the header and border, and use | ||||||||
the provided delegate to populate the nodes. The second will iterate | ||||||||
over the `edges` and draw the splines. | ||||||||
|
||||||||
|
||||||||
## 4 Metrics & Dashboards | ||||||||
|
||||||||
The final implementation will need to provide the following abilities: | ||||||||
|
||||||||
- Add new nodes of any type | ||||||||
- Drag nodes to a new position | ||||||||
- Draw connections between nodes with the mouse | ||||||||
- Delete connections | ||||||||
- Auto-arrange nodes | ||||||||
- Display the nodes at multiple zoom levels | ||||||||
- Scroll around the node environment | ||||||||
|
||||||||
None of these actions should leave the graph in an invalid state. | ||||||||
|
||||||||
## 5 Drawbacks | ||||||||
|
||||||||
- This project provides a significant time sink for at least one developer. | ||||||||
|
||||||||
- Even once the general library is written, more developer time will | ||||||||
be needed to write the Dissolve specific uses of the library | ||||||||
|
||||||||
|
||||||||
## 6 Alternatives | ||||||||
|
||||||||
[Qt Node Editor](https://github.com/paceholder/nodeeditor) does not | ||||||||
natively support QML and thus will require significant work to provide | ||||||||
a consistent experience with the rest of the application. | ||||||||
|
||||||||
[QuickQanava](https://github.com/cneben/QuickQanava?tab=readme-ov-file) | ||||||||
requires the construction and maintenance of an explicit graph object, | ||||||||
as opposed to providing an abstract interface that we can build our | ||||||||
own model again. Additionally, at the time of this writing, | ||||||||
QuickQanava does not build on Mac or Linux | ||||||||
|
||||||||
## 7 Potential Impact and Dependencies | ||||||||
|
||||||||
This could have a significant impact on the structure of the input | ||||||||
file, since it represents a major change in the state of the data. | ||||||||
Furthermore, this may be a significant break change, as it would not | ||||||||
be a trivial matter to convert existing, linear code into the new | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This work would really represent a huge change - Dissolve 2 - and one which would break basically every existing input file. It might be possible to write some kind of importer for 1.X TOML input, but it may be difficult and perhaps not worth the effort. I would be happy to accept the fact that Dissolve 2 is a new starting point for everyone's simulations. One might argue there is little value to the user in importing an old simulation, since they will need to get used to the new workflows we'll be presenting. |
||||||||
graph based setup. | ||||||||
|
||||||||
## 8 Unresolved questions | ||||||||
|
||||||||
### 8.1 Invalid Graphs | ||||||||
|
||||||||
Users might attempt to connect nodes in incompatible way | ||||||||
(e.g. connecting a string to a value expecting an int) or fail to | ||||||||
provide a node with all of the information that it requires. More | ||||||||
generally, the system needs to deal with the possibility of invalid | ||||||||
graphs. | ||||||||
|
||||||||
This first method is to simply forbid invalid graphs. The model | ||||||||
should reject any attempt to create invalid edges or delete nodes that | ||||||||
are required. This had advantages in design and implementation, but | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
places limits on what can provide a good user experience. For | ||||||||
example, every possible node input must have a **valid** default | ||||||||
value, so that the connection to that input can be deleted. | ||||||||
Comment on lines
+199
to
+201
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that strictly disallowing connections between dissimilar input / output types is the best route to go - something like Blender will allow you to connect related type inputs/outputs and still yield sensible results, but I don't think we need that softness here. Regarding the need for default values, this is what we currently have with keywords in modules and nodes, so not a problem moving forward. |
||||||||
|
||||||||
The second method is to allow invalid graphs, but refuse to run them. | ||||||||
This provides more flexibility in the user interface, but also | ||||||||
provides the challenge of informing the user about invalid graphs. A | ||||||||
user might edit a graph, then switch to a different tab, come back ten | ||||||||
hours later and be very confused why their simulation will not run. | ||||||||
This becomes especially perilous in the case of saving files. Should | ||||||||
it possible to save an input file with an invalid graph? If it is, | ||||||||
then the user has been handed a tool to create files that break the | ||||||||
CLI version of Dissolve. If saving invalid graphs is forbidden, then | ||||||||
a user is stuck unable to save hours of work until they have fixed the | ||||||||
problem with their graph. | ||||||||
|
||||||||
### 8.2 Underlying iteration model | ||||||||
|
||||||||
|
||||||||
Do we truly want an `AbstractListModel` for the `edges` and `nodes`? | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably we do, at least based on how I'm understanding your proposal here as it stands. Creating this as a light framework feels like the best option, with the specific functionality we need only. |
||||||||
It would also be possible to use `AbtractTableModel` or | ||||||||
`AbstractItemModel`. I chose `AbstractListModel` since it is the | ||||||||
easier to subclass, but all of these models have an element of | ||||||||
ordering that we currently do not care about. If we do care in the | ||||||||
future, it might be better to have used `AbstractListModel`, but that | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess you meant either |
||||||||
also might be adding a bunch of complication now for functionality | ||||||||
that we will never use. | ||||||||
|
||||||||
### 8.3 Raw spline coordinates | ||||||||
|
||||||||
Should the `EdgeInterface` truly provide raw coordinates? An | ||||||||
alternative implementation would be to simply provide a source and | ||||||||
destination and leave the drawing up to the view. It would also be a | ||||||||
more accurate representation of the data that we have. The | ||||||||
disadvantage is finding a valid representation of the source and | ||||||||
destination that can be passed in the interface. This is especially | ||||||||
relevant since we are not merely connecting nodes together, but | ||||||||
individual components of nodes, which might each have multiple inputs | ||||||||
and outputs. | ||||||||
|
||||||||
## 9 Conclusion | ||||||||
|
||||||||
N/A |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Dashboards"? I think measurable metrics is fine, and all we need to capture the product success / scope.