Skip to content

Commit

Permalink
Merge pull request #3088 from SasView/3077-generate-user-model-docs
Browse files Browse the repository at this point in the history
Fix model documentation generation
  • Loading branch information
krzywon authored Sep 27, 2024
2 parents f7869dd + 4e8be14 commit 3b7abf3
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 103 deletions.
3 changes: 2 additions & 1 deletion src/sas/qtgui/MainWindow/GuiManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ def quitApplication(self):

# save the paths etc.
self.saveCustomConfig()
self.communicate.closeSignal.emit()
reactor.callFromThread(reactor.stop)
return True

Expand Down Expand Up @@ -689,7 +690,7 @@ def addCallbacks(self):
"""
Method defining all signal connections for the gui manager
"""
self.communicate = GuiUtils.Communicate()
self.communicate = GuiUtils.communicate
self.communicate.fileDataReceivedSignal.connect(self.fileWasRead)
self.communicate.statusBarUpdateSignal.connect(self.updateStatusBar)
self.communicate.updatePerspectiveWithDataSignal.connect(self.updatePerspective)
Expand Down
14 changes: 10 additions & 4 deletions src/sas/qtgui/Utilities/DocRegenInProgess.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,22 @@ def __init__(self, parent=None):
super(DocRegenProgress, self).__init__()
self.setupUi(self)
self.parent = parent
if parent and hasattr(parent, 'communicate'):
self.communicate = parent.communicate
else:
from sas.qtgui.Utilities.GuiUtils import communicate
self.communicate = communicate

self.textBrowser.setText("Placeholder Text.")
self.textBrowser.setText("Generating Plugin Documentation...")
self.file_watcher = QtCore.QFileSystemWatcher()

self.addSignals()

def addSignals(self):
"""Adds triggers and signals to the window to ensure proper behavior."""
if self.parent:
self.parent.communicate.documentationRegenInProgressSignal.connect(self.show)
self.parent.communicate.documentationRegeneratedSignal.connect(self.close)
self.communicate.documentationRegenInProgressSignal.connect(self.show)
self.communicate.documentationRegeneratedSignal.connect(self.close)
self.communicate.documentationUpdateLogSignal.connect(self.updateLog)
# Trigger the file watcher when the documentation log changes on disk.
self.file_watcher.addPath(str(DOC_LOG.absolute()))
self.file_watcher.fileChanged.connect(self.updateLog)
Expand Down
60 changes: 38 additions & 22 deletions src/sas/qtgui/Utilities/DocViewWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import time
from pathlib import Path
from typing import Optional

from PySide6 import QtCore, QtWidgets, QtWebEngineCore
from twisted.internet import threads
Expand All @@ -28,7 +29,6 @@ class DocGenThread(CalcThread):
"""Thread performing the fit """

