Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
50ebeb4
Added a sample model addiotion group
seventil Feb 18, 2026
9988862
Replace text area with table view in SampleModel
AndrewSazonov Feb 18, 2026
6353202
Added a sample model name field in popup
seventil Feb 19, 2026
29774b5
Merge branch '9_sample_model_mock_gui' of github.com:easyscience/shap…
seventil Feb 19, 2026
2a5c8cd
Redefined a samplemodel creation popup
seventil Feb 20, 2026
7d85dd3
added a new button for saving samples
seventil Mar 25, 2026
d4ef5a6
updates to mock group components
seventil Apr 22, 2026
1561733
code cleanup
seventil Apr 22, 2026
86b9018
Implemented save button for components groupbox
seventil Apr 22, 2026
8d1ccf3
Fixes to the load components popup
seventil Apr 22, 2026
87a2651
Basic components and samplemodel code cleanup
seventil Apr 27, 2026
f0dcdd8
Made Layers component group
seventil Apr 29, 2026
e0f0388
Added a label to the fractions listview and made it toggleable based …
seventil Apr 29, 2026
df34aa3
Extracted fractions into a reusable component
seventil Apr 29, 2026
966c021
Added lamellae group
seventil Apr 30, 2026
970e9d5
convert to new EasyApp(lication)
seventil May 1, 2026
d2f8c81
separate columns for fractions groups in lamella
seventil May 1, 2026
23c4765
Transfering the sample model's model to mock qml backend
seventil May 4, 2026
2e0b32e
Added structure definition groups
seventil May 6, 2026
a000a7b
fix button widths in lattice structure definition
seventil May 6, 2026
7613f43
solution mock
seventil May 8, 2026
4727be3
adjusted solvent ions columns
seventil May 20, 2026
20c7ba6
adjusted solvent ions columns further
seventil May 20, 2026
4fffb3d
added advanced controls to sample model and basic analysis tab
seventil May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/easyshapes_app.qmlproject
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import QmlProject 1.1


Project {
mainFile: "easyshapes_app/main.qml"

// List of module and plugin directories passed to QML runtime
importPaths: [
"easyshapes_app",
"../../EasyApp/src", // EasyApp
"../../EasyApp/src", // EasyApplication
]

// Include .qml files from specified directory and its subdirectories
QmlFiles {
directory: "easyshapes_app"
}
QmlFiles {
directory: "../../src/EasyApp"
directory: "../../EasyApp/src/EasyApplication"
}

// Include .js files from specified directory and its subdirectories
JavaScriptFiles {
directory: "easyshapes_app"
}
JavaScriptFiles {
directory: "../../src/EasyApp"
directory: "../../EasyApp/src/EasyApplication"
}

// Include Module Definition Files (qmldir), as well as .ts and .qrc
Expand All @@ -33,9 +34,11 @@ Project {
recursive: true
}
Files {
directory: "../../src/EasyApp"
directory: "../../EasyApp/src/EasyApplication"
filter: "qmldir;*.ts;*.qrc;*.html"
recursive: true
}


mainUiFile: "easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/SampleModel.qml"
}
205 changes: 205 additions & 0 deletions src/easyshapes_app.qrc

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions src/easyshapes_app/Backends/MockBackend.qml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,51 @@ import Backends.MockQml as MockLogic
QtObject {

property var project: MockLogic.Project
property var sampleModel: MockLogic.SampleModel
property var components: MockLogic.Components
// Advanced sidebar (Sample Model page) domain singletons.
property var componentsFiles: MockLogic.ComponentsFiles
property var positionRestraints: MockLogic.PositionRestraints
property var structureFiles: MockLogic.StructureFiles
// Default fractions set tied to the shared component list.
// Used as the fallback for the Fractions sidebar component when no
// per-row override is set.
property var fractions: MockLogic.Fractions { source: MockLogic.Components.loaded }
// Layers owns one Fractions instance per layer (per-row state) and
// shares the same component source so names stay in sync with the
// global components list.
property var layers: MockLogic.Layers
// Lamellae owns distinct inner/outer Fractions instances per lamella.
property var lamellae: MockLogic.Lamellae
// Ring structure parameters (single record, edited inline).
property var ringStructure: MockLogic.RingStructure
// Ball structure parameters (single record, edited inline).
property var ballStructure: MockLogic.BallStructure
// Vesicle structure parameters (single record, edited inline).
property var vesicleStructure: MockLogic.VesicleStructure
// Rod structure parameters (single record, edited inline).
property var rodStructure: MockLogic.RodStructure
// Bilayer structure parameters (single record, edited inline).
property var bilayerStructure: MockLogic.BilayerStructure
// Monolayer structure parameters (single record, edited inline).
property var monolayerStructure: MockLogic.MonolayerStructure
// Lattice structure parameters (single record, edited inline) plus a
// single-record substructure with its own asset library.
property var latticeStructure: MockLogic.LatticeStructure
// Solution group: solution (single-record, TIP3 default) + ions (max 2 rows).
property var solution: MockLogic.Solution
property var ions: MockLogic.Ions
property var analysis: MockLogic.Analysis
// Analysis page — equilibration configuration (config files + force
// field + step range), driving the Equilibrate action.
property var analysisConfig: MockLogic.AnalysisConfig
property var status: MockLogic.Status
property var report: MockLogic.Report

Component.onCompleted: {
layers.fractionsSource = MockLogic.Components.loaded
lamellae.fractionsSource = MockLogic.Components.loaded
positionRestraints.source = MockLogic.Components.loaded
}

}
88 changes: 88 additions & 0 deletions src/easyshapes_app/Backends/MockQml/AnalysisConfig.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: 2024 EasyApp contributors
// SPDX-License-Identifier: BSD-3-Clause
// © 2024 Contributors to the EasyApp project <https://github.com/easyscience/EasyApp>

