') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Persist and auto-guess layer/field selections across map2loop and LoopStructural setup widgets by Copilot · Pull Request #73 · Loop3D/plugin_loopstructural · GitHub
Skip to content

Persist and auto-guess layer/field selections across map2loop and LoopStructural setup widgets - #73

Merged
lachlangrose merged 9 commits into
dev-0.1.12from
copilot/enhance-layer-selection-storage
Jan 26, 2026
Merged

Persist and auto-guess layer/field selections across map2loop and LoopStructural setup widgets#73
lachlangrose merged 9 commits into
dev-0.1.12from
copilot/enhance-layer-selection-storage

Conversation

CopilotAI commented Jan 26, 2026

Copy link
Copy Markdown
Contributor

Project widgets were not remembering selected layers/fields and qgscombomaplayer/field combos lacked consistent column guessing from project layers.

  • Data persistence

    • Added widget_settings storage to ModellingDataManager (saved/loaded with project) for widget-specific selections.
  • Layer/field auto-guessing & restoration

    • Basal contacts, sorter, sampler, and thickness calculator widgets now use ColumnMatcher to suggest layers/fields and restore previous selections; guards added when layers are missing.
    • Thickness calculator passes required parameters and handles multi-part results, with a simple progress updater.
    • LoopStructural model setup widgets (DEM, Fault Layers, Stratigraphic Layers) now auto-guess layers/fields via ColumnMatcher, persist selections through widget_settings, and restore them on reopen (with missing-layer guards).
  • Example (sampler widget guessing/persisting)

    dtm_names=get_layer_names(self.dtmLayerComboBox)
    matcher=ColumnMatcher(dtm_names)
    dtm_match=matcher.find_match('DTM') ormatcher.find_match('DEM')
    ifdtm_match:
    self.dtmLayerComboBox.setLayer(self.data_manager.find_layer_by_name(dtm_match))
    # Persist current selectionsself.data_manager.set_widget_settings('sampler_widget', {
    'dtm_layer': self.dtmLayerComboBox.currentLayer().name() ifself.dtmLayerComboBox.currentLayer() elseNone,
    ...
    })
Original prompt

For every qgscombomaplayer combo box and field combo box ensure that the column matcher is used to guess the appropriate column from the current layers in the project. Also ensure that any selections of layers are stored in the project data. So that when the widgets are closed and reopened the same layers are selected. This can be managed through the data manager and the selections stored in a json. Feel free to suggest or use a different data structure to achieve the same purpose but ensure that the approach implemented is modular and can be extended.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

CopilotAIand others added 5 commits January 26, 2026 01:41
Co-authored-by: lachlangrose <7371904+lachlangrose@users.noreply.github.com>
Co-authored-by: lachlangrose <7371904+lachlangrose@users.noreply.github.com>
Co-authored-by: lachlangrose <7371904+lachlangrose@users.noreply.github.com>
Co-authored-by: lachlangrose <7371904+lachlangrose@users.noreply.github.com>
Co-authored-by: lachlangrose <7371904+lachlangrose@users.noreply.github.com>
CopilotAI changed the title [WIP] Implement layer selection storage for combo boxesPersist and auto-guess layer/field selections across map2loop widgetsJan 26, 2026
@lachlangrose

Copy link
Copy Markdown
Member

@copilot can you do the same thing with the LoopStructural widget? e.g. all of the model set up

Co-authored-by: lachlangrose <7371904+lachlangrose@users.noreply.github.com>

CopilotAI commented Jan 26, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot can you do the same thing with the LoopStructural widget? e.g. all of the model set up

Added ColumnMatcher-based auto-guessing plus persistence/restore for the LoopStructural setup widgets (DEM, Fault Layers, Stratigraphic Layers). Changes are in 271ca04.

CopilotAI changed the title Persist and auto-guess layer/field selections across map2loop widgetsPersist and auto-guess layer/field selections across map2loop and LoopStructural setup widgetsJan 26, 2026
@lachlangrose
lachlangrose marked this pull request as ready for review January 26, 2026 07:47
@lachlangrose
lachlangrose merged commit 6dec6e2 into dev-0.1.12Jan 26, 2026
1 check passed
@lachlangrose
lachlangrose deleted the copilot/enhance-layer-selection-storage branch January 26, 2026 07:48
@github-actionsgithub-actionsBot mentioned this pull request Jan 31, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@lachlangrose