Skip to content
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

What's new dialog #2608

Merged
merged 12 commits into from
Sep 18, 2023
1 change: 1 addition & 0 deletions installers/sasview.spec
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ datas = [
('../src/sas/example_data', 'example_data'),
('../src/sas/qtgui/Utilities/Reports/report_style.css', 'sas/qtgui/Utilities/Reports'),
('../src/sas/qtgui/Perspectives/Fitting/plugin_models', 'plugin_models'),
('../src/sas/qtgui/Utilities/WhatsNew/messages', 'sas/qtgui/Utilities/WhatsNew/messages'),
('../src/sas/system/log.ini', 'sas/system/'),
('../../sasmodels/sasmodels','sasmodels'),
('../docs/sphinx-docs/build/html','doc')
Expand Down
11 changes: 10 additions & 1 deletion src/sas/qtgui/MainWindow/GuiManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
from sas.qtgui.Utilities.AddMultEditor import AddMultEditor
from sas.qtgui.Utilities.ImageViewer import ImageViewer
from sas.qtgui.Utilities.FileConverter import FileConverterWidget
from sas.qtgui.Utilities.WhatsNew.WhatsNew import WhatsNew

import sas
from sas import config
Expand Down Expand Up @@ -134,6 +135,9 @@ def __init__(self, parent=None):
"_downloads",
"Tutorial.pdf"))

if self.WhatsNew.has_new_messages():
self.actionWhatsNew()

def info(self, type, value, tb):
logger.error("".join(traceback.format_exception(type, value, tb)))

Expand Down Expand Up @@ -199,6 +203,7 @@ def addWidgets(self):
self.ResolutionCalculator = ResolutionCalculatorPanel(self)
self.DataOperation = DataOperationUtilityPanel(self)
self.FileConverter = FileConverterWidget(self)
self.WhatsNew = WhatsNew(self)

def loadAllPerspectives(self):
# Close any existing perspectives to prevent multiple open instances
Expand Down Expand Up @@ -619,6 +624,9 @@ def actionWelcome(self):
self._workspace.workspace.addSubWindow(self.welcomePanel)
self.welcomePanel.show()

def actionWhatsNew(self):
self.WhatsNew.show()

def showWelcomeMessage(self):
""" Show the Welcome panel, when required """
# Assure the welcome screen is requested
Expand Down Expand Up @@ -734,7 +742,8 @@ def addTriggers(self):
self._workspace.actionAbout.triggered.connect(self.actionAbout)
self._workspace.actionWelcomeWidget.triggered.connect(self.actionWelcome)
self._workspace.actionCheck_for_update.triggered.connect(self.actionCheck_for_update)

self._workspace.actionWhat_s_New.triggered.connect(self.actionWhatsNew)

self.communicate.sendDataToGridSignal.connect(self.showBatchOutput)
self.communicate.resultPlotUpdateSignal.connect(self.showFitResults)

Expand Down
6 changes: 6 additions & 0 deletions src/sas/qtgui/MainWindow/UI/MainWindowUI.ui
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
<addaction name="actionWelcomeWidget"/>
<addaction name="separator"/>
<addaction name="actionCheck_for_update"/>
<addaction name="actionWhat_s_New"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menuEdit"/>
Expand Down Expand Up @@ -619,6 +620,11 @@
<string>Preferences...</string>
</property>
</action>
<action name="actionWhat_s_New">
<property name="text">
<string>What's New</string>
</property>
</action>
</widget>
<resources/>
<connections/>
Expand Down
5 changes: 5 additions & 0 deletions src/sas/qtgui/MainWindow/UnitTesting/GuiManagerTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@
from sas.qtgui.UnitTesting.TestUtils import QtSignalSpy
from sas.qtgui.Utilities.HidableDialog import HidableDialog

from sas.system import config

class GuiManagerTest:
'''Test the Main Window functionality'''

def __init__(self):
config.override_with_defaults() # Disable saving of test file
config.LAST_WHATS_NEW_HIDDEN_VERSION = "999.999.999" # Give a very large version number

@pytest.fixture(autouse=True)
def manager(self, qapp):
'''Create/Destroy the GUI Manager'''
Expand Down
6 changes: 6 additions & 0 deletions src/sas/qtgui/MainWindow/UnitTesting/MainWindowTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@
from sas.qtgui.Perspectives.Fitting import FittingPerspective
from sas.qtgui.Utilities.HidableDialog import HidableDialog, ShowAgainResult

from sas.system import config
class MainWindowTest:
"""Test the Main Window GUI"""

def __init__(self):
config.override_with_defaults() # Disable saving of test file
config.LAST_WHATS_NEW_HIDDEN_VERSION = "999.999.999" # Give a very large version number

@pytest.fixture(autouse=True)
def widget(self, qapp):
'''Create/Destroy the GUI'''
Expand Down
152 changes: 152 additions & 0 deletions src/sas/qtgui/Utilities/WhatsNew/WhatsNew.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
from collections import defaultdict

from PySide6 import QtWidgets
from PySide6.QtWidgets import QDialog, QWidget, QTextBrowser, QVBoxLayout, QHBoxLayout, QPushButton, QCheckBox

from sas.system.version import __version__ as sasview_version
import importlib.resources as resources

from sas.system import config


from sas.qtgui.Utilities.WhatsNew.newer import strictly_newer_than, reduced_version, newest

def whats_new_messages():
""" Accumulate all files that are newer than the value in the config"""