pragma Singleton

import QtQuick


// Analysis page — equilibration configuration.
//
// Holds:
// * configFiles — Pattern D ListModel of .mdp paths (delete + edit per row).
// * forceFields — JS array of selectable force-field labels (no edits).
// * forceField — currently chosen entry from forceFields.
// * startStep / stopStep — inclusive integer bounds driving which mdp
// files participate in equilibration (defaults 0..6, matching the
// seeded equil0.mdp..equil6.mdp set).
QtObject {

readonly property var configFiles: ListModel {
id: configFilesModel
ListElement { path: 'analysis/equil0.mdp' }
ListElement { path: 'analysis/equil1.mdp' }
ListElement { path: 'analysis/equil2.mdp' }
ListElement { path: 'analysis/equil3.mdp' }
ListElement { path: 'analysis/equil4.mdp' }
ListElement { path: 'analysis/equil5.mdp' }
ListElement { path: 'analysis/equil6.mdp' }
}

readonly property var forceFields: [
'CHARMM36',
'CHARMM27',
'AMBER99SB-ILDN',
'AMBER14SB',
'OPLS-AA/L',
'GROMOS54a7',
'MARTINI 3',
'MARTINI 2.2',
'GAFF'
]

property string forceField: 'CHARMM36'

property int startStep: 0
property int stopStep: 6

function appendFile(item) {
configFilesModel.append({ path: item.path || '' })
}

function appendPath(path) {
configFilesModel.append({ path: path })
}

function removeFile(index) {
if (index < 0 || index >= configFilesModel.count) return
configFilesModel.remove(index)
}

function clearFiles() { configFilesModel.clear() }

// Mock placeholder for opening the file in an editor.
function editFile(index) {
if (index < 0 || index >= configFilesModel.count) return
console.debug('AnalysisConfig.editFile:', configFilesModel.get(index).path)
}

function setForceField(value) {
if (forceField === value) return
forceField = value
}

function setStartStep(value) {
const v = Math.max(0, parseInt(value))
if (isNaN(v) || startStep === v) return
startStep = v
if (stopStep < startStep) stopStep = startStep
}

function setStopStep(value) {
const v = Math.max(0, parseInt(value))
if (isNaN(v) || stopStep === v) return
stopStep = v
if (startStep > stopStep) startStep = stopStep
}
}
14 changes: 14 additions & 0 deletions src/easyshapes_app/Backends/MockQml/BallStructure.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2024 EasyApp contributors
// SPDX-License-Identifier: BSD-3-Clause
// © 2024 Contributors to the EasyApp project <https://github.com/easyscience/EasyApp>

pragma Singleton

import QtQuick


QtObject {
property bool fxz: false
property bool rev: false
property string fill: 'FIBO'
}
14 changes: 14 additions & 0 deletions src/easyshapes_app/Backends/MockQml/BilayerStructure.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2024 EasyApp contributors
// SPDX-License-Identifier: BSD-3-Clause
// © 2024 Contributors to the EasyApp project <https://github.com/easyscience/EasyApp>

pragma Singleton

import QtQuick


QtObject {
property double zsep: 0.0
property int nside: 1
property double dmin: 0.5
}
58 changes: 58 additions & 0 deletions src/easyshapes_app/Backends/MockQml/Components.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: 2024 EasyApp contributors
// SPDX-License-Identifier: BSD-3-Clause
// © 2024 Contributors to the EasyApp project <https://github.com/easyscience/EasyApp>

