From fea53d0db70e33d17728d6d3895f9ee8b1f6cfe4 Mon Sep 17 00:00:00 2001 From: L-Nafaryus Date: Sat, 12 Feb 2022 13:58:24 +0500 Subject: [PATCH] Mod: refactoring gui --- anisotropy/cli/cli.py | 6 +- anisotropy/gui/__init__.py | 12 ++- anisotropy/gui/app.py | 6 +- anisotropy/gui/layouts/__init__.py | 28 +++++++ anisotropy/gui/layouts/{main.py => base.py} | 30 +++---- anisotropy/gui/layouts/database.py | 47 ++++++----- anisotropy/gui/layouts/runner.py | 43 +++++----- anisotropy/gui/layouts/settings.py | 47 ++++++----- anisotropy/gui/layouts/visualization.py | 93 ++++++++++++--------- 9 files changed, 186 insertions(+), 126 deletions(-) rename anisotropy/gui/layouts/{main.py => base.py} (78%) diff --git a/anisotropy/cli/cli.py b/anisotropy/cli/cli.py index e2342ab..076f464 100644 --- a/anisotropy/cli/cli.py +++ b/anisotropy/cli/cli.py @@ -34,7 +34,7 @@ def init(path, verbose): core_utils.setupLogger(utils.verbose_level(verbose)) logger = logging.getLogger(__name__) - config = config.default_config() + config = core_config.default_config() filepath = os.path.abspath(os.path.join(path, "anisotropy.toml")) logger.info(f"Saving file at { filepath }") @@ -165,7 +165,7 @@ def compute(path, configFile, nprocs, stage, overwrite, params, verbose, executi def gui(path, verbose): import anisotropy from anisotropy.core import utils as core_utils - from anisotropy.gui import app + from anisotropy import gui anisotropy.loadEnv() @@ -178,4 +178,4 @@ def gui(path, verbose): core_utils.setupLogger(utils.verbose_level(verbose)) # logger = logging.getLogger(__name__) - app.run_server(debug = True) + gui.run(debug = True) diff --git a/anisotropy/gui/__init__.py b/anisotropy/gui/__init__.py index a6814f2..eacc36a 100644 --- a/anisotropy/gui/__init__.py +++ b/anisotropy/gui/__init__.py @@ -1,7 +1,15 @@ # -*- coding: utf-8 -*- -from .layouts.main import app +from . import layouts +from .layouts import app + + +app.layout = layouts.base + + +def run(*args, **kwargs): + app.run_server(*args, **kwargs) if __name__ == "__main__": - app.run_server(debug = True) + run(debug = True) diff --git a/anisotropy/gui/app.py b/anisotropy/gui/app.py index dcc1661..06889b6 100644 --- a/anisotropy/gui/app.py +++ b/anisotropy/gui/app.py @@ -4,7 +4,5 @@ import dash import dash_bootstrap_components as dbc app = dash.Dash(__name__, external_stylesheets = [ dbc.themes.LUX ]) -app.title = "anisotropy" -app.config.update( - update_title = None -) +app.title = "Anisotropy" +app.config["update_title"] = None diff --git a/anisotropy/gui/layouts/__init__.py b/anisotropy/gui/layouts/__init__.py index 40a96af..dd73e85 100644 --- a/anisotropy/gui/layouts/__init__.py +++ b/anisotropy/gui/layouts/__init__.py @@ -1 +1,29 @@ # -*- coding: utf-8 -*- + +from . import ( + runner, + settings, + database, + visualization, + about, + base +) +from .base import app + + +runner = runner.layout +settings = settings.layout +database = database.layout +visualization = visualization.layout +about = about.layout +base = base.layout + +__all__ = [ + "runner", + "settings", + "database", + "visualization", + "about", + "base", + "app" +] diff --git a/anisotropy/gui/layouts/main.py b/anisotropy/gui/layouts/base.py similarity index 78% rename from anisotropy/gui/layouts/main.py rename to anisotropy/gui/layouts/base.py index 5e4b381..3a13796 100644 --- a/anisotropy/gui/layouts/main.py +++ b/anisotropy/gui/layouts/base.py @@ -2,7 +2,7 @@ from dash import html from dash import dcc -from dash.dependencies import Input, Output, State +from dash.dependencies import Input, Output import dash_bootstrap_components as dbc from . import ( @@ -13,31 +13,32 @@ from . import ( about ) from ..app import app -from ..styles import * +from .. import styles + import anisotropy ### # Layout ## -app.layout = html.Div([ +layout = html.Div([ # Location dcc.Location(id = "url", refresh = False), # Sidebar html.Div([ # Sidebar - html.H2([html.Img(src = "/assets/simple.png", height = "150px")], style = logo), + html.H2([html.Img(src = "/assets/simple.png", height = "150px")], style = styles.logo), html.Hr(style = { "color": "#ffffff" }), dbc.Nav([ - dbc.NavLink("Runner", href = "/", active = "exact", style = white), - dbc.NavLink("Settings", href = "/settings", active = "exact", style = white), - dbc.NavLink("Database", href = "/database", active = "exact", style = white), - dbc.NavLink("Visualization", href = "/visualization", active = "exact", style = white), - dbc.NavLink("About", href = "/about", active = "exact", style = white), + dbc.NavLink("Runner", href = "/", active = "exact", style = styles.white), + dbc.NavLink("Settings", href = "/settings", active = "exact", style = styles.white), + dbc.NavLink("Database", href = "/database", active = "exact", style = styles.white), + dbc.NavLink("Visualization", href = "/visualization", active = "exact", style = styles.white), + dbc.NavLink("About", href = "/about", active = "exact", style = styles.white), ], vertical = True, pills = True), # Misc - html.Hr(style = white), + html.Hr(style = styles.white), dbc.Container([ dbc.Row([ dbc.Col("v1.2.0"), @@ -48,12 +49,12 @@ app.layout = html.Div([ ) ) ]) - ], style = misc) - ], style = sidebar), + ], style = styles.misc) + ], style = styles.sidebar), # Content - html.Div(id = "page-content", style = page), -], style = content) + html.Div(id = "page-content", style = styles.page), +], style = styles.content) ### @@ -78,4 +79,3 @@ def displayPage(pathname): else: return runner.layout - diff --git a/anisotropy/gui/layouts/database.py b/anisotropy/gui/layouts/database.py index 122a1d0..8371f0d 100644 --- a/anisotropy/gui/layouts/database.py +++ b/anisotropy/gui/layouts/database.py @@ -6,10 +6,11 @@ from dash import dcc import dash_bootstrap_components as dbc from dash.dependencies import Input, Output, State -import os +import pathlib +from os import environ from ..app import app -from ..styles import * +from .. import styles ### @@ -22,9 +23,9 @@ layout = html.Div([ duration = 10000, dismissable = True, is_open = False, - style = message + style = styles.message ), - #dcc.Interval(id = "interval", interval = 1000, n_intervals = 0), + # dcc.Interval(id = "interval", interval = 1000, n_intervals = 0), # Query html.H2("Database"), @@ -32,20 +33,26 @@ layout = html.Div([ html.P("Query"), dcc.Textarea(id = "db_input", style = { "min-width": "100%"}), html.Br(), - dbc.Button("Query", id = "query", style = minWidth), + dbc.Button("Query", id = "query", style = styles.minWidth), # Output html.Hr(), html.P("Output"), - DataTable(id = "db_output", columns = [], data = [], style_table = { "overflow": "scroll"}, style_cell={ - 'textAlign': 'left', - 'width': '150px', - 'minWidth': '180px', - 'maxWidth': '180px', - 'whiteSpace': 'no-wrap', - 'overflow': 'hidden', - 'textOverflow': 'ellipsis', - }), + DataTable( + id = "db_output", + columns = [], + data = [], + style_table = { "overflow": "scroll"}, + style_cell = { + 'textAlign': 'left', + 'width': '150px', + 'minWidth': '180px', + 'maxWidth': '180px', + 'whiteSpace': 'no-wrap', + 'overflow': 'hidden', + 'textOverflow': 'ellipsis', + } + ), ]) @@ -63,17 +70,17 @@ layout = html.Div([ prevent_initial_call = True ) def db_query(clicks, db_input): - from anisotropy.database import Database - from peewee import OperationalError + from anisotropy import database + import peewee as pw - dbpath = os.path.join(os.environ["ANISOTROPY_CWD"], os.environ["ANISOTROPY_DB_FILE"]) - db = Database(path = dbpath) + path = pathlib.Path(environ["AP_CWD"], environ["AP_DB_FILE"]) + db = database.Database(path) try: db.connect() cursor = db.execute_sql(db_input) - except OperationalError as e: + except pw.OperationalError as e: db.close() return None, None, str(e), True, "danger" @@ -91,4 +98,4 @@ def db_query(clicks, db_input): db.close() - return columns, data, "", False, "success" \ No newline at end of file + return columns, data, "", False, "success" diff --git a/anisotropy/gui/layouts/runner.py b/anisotropy/gui/layouts/runner.py index 683d86c..f4613b5 100644 --- a/anisotropy/gui/layouts/runner.py +++ b/anisotropy/gui/layouts/runner.py @@ -6,11 +6,13 @@ from dash import dcc import dash_bootstrap_components as dbc from dash.dependencies import Input, Output, State +import pathlib import os +from os import environ from ..app import app -from ..styles import * -from ..utils import getSize +from .. import styles +from .. import utils ### @@ -23,7 +25,7 @@ layout = html.Div([ duration = 10000, dismissable = True, is_open = False, - style = message + style = styles.message ), dcc.Interval(id = "interval", interval = 1000, n_intervals = 0), @@ -31,22 +33,22 @@ layout = html.Div([ html.H2("Runner"), html.Hr(), html.P("Execution (leave zero for the latest)"), - dcc.Input(id = "execution", type = "number", value = 0, min = 0, style = minWidth), + dcc.Input(id = "execution", type = "number", value = 0, min = 0, style = styles.minWidth), html.Br(), - dbc.Button("Start", id = "start", color = "success", style = minWidth), - dbc.Button("Stop", id = "stop", color = "danger", disabled = True, style = minWidth), + dbc.Button("Start", id = "start", color = "success", style = styles.minWidth), + dbc.Button("Stop", id = "stop", color = "danger", disabled = True, style = styles.minWidth), # Monitor html.H2("Monitor"), html.Hr(), html.P(id = "runner-status"), - DataTable(id = "monitor", columns = [], data = [], style_table = table), + DataTable(id = "monitor", columns = [], data = [], style_table = styles.table), # Log html.H2("Log"), html.Hr(), - dbc.Button("Delete", id = "delete", style = minWidth), - dcc.Textarea(id = "logger", disabled = True, style = bigText) + dbc.Button("Delete", id = "delete", style = styles.minWidth), + dcc.Textarea(id = "logger", disabled = True, style = styles.bigText) ]) @@ -67,10 +69,10 @@ def runnerStart(clicks, execution): "anisotropy", "compute", "-v", - "--path", os.environ["ANISOTROPY_CWD"], - "--conf", os.environ["ANISOTROPY_CONF_FILE"], + "--path", environ["AP_CWD"], + "--conf", environ["AP_CONF_FILE"], "--pid", "anisotropy.pid", - "--logfile", os.environ["ANISOTROPY_LOG_FILE"], + "--logfile", environ["AP_LOG_FILE"], ] if execution > 0: @@ -83,6 +85,7 @@ def runnerStart(clicks, execution): return True + @app.callback( Output("stop", "active"), [ Input("stop", "n_clicks") ], @@ -92,7 +95,7 @@ def runnerStop(clicks): import psutil import signal - pidpath = os.path.join(os.environ["ANISOTROPY_CWD"], "anisotropy.pid") + pidpath = pathlib.Path(environ["AP_CWD"], "anisotropy.pid") try: pid = int(open(pidpath, "r").read()) @@ -107,7 +110,6 @@ def runnerStop(clicks): return True - @app.callback( Output("monitor", "columns"), Output("monitor", "data"), @@ -120,14 +122,14 @@ def runnerStop(clicks): def monitorUpdate(intervals): import psutil - pidpath = os.path.join(os.environ["ANISOTROPY_CWD"], "anisotropy.pid") + pidpath = pathlib.Path(environ["AP_CWD"], "anisotropy.pid") processes = [] try: pid = int(open(pidpath, "r").read()) master = psutil.Process(pid) - except (FileNotFoundError, psutil.NoSuchProcess) as e: + except (FileNotFoundError, psutil.NoSuchProcess): return [], [], "Status: not running", False, True, False else: @@ -137,7 +139,7 @@ def monitorUpdate(intervals): "name": process.name(), "pid": process.pid, "status": process.status(), - "memory": getSize(process.memory_full_info().uss), + "memory": utils.getSize(process.memory_full_info().uss), "threads": process.num_threads(), "created": "{}:{}:{}".format(created.tm_hour, created.tm_min, created.tm_sec) }) @@ -146,12 +148,13 @@ def monitorUpdate(intervals): return columns, processes, "Status: running", True, False, True + @app.callback( Output("logger", "value"), [ Input("interval", "n_intervals") ] ) def logUpdate(intervals): - logpath = os.path.join(os.environ["ANISOTROPY_CWD"], "anisotropy.log") + logpath = pathlib.Path(environ["AP_CWD"], "anisotropy.log") if os.path.exists(logpath): with open(logpath, "r") as io: @@ -169,9 +172,9 @@ def logUpdate(intervals): prevent_initial_call = True ) def logDelete(clicks): - logpath = os.path.join(os.environ["ANISOTROPY_CWD"], "anisotropy.log") + logpath = pathlib.Path(environ["AP_CWD"], "anisotropy.log") if os.path.exists(logpath): os.remove(logpath) - return True \ No newline at end of file + return True diff --git a/anisotropy/gui/layouts/settings.py b/anisotropy/gui/layouts/settings.py index a41f4b8..7bb5a54 100644 --- a/anisotropy/gui/layouts/settings.py +++ b/anisotropy/gui/layouts/settings.py @@ -5,9 +5,11 @@ from dash import dcc import dash_bootstrap_components as dbc from dash.dependencies import Input, Output, State -import os +import pathlib +from os import environ + from ..app import app -from ..styles import * +from .. import styles ### @@ -20,14 +22,14 @@ layout = html.Div([ duration = 10000, dismissable = True, is_open = False, - style = message + style = styles.message ), dbc.Alert( id = "general-status", duration = 10000, dismissable = True, is_open = False, - style = message + style = styles.message ), # General html.H2("General"), @@ -35,25 +37,25 @@ layout = html.Div([ html.P("Path"), dcc.Input(id = "cwd", style = { "min-width": "500px" }), html.Br(), - dbc.Button("Save general", id = "general-save", style = minWidth), + dbc.Button("Save general", id = "general-save", style = styles.minWidth), # Options html.H2("Options"), html.Hr(), html.P("Nprocs"), - dcc.Input(id = "nprocs", type = "number", style = minWidth), + dcc.Input(id = "nprocs", type = "number", style = styles.minWidth), html.P("Stage"), dcc.Dropdown( id = "stage", options = [ { "label": k, "value": k } for k in ["all", "shape", "mesh", "flow", "postProcess"] ], - style = minWidth + style = styles.minWidth ), - dbc.Button("Save", id = "submit", style = minWidth), + dbc.Button("Save", id = "submit", style = styles.minWidth), # Cases html.H2("Cases"), html.Hr(), - dcc.Textarea(id = "cases", style = bigText), + dcc.Textarea(id = "cases", style = styles.bigText), ]) @@ -71,13 +73,12 @@ layout = html.Div([ prevent_initial_call = True ) def generalSave(clicks, cwd): - if not os.path.abspath(cwd): + path = pathlib.Path(cwd) + + if not path.is_absolute(): return "Cwd path must be absolute", True, "danger" - if cwd[-1] == "/": - cwd = cwd[ :-1] - - os.environ["ANISOTROPY_CWD"] = cwd + environ["AP_CWD"] = str(path) return "General settings saved", True, "success" @@ -93,13 +94,13 @@ def settingsLoad(pathname): from anisotropy.core import config as core_config import toml - filepath = os.path.join(os.environ["ANISOTROPY_CWD"], os.environ["ANISOTROPY_CONF_FILE"]) + path = pathlib.Path(environ["AP_CWD"], environ["AP_CONF_FILE"]) config = core_config.default_config() - if os.path.exists(filepath): - config.load(filepath) + if path.exists(): + config.load(path) - return os.environ["ANISOTROPY_CWD"], config["nprocs"], config["stage"], toml.dumps(config.content) + return environ["AP_CWD"], config["nprocs"], config["stage"], toml.dumps(config.content) @app.callback( @@ -118,11 +119,11 @@ def settingsSave(nclick, nprocs, stage, cases): from anisotropy.core import config as core_config import toml - filepath = os.path.join(os.environ["ANISOTROPY_CWD"], os.environ["ANISOTROPY_CONF_FILE"]) + path = pathlib.Path(environ["AP_CWD"], environ["AP_CONF_FILE"]) config = core_config.default_config() - if os.path.exists(filepath): - config.load(filepath) + if path.exists(): + config.load(path) config.update( nprocs = nprocs, @@ -131,10 +132,10 @@ def settingsSave(nclick, nprocs, stage, cases): try: config.content = toml.loads(cases) - config.dump(filepath) + config.dump(path) except Exception as e: return str(e), True, "danger" else: - return f"Saved to { filepath }", True, "success" \ No newline at end of file + return f"Saved to { path }", True, "success" diff --git a/anisotropy/gui/layouts/visualization.py b/anisotropy/gui/layouts/visualization.py index 7e2788b..98f6ba0 100644 --- a/anisotropy/gui/layouts/visualization.py +++ b/anisotropy/gui/layouts/visualization.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from dash.dash_table import DataTable from dash import html from dash import dcc import dash_bootstrap_components as dbc @@ -8,7 +7,9 @@ from dash.dependencies import Input, Output, State import dash_vtk import dash_vtk.utils import vtk -import os + +import pathlib +from os import environ from ..app import app from .. import styles @@ -149,7 +150,10 @@ plotcontrols = html.Div([ html.P("Structure"), dcc.Dropdown( id = "plot-structure", - options = [ { "label": v, "value": v } for v in [ "simple", "bodyCentered", "faceCentered" ] ], + options = [ + { "label": v, "value": v } + for v in [ "simple", "bodyCentered", "faceCentered" ] + ], value = "simple" ), html.Br(), @@ -157,7 +161,10 @@ plotcontrols = html.Div([ html.P("Direction"), dcc.Dropdown( id = "plot-direction", - options = [ { "label": str(v), "value": str(v)} for v in [ [1., 0., 0.], [0., 0., 1.], [1., 1., 1.], "all" ] ], + options = [ + { "label": str(v), "value": str(v)} + for v in [ [1., 0., 0.], [0., 0., 1.], [1., 1., 1.], "all" ] + ], value = str([1., 0., 0.]), ), html.Br(), @@ -185,10 +192,11 @@ layout = html.Div([ width = 8, children = [ html.Div(id = "plot-output", style = { "width": "100%", "min-width": "800px" }) - ] - , style = { "min-width": "800px" }), + ], + style = { "min-width": "800px" } + ), ], style = { "height": "100%"}), - ]), + ]), html.Br(), html.H2("Mesh"), @@ -201,11 +209,14 @@ layout = html.Div([ dbc.Col( width = 8, children = [ - html.Div(id = "vtk-output", style = { "height": "800px", "width": "100%", "min-width": "800px" }) - ] - , style = { "min-width": "800px" }), + html.Div( + id = "vtk-output", + style = { "height": "800px", "width": "100%", "min-width": "800px" } + ) + ], + style = { "min-width": "800px" }), ], style = { "height": "100%"}), - ]) + ]) ]) @@ -220,19 +231,18 @@ layout = html.Div([ ] ) def plotDraw(clicks, execution, structure, direction, data): - from os import path from peewee import JOIN - from anisotropy.database import Database, models + from anisotropy.database import Database, tables import json from pandas import DataFrame import plotly.express as px - dbpath = path.join(os.environ["ANISOTROPY_CWD"], os.environ["ANISOTROPY_DB_FILE"]) + path = pathlib.Path(environ["AP_CWD"], environ["AP_DB_FILE"]) - if not path.isfile(dbpath): + if not path.is_file(): return [ "Database not found" ] - db = Database(path = dbpath) + db = Database(path) if not db.getExecution(execution): return [ "Execution not found" ] @@ -241,33 +251,35 @@ def plotDraw(clicks, execution, structure, direction, data): try: column = getattr(model, data) - except AttributeError as e: + except AttributeError: pass else: break if direction == "all": - select = (models.Shape.alpha, column, models.Shape.direction) + select = (tables.Shape.alpha, column, tables.Shape.direction) else: - select = (models.Shape.alpha, column) + select = (tables.Shape.alpha, column) query = ( - models.Shape + tables.Shape .select(*select) - .join(models.Execution, JOIN.LEFT_OUTER) - .switch(models.Shape) - .join(models.Mesh, JOIN.LEFT_OUTER) - .switch(models.Shape) + .join(tables.Execution, JOIN.LEFT_OUTER) + .switch(tables.Shape) + .join(tables.Mesh, JOIN.LEFT_OUTER) + .switch(tables.Shape) + # .join(tables.FlowOnephase, JOIN.LEFT_OUTER) + # .switch(tables.Shape) .where( - models.Shape.exec_id == execution, - models.Shape.label == structure, + tables.Shape.exec_id == execution, + tables.Shape.label == structure, ) ) if not direction == "all": - query = query.where(models.Shape.direction == json.loads(direction)) + query = query.where(tables.Shape.direction == json.loads(direction)) with db: if query.exists(): @@ -305,8 +317,8 @@ def plotDraw(clicks, execution, structure, direction, data): ) fig.layout.template = "custom_dark" - fig.update_xaxes(showline=True, linewidth=1, linecolor='#4f687d', mirror=True) - fig.update_yaxes(showline=True, linewidth=1, linecolor='#4f687d', mirror=True) + fig.update_xaxes(showline = True, linewidth = 1, linecolor = '#4f687d', mirror = True) + fig.update_yaxes(showline = True, linewidth = 1, linecolor = '#4f687d', mirror = True) plot = dcc.Graph( figure = fig @@ -315,7 +327,6 @@ def plotDraw(clicks, execution, structure, direction, data): return [ plot ] - @app.callback( [ Output("alpha", "min"), Output("alpha", "max") ], [ Input("structure", "value") ] @@ -330,6 +341,7 @@ def alphaLimits(label): elif label == "faceCentered": return 0.01, 0.13 + @app.callback( [ Output("vtk-output", "children") ], [ Input("draw", "n_clicks") ], @@ -351,19 +363,22 @@ def alphaLimits(label): prevent_initial_call = True ) def meshDraw(clicks, execution, structure, direction, alpha, clip, crinkle, wireframe, normal_x, normal_y, normal_z, origin_x, origin_y, origin_z): - from os import path import meshio + + path = pathlib.Path(environ["AP_CWD"], environ["AP_BUILD_DIR"]) + path /= "execution-{}".format(execution) + path /= "{}-{}-{}".format( + structure, + direction.replace(" ", ""), + alpha + ) + basemeshpath = path / "mesh.msh" + meshpath = path / "mesh.vtu" - execution = "execution-{}".format(execution) - case = "{}-{}-{}".format(structure, direction.replace(" ", ""), alpha) - casepath = path.join(os.environ["ANISOTROPY_CWD"], os.environ["ANISOTROPY_BUILD_DIR"], execution, case) - basemeshpath = path.join(casepath, "mesh.msh") - meshpath = path.join(casepath, "mesh.vtu") - - if not path.exists(basemeshpath): + if not basemeshpath.exists(): return [ "Mesh not found" ] - if not path.exists(meshpath) or not path.isfile(meshpath): + if not meshpath.exists() or not meshpath.is_file(): meshold = meshio.read(basemeshpath) meshold.write(meshpath)