def __init__(self,
target,
completefn=None,
updatefn=None,
yieldtime=0.03,
Expand All @@ -42,19 +42,35 @@ def __init__(self,
self.starttime = time.time()
self.updatefn = updatefn
self.reset_flag = reset_flag
self.target = Path(target)
self.target = None
self.runner = None
self._running = False
from sas.qtgui.Utilities.GuiUtils import communicate
self.communicate = communicate

def compute(self):
def compute(self, target=None):
"""
Regen the docs in a separate thread
"""
self.target = Path(target)
try:
if self.target.exists():
make_documentation(self.target)
else:
return
# Start a try/finally block to ensure that the lock is released if an exception is thrown
if not self.target.exists():
self.runner = make_documentation(self.target)
while self.runner and self.runner.poll() is None:
time.sleep(0.5)
self.communicate.documentationUpdateLogSignal.emit()
except KeyboardInterrupt as msg:
logging.log(0, msg)
finally:
self.close()

def close(self):
# Ensure the runner and locks are fully released when closing the main application
if self.runner:
self.runner.kill()
self.runner = None
self.stop()


class DocViewWindow(QtWidgets.QDialog, Ui_DocViewerWindow):
Expand All @@ -68,19 +84,17 @@ def __init__(self, source: Path = None):
:param parent: Any Qt object with a communicator that can trigger events.
:param source: The Path to the html file.
"""
# Avoid circular imports by importing the communicate class as a class attribute
#from sas.qtgui.Utilities.GuiUtils import Communicate
from sas.qtgui.Utilities.GuiUtils import Communicate
self.communicate = Communicate()

super(DocViewWindow, self).__init__(None)
self.setupUi(self)
self.setWindowTitle("Documentation Viewer")

# Necessary globals
self.source: Path = Path(source)
self.regen_in_progress: bool = False
self.thread: Optional[CalcThread] = None

from sas.qtgui.Utilities.GuiUtils import communicate
self.communicate = communicate
self.initializeSignals() # Connect signals

# Hide editing button for 6.0.0 release
Expand All @@ -93,6 +107,7 @@ def initializeSignals(self):
self.editButton.clicked.connect(self.onEdit)
self.closeButton.clicked.connect(self.onClose)
self.communicate.documentationRegeneratedSignal.connect(self.refresh)
self.communicate.closeSignal.connect(self.onClose)
self.webEngineViewer.urlChanged.connect(self.updateTitle)

def onEdit(self):
Expand Down Expand Up @@ -125,6 +140,8 @@ def onClose(self):
Close window
Keep as a separate method to allow for additional functionality when closing
"""
if self.thread:
self.thread.close()
self.close()

def onShow(self):
Expand Down Expand Up @@ -160,7 +177,7 @@ def regenerateIfNeeded(self):
# Test if this is a user defined model, and if its HTML does not exist or is older than python source file
if os.path.isfile(user_model_name):
if self.newer(user_model_name, url_str):
self.regenerateHtml(user_model_name)
self.regenerateHtml(self.source.name)

# Test to see if HTML does not exist or is older than python file
elif self.newer(self.source, url_str):
Expand Down Expand Up @@ -272,21 +289,20 @@ def regenerateHtml(self, file_name: PATH_LIKE):
d.addCallback(self.docRegenComplete)
self.regen_in_progress = True

@staticmethod
def regenerateDocs(target: PATH_LIKE = None):
def regenerateDocs(self, target: PATH_LIKE = None):
"""Regenerates documentation for a specific file (target) in a subprocess
:param target: A file-path like object that needs regeneration.
"""
thread = DocGenThread(target=target)
thread.queue()
thread.ready(2.5)
while not thread.isrunning():
self.thread = DocGenThread()
self.thread.queue(target=target)
self.thread.ready(2.5)
while not self.thread.isrunning():
time.sleep(0.1)
while thread.isrunning():
while self.thread.isrunning():
time.sleep(0.1)
def docRegenComplete(self, return_val):

def docRegenComplete(self, *args):
"""Tells Qt that regeneration of docs is done and emits signal tied to opening documentation viewer window.
This method is likely called as a thread call back, but no value is used from that callback return.
"""
Expand Down
11 changes: 9 additions & 2 deletions src/sas/qtgui/Utilities/GuiUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ def get_sensible_default_open_directory():
config.DEFAULT_OPEN_FOLDER = get_sensible_default_open_directory()



class Communicate(QtCore.QObject):
"""
Utility class for tracking of the Qt signals
Expand Down Expand Up @@ -179,9 +178,17 @@ class Communicate(QtCore.QObject):
# Update the masked ranges in fitting
updateMaskedDataSignal = QtCore.Signal()

# Triggers refresh of all documentation windows
# Triggers to refresh documentation
documentationRegenInProgressSignal = QtCore.Signal()
documentationRegeneratedSignal = QtCore.Signal()
documentationUpdateLogSignal = QtCore.Signal()

# Global close to help kill active threads
closeSignal = QtCore.Signal()


communicate = Communicate()


def updateModelItemWithPlot(item, update_data, name="", checkbox_state=None):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,9 +306,9 @@ def closeEvent(self, event):

def onHelp(self):
# Use a web link until document viewer is in a good enough state to use
url = "https://www.sasview.org/docs/user/qtgui/Perspectives/Fitting/orientation/orientation.html"

webbrowser.open(url)
from sas.qtgui.MainWindow.GuiManager import GuiManager
url = "/user/qtgui/Perspectives/Fitting/orientation/orientation.html"
GuiManager.showHelp(url)


# Code for handling multiple orientation viewers
Expand Down
Loading

0 comments on commit 3b7abf3

Please sign in to comment.