diff --git a/.gitignore b/.gitignore index 740d7b7a3..1b9386c4d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ export/ # Sphinx documentation docs/html screenshots/ + +# User config (used to mark first-time users as existing) +.app-user-config diff --git a/qe.ipynb b/qe.ipynb index 058fd4e6e..6cd55b78f 100644 --- a/qe.ipynb +++ b/qe.ipynb @@ -32,7 +32,7 @@ "source": [ "from aiidalab_widgets_base.utils.loaders import load_css_stylesheet\n", "\n", - "load_css_stylesheet(package=\"aiidalab_qe.app.static.styles.css\")" + "load_css_stylesheet(package=\"aiidalab_qe.app.static.styles\")" ] }, { @@ -47,22 +47,24 @@ "\n", " sys.modules[\"pybel\"] = __import__(\"openbabel\", globals(), locals(), [\"pybel\"]).pybel\n", "except Exception:\n", - " pass\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import display\n", "\n", - "import urllib.parse as urlparse\n", - "from datetime import datetime\n", + "from aiidalab_qe.app.wrapper import AppWrapperContoller, AppWrapperModel, AppWrapperView\n", "\n", - "import ipywidgets as ipw\n", - "from aiidalab_widgets_base.bug_report import (\n", - " install_create_github_issue_exception_handler,\n", - ")\n", - "from importlib_resources import files\n", - "from IPython.display import display\n", - "from jinja2 import Environment\n", + "model = AppWrapperModel()\n", + "view = AppWrapperView()\n", + "controller = AppWrapperContoller(model, view)\n", "\n", - "from aiidalab_qe.app import App\n", - "from aiidalab_qe.app.static import styles, templates\n", - "from aiidalab_qe.version import __version__" + "display(view)" ] }, { @@ -71,37 +73,39 @@ "metadata": {}, "outputs": [], "source": [ - "env = Environment()\n", + "import urllib.parse as urlparse\n", "\n", - "template = files(templates).joinpath(\"welcome.jinja\").read_text()\n", - "style = files(styles).joinpath(\"style.css\").read_text()\n", - "welcome_message = ipw.HTML(env.from_string(template).render(style=style))\n", - "current_year = datetime.now().year\n", - "footer = ipw.HTML(\n", - " f'
Copyright (c) {current_year} AiiDAlab team Version: {__version__}
'\n", + "from aiidalab_widgets_base.bug_report import (\n", + " install_create_github_issue_exception_handler,\n", ")\n", "\n", - "url = urlparse.urlsplit(jupyter_notebook_url) # noqa F821\n", - "query = urlparse.parse_qs(url.query)\n", - "\n", - "\n", - "app_with_work_chain_selector = App(qe_auto_setup=True)\n", - "# if a pk is provided in the query string, set it as the value of the work_chain_selector\n", - "if 'pk' in query:\n", - " pk = int(query['pk'][0])\n", - " app_with_work_chain_selector.work_chain_selector.value = pk\n", + "from aiidalab_qe.app.main import App\n", "\n", - "output = ipw.Output()\n", "install_create_github_issue_exception_handler(\n", - " output,\n", + " view.output,\n", " url=\"https://github.com/aiidalab/aiidalab-qe/issues/new\",\n", " labels=(\"bug\", \"automated-report\"),\n", ")\n", "\n", - "with output:\n", - " display(welcome_message, app_with_work_chain_selector, footer)\n", + "url = urlparse.urlsplit(jupyter_notebook_url) # noqa F821\n", + "query = urlparse.parse_qs(url.query)\n", + "\n", + "app_with_work_chain_selector = App(qe_auto_setup=True)\n", + "# if a pk is provided in the query string, set it as the value of the work_chain_selector\n", + "if \"pk\" in query:\n", + " pk = int(query[\"pk\"][0])\n", + " app_with_work_chain_selector.work_chain_selector.value = pk\n", "\n", - "display(output)" + "view.main.children = [app_with_work_chain_selector]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "controller.enable_toggles()" ] } ], diff --git a/src/aiidalab_qe/app/__init__.py b/src/aiidalab_qe/app/__init__.py index ea2de96ed..8138fc07a 100644 --- a/src/aiidalab_qe/app/__init__.py +++ b/src/aiidalab_qe/app/__init__.py @@ -1,7 +1 @@ """Package for the AiiDAlab QE app.""" - -from .main import App - -__all__ = [ - "App", -] diff --git a/src/aiidalab_qe/app/static/styles/custom.css b/src/aiidalab_qe/app/static/styles/custom.css index 876d68a98..d27c481e2 100644 --- a/src/aiidalab_qe/app/static/styles/custom.css +++ b/src/aiidalab_qe/app/static/styles/custom.css @@ -1,3 +1,43 @@ .output_subarea { max-width: none !important; } + +.app-header { + margin-bottom: 1em; +} + +.logo { + text-align: center; +} + +#subtitle { + text-align: center; + font-style: italic; +} + +.info-toggles { + margin: 0 auto; +} +.info-toggles button { + width: 100px; + margin: 1em 0.5em; +} +.info-toggles button:focus { + outline: none !important; +} + +.guide ol { + list-style: none; +} +.guide p:not(:last-of-type) { + margin-bottom: 0.5em; +} + +#loading { + text-align: center; + font-size: large; +} + +footer { + text-align: right; +} diff --git a/src/aiidalab_qe/app/static/styles/scss/custom.scss b/src/aiidalab_qe/app/static/styles/scss/custom.scss index 876d68a98..64b306d89 100644 --- a/src/aiidalab_qe/app/static/styles/scss/custom.scss +++ b/src/aiidalab_qe/app/static/styles/scss/custom.scss @@ -1,3 +1,47 @@ .output_subarea { max-width: none !important; } + +.app-header { + margin-bottom: 1em; +} +.logo { + text-align: center; +} + +#subtitle { + text-align: center; + font-style: italic; +} + +.info-toggles { + margin: 0 auto; + + button { + width: 100px; + margin: 1em 0.5em; + + &:focus { + outline: none !important; + } + } +} + +.guide { + ol { + list-style: none; + } + + p:not(:last-of-type) { + margin-bottom: 0.5em; + } +} + +#loading { + text-align: center; + font-size: large; +} + +footer { + text-align: right; +} diff --git a/src/aiidalab_qe/app/static/templates/about.jinja b/src/aiidalab_qe/app/static/templates/about.jinja new file mode 100644 index 000000000..a3b2e8377 --- /dev/null +++ b/src/aiidalab_qe/app/static/templates/about.jinja @@ -0,0 +1,10 @@ ++ The Quantum ESPRESSO app + (or QE app for short) is a graphical front end for calculating materials properties using + Quantum ESPRESSO (QE). Each property is calculated by workflows powered by the + AiiDA engine, and maintained in the + Quantum ESPRESSO plugin + for AiiDA. +
++ The QE app allows you to calculate properties in a simple 4-step process: +
+ ++ New users can go straight to the first step and select their structure. +
+ ++ Completed workflows can be selected at the top of the app. +
+ ++ You can also check out the + basic tutorial to get started + with the Quantum ESPRESSO app, or try out the + advanced tutorial to learn + additional features offered by the app. +
+ ++ For a more in-depth dive into the app's features, please refer to the + how-to guides. +
+ +The QE app allows you to calculate properties in a simple 4-step process:
- -New users can go straight to the first step and select their structure. Once you've already run some calculations, you can select the corresponding workflow using the dropdown below.
-Happy computing! 🎉
-+ For new users, + click on the "Guide" button below for tutorials. +
+ """) + + self.guide_toggle = ipw.ToggleButton( + button_style="", + icon="question", + value=False, + description="Guide", + tooltip="Learn how to use the app", + disabled=True, + ) + + self.about_toggle = ipw.ToggleButton( + button_style="", + icon="info", + value=False, + description="About", + tooltip="Learn about the app", + disabled=True, + ) + + info_toggles = ipw.HBox( + children=[ + self.guide_toggle, + self.about_toggle, + ] + ) + info_toggles.add_class("info-toggles") + + env = Environment() + guide_template = files(templates).joinpath("guide.jinja").read_text() + about_template = files(templates).joinpath("about.jinja").read_text() + + self.guide = ipw.HTML(env.from_string(guide_template).render()) + self.about = ipw.HTML(env.from_string(about_template).render()) + + self.info_container = InfoBox() + + header = ipw.VBox( + children=[ + logo, + subtitle, + self.first_time_user_message, + info_toggles, + self.info_container, + ], + ) + header.add_class("app-header") + + loading = ipw.HTML(""" +