out = defaultdict(list)
message_dir = resources.files("sas.qtgui.Utilities.WhatsNew.messages")
for message_dir in message_dir.iterdir():
# Get short filename
if message_dir.is_dir():

newer = False

try:
newer = strictly_newer_than(message_dir.name, config.LAST_WHATS_NEW_HIDDEN_VERSION)

except ValueError:
pass

if newer:
for file in message_dir.iterdir():
if file.name.endswith(".html"):
out[message_dir.name].append(file)


return out


class WhatsNew(QDialog):
""" What's New window: displays messages about what is new in this version of SasView

It will find all files in messages.[version] if [version] is newer than the last time
the "don't show me again" option was chosen

To add new messages, just dump a (self-contained) html file into the appropriate folder

"""
def __init__(self, parent=None):
super().__init__()

self.setWindowTitle(f"What's New in SasView {sasview_version}")

self.browser = QTextBrowser()

# Layout stuff
self.mainLayout = QVBoxLayout()
self.buttonBar = QWidget()
self.buttonLayout = QHBoxLayout()


# Buttons
self.buttonBar.setLayout(self.buttonLayout)

self.closeButton = QPushButton("Close")
self.nextButton = QPushButton("Next")

self.showAgain = QCheckBox("Show on Startup")
self.showAgain.setChecked(True)

self.buttonLayout.addWidget(self.showAgain)
self.buttonLayout.addWidget(self.closeButton)
self.buttonLayout.addWidget(self.nextButton)

# Viewer
self.setLayout(self.mainLayout)
self.mainLayout.addWidget(self.browser)
self.mainLayout.addWidget(self.buttonBar)

# Callbacks
self.closeButton.clicked.connect(self.close_me)
self.nextButton.clicked.connect(self.next_file)

# # Gather new files
new_messages = whats_new_messages()
new_message_directories = [key for key in new_messages.keys()]
new_message_directories.sort(key=reduced_version)

self.all_messages = []

for version in new_messages:
self.all_messages += new_messages[version]

self.max_index = len(self.all_messages)
self.current_index = 0

self.show_file()

self.setModal(True)

def next_file(self):
self.current_index += 1
self.current_index %= self.max_index
self.show_file()

def show_file(self):
if len(self.all_messages) > 0:
filename = self.all_messages[self.current_index]
with open(filename, 'r') as fid:
data = fid.read()
self.browser.setText(data)
else:
self.browser.setText("<html><body><h1>You should not see this!!!</h1></body></html>")

def close_me(self):
if not self.showAgain.isChecked():
# We choose the newest, for backwards compatability, i.e. we never reduce the last version
config.LAST_WHATS_NEW_HIDDEN_VERSION = newest(sasview_version, config.LAST_WHATS_NEW_HIDDEN_VERSION)

self.close()

def has_new_messages(self) -> bool:
""" Should the window be shown? """
return bool(self.all_messages)



def maybe_show_whats_new():
global whats_new_window
""" Show the What's New dialogue if it is wanted """

if whats_new_messages():
whats_new_window = WhatsNew()
whats_new_window.show()


def main():
""" Demo/testing window"""

from sas.qtgui.convertUI import main

main()

app = QtWidgets.QApplication([])

maybe_show_whats_new()

app.exec_()


if __name__ == "__main__":
main()
Empty file.
11 changes: 11 additions & 0 deletions src/sas/qtgui/Utilities/WhatsNew/messages/6.0.0/1.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<html>
<head/>
<body>

<h1>
Some Handy Tips
</h1>

Have you tried randomly pressing buttons?
</body>
</html>
10 changes: 10 additions & 0 deletions src/sas/qtgui/Utilities/WhatsNew/messages/6.0.0/2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<html>
<head/>
<body>
<h1>
Welcome to SasView - What's New
</h1>

Lot's of things
</body>
</html>
Empty file.
43 changes: 43 additions & 0 deletions src/sas/qtgui/Utilities/WhatsNew/newer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import Tuple
import re

def reduced_version(version_string: str) -> Tuple[int, int, int]:
""" Convert a version string into the three numbers we care about for the purposes
of the WhatsNew dialog (i.e. strip a,b suffixes etc, make into three ints"""

version_string = re.sub(r"[^\.0-9]+.*", "", version_string)

parts = version_string.split(".")

if len(parts) > 3:
raise ValueError(f"{version_string} not a valid version string")


parts = [int(part) for part in parts]

return tuple(parts + [0]*(3-len(parts)))


def strictly_newer_than(version_a: str, version_b: str) -> bool:
""" Is the version string "version_a" string strictly newer than "version_b" """

numeric_a = reduced_version(version_a)
numeric_b = reduced_version(version_b)

for i in range(3):
if numeric_a[i] > numeric_b[i]:
return True
elif numeric_a[i] < numeric_b[i]:
return False

return False

def newest(version_a: str, version_b: str) -> str:
"""Return the newest of two versions by the comparison used in the what's new box,
if they are equally new, return the first one.
"""

if strictly_newer_than(version_b, version_a):
return version_b

return version_a
3 changes: 3 additions & 0 deletions src/sas/system/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ def __init__(self):
# Default fitting optimizer
self.FITTING_DEFAULT_OPTIMIZER = 'lm'

# What's New variables
self.LAST_WHATS_NEW_HIDDEN_VERSION = "5.0.0"

#
# Lock the class down, this is necessary both for
# securing the class, and for setting up reading/writing files
Expand Down