pragma Singleton

import QtQuick

QtObject {

// Working set of components currently loaded into the sample model.
// ListModel (not JS array) so EaComponents.ListView's ItemSelectionModel
// can drive Ctrl/Shift multi-selection, and so role-based delegate
// properties (`required property string name` etc.) bind correctly.
// Roles: name, component_type, atoms, mint, mext.
readonly property var loaded: ListModel {
id: loadedComponentsModel
}

// File paths staged while creating a new component.
// Roles: path.
readonly property var pendingFilePaths: ListModel {
id: pendingFilePathsModel
}

function appendItem(item) {
loadedComponentsModel.append({
name: item.name,
component_type: item.component_type,
atoms: item.atoms,
mint: item.mint,
mext: item.mext
})
}

function removeItem(index) {
if (index < 0 || index >= loadedComponentsModel.count) return
loadedComponentsModel.remove(index)
}

function clear() {
loadedComponentsModel.clear()
}

function appendPendingFilePath(path) {
pendingFilePathsModel.append({ path: path })
}

function removePendingFilePath(index) {
if (index < 0 || index >= pendingFilePathsModel.count) return
pendingFilePathsModel.remove(index)
}

function clearPendingFilePaths() {
pendingFilePathsModel.clear()
}

}
101 changes: 101 additions & 0 deletions src/easyshapes_app/Backends/MockQml/ComponentsFiles.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: 2024 EasyApp contributors
// SPDX-License-Identifier: BSD-3-Clause
// © 2024 Contributors to the EasyApp project <https://github.com/easyscience/EasyApp>

pragma Singleton

import QtQuick


// Pattern D backend (multi-row list with selection) for the per-component
// file list shown in the Advanced sidebar of the Sample Model page.
//
// The full state is a map of {componentName -> [paths]}. The UI only ever
// sees one component's file list at a time, so we keep that as a single
// ListModel (`files`) and repopulate it whenever the user picks a different
// component through `selectComponent(name)`. Storing the active list as a
// ListModel (rather than a JS array) keeps role-based delegate properties
// and ItemSelectionModel-driven selection working — see QML_MOCKUP_BACKEND
// Pattern D for the rationale.
QtObject {
id: root

// Name of the component whose files are currently being shown.
// Empty string means no selection — `files` is then empty.
property string selectedComponent: ''

// Internal map: componentName -> JS array of file paths. Mutations go
// through __syncBack() so the map stays consistent with the ListModel.
property var filesByComponent: ({
'DPPC': [
'assets/components/DPPC.itp',
'assets/components/DPPC.gro',
'assets/components/DPPC.pdb'
],
'DOPC': [
'assets/components/DOPC.itp',
'assets/components/DOPC.gro'
],
'POPC': [
'assets/components/POPC.itp'
]
})

// Active file list for `selectedComponent`. Roles: path.
readonly property var files: ListModel {
id: filesModel
}

function selectComponent(name) {
if (selectedComponent === name) return
selectedComponent = name
filesModel.clear()
const list = filesByComponent[name] || []
for (let i = 0; i < list.length; ++i) {
filesModel.append({ path: list[i] })
}
}

function appendFile(path) {
if (selectedComponent === '') return
filesModel.append({ path: path })
__syncBack()
}

function removeFile(index) {
if (index < 0 || index >= filesModel.count) return
filesModel.remove(index)
__syncBack()
}

// Export the whole selected component (all its files). Mock backend
// just logs — the real backend will write to disk.
function exportComponent() {
if (selectedComponent === '') return
console.debug('ComponentsFiles.exportComponent:', selectedComponent,
JSON.stringify(filesByComponent[selectedComponent] || []))
}

// Persist the current file list for the selected component. Mock just
// mirrors filesModel back into filesByComponent — already kept in sync,
// so this is a no-op for the mock other than the debug print.
function save() {
if (selectedComponent === '') return
__syncBack()
console.debug('ComponentsFiles.save:', selectedComponent,
JSON.stringify(filesByComponent[selectedComponent]))
}

function __syncBack() {
if (selectedComponent === '') return
const arr = []
for (let i = 0; i < filesModel.count; ++i) {
arr.push(filesModel.get(i).path)
}
// Reassign the map so the `filesByComponent` property emits its
// Changed signal — keeps any future bindings on the map honest.
const copy = Object.assign({}, filesByComponent)
copy[selectedComponent] = arr
filesByComponent = copy
}
}
Loading
Loading