mirror of https://gitlab.com/pamhyr/pamhyr2
1422 lines
49 KiB
Org Mode
1422 lines
49 KiB
Org Mode
# documentation.org -- Pamhyr developers documentation
|
|
# Copyright (C) 2023-2024 INRAE
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#+STARTUP: indent
|
|
|
|
#+INCLUDE: ../tools/macro.org
|
|
#+INCLUDE: ../tools/latex.org
|
|
|
|
#+TITLE: Developers documentation
|
|
#+SUBTITLE: Version: {{{version}}}
|
|
#+AUTHOR: {{{INRAE}}}
|
|
|
|
#+OPTIONS: toc:t
|
|
#+LANGUAGE: UKenglish
|
|
|
|
#+BEGIN_abstract
|
|
This document is for the use of developers. It describes the project
|
|
architecture, the tools available to assist development and
|
|
debugging. It also describes the procedures for creating packages and
|
|
the configurations required to set up the gitlab runners. Finally,
|
|
this document explains how documentation is written and modified, and
|
|
how to contribute to the project by modifying, improving or adding
|
|
documentation, translations or code.
|
|
#+END_abstract
|
|
|
|
* Introduction
|
|
|
|
{{{pamhyr2}}} is free and open source software (FOSS) graphical user
|
|
interface (GUI) for 1D hydro-sedimentary modelling of rivers developed
|
|
in Python (with version 3.8). It use PyQt at version 5 and matplotlib
|
|
in version 3.4.1 or later for the user insterface (see
|
|
{{{file(/requirements.txt)}}} for details). The architecture of
|
|
project code follow the Qt Model/View architecture [fn:qt-arch] (see
|
|
details in section [[Architecture]]). {{{pamhyr2}}} packages can be build
|
|
manually (see section [[Building packages]]), but there are automatically
|
|
build with the gitlab-ci (see the section [[Setup the CI
|
|
environment]]). Documentation files are written with org-mode[fn:org],
|
|
let see section [[Documentation files]]. Finally, to see the contribution
|
|
rules, see the section [[How to contribute?]].
|
|
|
|
[fn:qt-arch] Qt Model/View documentation:
|
|
https://doc.qt.io/qt-5/model-view-programming.html (last access
|
|
2023-09-15)
|
|
[fn:org] The org-mode website: https://orgmode.org/ (last access
|
|
2023-09-15)
|
|
|
|
* Architecture
|
|
|
|
{{{pamhyr2}}}'s architecture is based on Qt Model/View, see Figure
|
|
[[graph-architecture]]. It is made up of several different components: the
|
|
model (in blue), the graphical components (in red), the
|
|
actions/delegates (in green), the commands (in purple), the solvers
|
|
(in yellow) and the save file (in grey).
|
|
|
|
The model is a set of python classes and can be exported to a single
|
|
SQLite3 format backup file. The view can be made up of various
|
|
components, generally a Qt window with other view components, such as:
|
|
a table, a text box, a button, a plot, and so on. The user can view
|
|
the data using the view and interact with certain components. These
|
|
components are linked to an action (such as a Python function) or to a
|
|
delegate class. These actions or delegate can create a command (based
|
|
on Qt UndoCommand class), this command must implement two functions:
|
|
One to modify the model, one to reverte this modification and reset
|
|
the model to previous state. All model modification must be perform by
|
|
a command to be cancelled. The user can also run a solver and add some
|
|
simulation results to model data.
|
|
|
|
#+name: graph-architecture
|
|
#+header: :results drawer
|
|
#+header: :exports results
|
|
#+header: :post attr_wrap(width="12cm", data=*this*, name="graph-architecture", caption="Pamhyr2 Model/View architecture scheme (inspired by Qt Model/View architecture [[https://doc.qt.io/qt-5/model-view-programming.html]])", float="t")
|
|
#+begin_src dot :file "images/auto_graph-architecture.png" :cache no
|
|
digraph {
|
|
bgcolor="transparent";
|
|
node[colorscheme=set19,shape=box,style="filled",fillcolor=white];
|
|
edge[colorscheme=set19,color=0];
|
|
|
|
root[style=invis];
|
|
|
|
subgraph cluster0 {
|
|
label="File System"
|
|
style=dashed;
|
|
|
|
save[label="Pamhyr save",fillcolor="9",shape=note];
|
|
sbin[label="Solver binary",fillcolor="9",shape=note];
|
|
configfile[label="Pamhyr configuration file",fillcolor="9",shape=note];
|
|
}
|
|
|
|
user[label="User",shape=ellipse];
|
|
|
|
subgraph cluster1 {
|
|
label="Pamhyr2";
|
|
|
|
config[label="Configuration",fillcolor="5"];
|
|
model[label="Model",fillcolor="2"];
|
|
view[label="View",fillcolor="1"];
|
|
delegate[label="Delegate",fillcolor="3"];
|
|
action[label="Action",fillcolor="3"];
|
|
solver[label="Solver",fillcolor="6"];
|
|
undocommand[label="Command",fillcolor="4"];
|
|
}
|
|
|
|
root -> model[style=invis];
|
|
root -> config[style=invis];
|
|
|
|
model -> save[dir=both,label="Save/Load"];
|
|
config -> configfile[dir=both,label="Save/Load"];
|
|
|
|
undocommand -> solver[style=invis];
|
|
action -> solver[style=invis];
|
|
delegate -> solver[style=invis];
|
|
|
|
model -> view[label="Rendering"];
|
|
view -> delegate[label="Rendering"];
|
|
delegate -> undocommand[label="Create"];
|
|
action -> undocommand[label="Create/use"];
|
|
action -> solver[label="Run"];
|
|
solver -> model[dir=both,label="Export/Results",labelangle=0,labelfloat=true,constraint=true];
|
|
solver -> sbin[dir=both,label="Execute/Results"];
|
|
undocommand -> model[label="Modify"];
|
|
|
|
view -> user[label="Vizualize"];
|
|
user -> delegate[label="Modify"];
|
|
user -> action[label="Triggere"];
|
|
|
|
config -> solver[label="Create",style=dashed,labelfloat=true,constraint=true];
|
|
//model -> config[style=invis];
|
|
config -> model[style=invis];
|
|
delegate -> action[style=invis];
|
|
save -> configfile[style=invis];
|
|
root -> solver[style=invis];
|
|
user -> solver[style=invis];
|
|
}
|
|
#+end_src
|
|
|
|
All the model source code are in the directory {{{file(src/Model)}}}
|
|
(let see section [[Model]] for more details), the View components,
|
|
delegate and command are in {{{file(src/View)}}} (see section [[View]]). Solvers classes are
|
|
in {{{file(src/Solver)}}} (see section [[Solver]]).
|
|
|
|
The following sub section show examples of main {{{pamhyr}}} internal
|
|
class for view componants, but this documentation is not exhaustive,
|
|
be free to watch existing code for more details and examples. In,
|
|
addition some features are not factorise and must be implemented from
|
|
scratch (directly with Qt for example).
|
|
|
|
[fn:qt-mv] The Qt Model/View documentation web page:
|
|
https://doc.qt.io/qt-5/model-view-programming.html
|
|
|
|
** Model
|
|
|
|
The model is a set of Python classes. In {{{pamhyr2}}}, this classes must
|
|
respect some constraint. Each model class must inherits
|
|
=Model.Tools.SQLSubModel= abstract class, except the =Model.Study=
|
|
class who inherits =Model.Tools.SQLModel= (see [[SQL]]).
|
|
|
|
The model entry point is the Study class. It contains infomation about
|
|
the study: their name, description, time system, and so on. Their
|
|
contains a River object too. This river object inherits the network
|
|
graph and contains a list of =RiverNode= and a list of =RiverReach=
|
|
(an edge who contains a source node, and destination node).
|
|
=RiverReach= contrains geometry, so, the river network (node and edge)
|
|
associated with the geometry forms the basis of the model, and the
|
|
other components are linked to one of these basic components.
|
|
|
|
#+name: graph-model
|
|
#+header: :results drawer
|
|
#+header: :exports results
|
|
#+header: :post attr_wrap(width="16cm", data=*this*, name="graph-model", caption="Pamhyr2 model class dependencies (A -> B means A can contain references to B)", float="t")
|
|
#+begin_src dot :file "images/auto_graph-model.png" :cache no
|
|
digraph {
|
|
bgcolor="transparent";
|
|
node[colorscheme=set19,shape=box,style="filled",fillcolor="2"];
|
|
|
|
//subgraph cluster0 {
|
|
// style=dashed;
|
|
study[label="Study"];
|
|
river[label="River"];
|
|
|
|
subgraph cluster00 {
|
|
style=solid;
|
|
label="Network"
|
|
rnode[label="RiverNode"];
|
|
redge[label="RiverReach"];
|
|
}
|
|
|
|
subgraph cluster06 {
|
|
style=solid;
|
|
label="Greometry"
|
|
georeach[label="Reach"];
|
|
geocrosssection[label="Cross-section"];
|
|
geopoint[label="Point"];
|
|
}
|
|
//}
|
|
|
|
//subgraph cluster1 {
|
|
// style=dashed;
|
|
frictionlist[label="FrictionList"];
|
|
|
|
subgraph cluster01 {
|
|
style=solid;
|
|
label="Stricklers";
|
|
stricklers[label="Stricklers"];
|
|
stricklerslist[label="StricklersList"];
|
|
}
|
|
subgraph cluster02 {
|
|
style=solid;
|
|
label="BoundaryCondition";
|
|
boundaryconditionlist[label="BoundaryConditionList"];
|
|
boundarycondition[label="BoundaryCondition"];
|
|
}
|
|
subgraph cluster03 {
|
|
style=solid;
|
|
label="LateralContribution";
|
|
lateralcontributionlist[label="LateralContributionList"];
|
|
lateralcontribution[label="LateralContribution"];
|
|
}
|
|
subgraph cluster04 {
|
|
style=solid;
|
|
label="InitialConditions";
|
|
initialconditionsdict[label="InitialConditionsDict"];
|
|
initialconditions[label="InitialConditions"];
|
|
}
|
|
|
|
solverparameterslist[label="SolverParametersList"];
|
|
|
|
subgraph cluster05 {
|
|
style=solid;
|
|
label="Sediment";
|
|
sedimentlayerlist[label="SedimentLayerList"];
|
|
sedimentlayer[label="SedimentLayer"];
|
|
layer[label="Layer"];
|
|
}
|
|
//}
|
|
|
|
subgraph cluster2 {
|
|
style=dashed;
|
|
label="Results"
|
|
results[label="Results"]
|
|
rriver[label="River"];
|
|
rreach[label="Reach"];
|
|
rcrosssection[label="Cross-section"];
|
|
}
|
|
|
|
study -> river;
|
|
river -> rnode;
|
|
river -> redge;
|
|
redge -> rnode;
|
|
river -> boundaryconditionlist -> boundarycondition -> rnode;
|
|
river -> lateralcontributionlist -> lateralcontribution -> redge;
|
|
river -> initialconditionsdict -> initialconditions;
|
|
initialconditions -> redge;
|
|
river -> stricklerslist -> stricklers;
|
|
river -> solverparameterslist;
|
|
river -> sedimentlayerlist -> sedimentlayer -> layer;
|
|
redge -> frictionlist -> stricklers;
|
|
redge -> georeach -> geocrosssection -> geopoint;
|
|
geocrosssection -> sedimentlayer;
|
|
geopoint -> sedimentlayer;
|
|
|
|
results -> study;
|
|
results -> rriver;
|
|
rriver -> river;
|
|
rriver -> rreach;
|
|
rreach -> georeach;
|
|
rreach -> rcrosssection;
|
|
rcrosssection -> geocrosssection;
|
|
|
|
// river -> boundaryconditionlist -> boundarycondition -> results[style=invis];
|
|
// river -> lateralcontributionlist -> lateralcontribution -> results[style=invis];
|
|
// river -> initialconditionsdict -> initialconditions -> results[style=invis];
|
|
// initialconditions -> results[style=invis];
|
|
// river -> stricklerslist -> stricklers -> results[style=invis];
|
|
// river -> solverparameterslist -> results[style=invis];
|
|
// river -> sedimentlayerlist -> sedimentlayer -> layer -> results[style=invis];
|
|
|
|
geopoint -> boundaryconditionlist[style=invis];
|
|
geopoint -> lateralcontributionlist[style=invis];
|
|
geopoint -> initialconditionsdict[style=invis];
|
|
geopoint -> initialconditions[style=invis];
|
|
geopoint -> stricklerslist[style=invis];
|
|
geopoint -> solverparameterslist[style=invis];
|
|
geopoint -> sedimentlayerlist[style=invis];
|
|
}
|
|
#+end_src
|
|
|
|
*** SQL
|
|
|
|
The model must be export to a database file to create a study save
|
|
file. This file use SQLite3[fn:sqlite] format and the extention
|
|
=.pamhyr=. So, each model componante must be register into this study
|
|
file. To create, update, set and get information into SQLite database
|
|
we use SQL command. The database use version number and some
|
|
modification could be perform to update database. For each model
|
|
componante, correspond one or more SQL table to store information. To
|
|
normalize the interaction with database we made two classes, SQLModel
|
|
and SQLSubModel. The Study class use SQLModel because is the top of
|
|
the model hierachy. The rest of model class inherits to SQLSubModel.
|
|
|
|
A class who inherits SQLSubModel, must implement some methods:
|
|
- =_sql_create=: Class method to create the database scheme
|
|
- =_sql_update=: Class method to update the database scheme if necessary
|
|
- =_sql_load=: Class method to load data from DB
|
|
- =_sql_save=: Method to save current object into DB
|
|
|
|
Class method take in arguments: The class (=cls=), a function to
|
|
execute SQL command into the database (=execute=). In addition, the
|
|
update method take the previous version of database, load method take
|
|
an optional arguments =data= if additional infomation ar needed, and
|
|
who can contains whatever you want. The method save take in arguments
|
|
the current object (=self=), a function to execute SQL command into
|
|
the database (=execute=), and optional data (=data=).
|
|
|
|
The class who inherits SQLSubModel can also define an class attribute
|
|
=_sub_classes= to set a formal class dependencies into database. This
|
|
attribute is use at database creation to create all table, and at
|
|
update to update all the database table. Let see examples of
|
|
SQLSubModel usage for two classes Foo and Bar with Foo contains list
|
|
of Bar (Listing [[sql-bar]] and [[sql-foo]]).
|
|
|
|
#+NAME: sql-bar
|
|
#+CAPTION: Exemple of class Bar inherits SQLSubModel.
|
|
#+begin_src python :python python3 :results output :noweb yes
|
|
from Model.Tools.PamhyrDB import SQLSubModel
|
|
|
|
class Bar(SQLSubModel):
|
|
_id_cnt = 0
|
|
def __init__(self, id = -1, x = 0, y = 0):
|
|
self._x = x
|
|
self._y = y
|
|
if id == -1:
|
|
self.id = Bar._id_cnt + 1
|
|
else:
|
|
self.id = id
|
|
Bar._id_cnt = max(id, Bar._id_cnt+1)
|
|
|
|
@classmethod
|
|
def _sql_create(cls, execute):
|
|
execute("""
|
|
CREATE TABLE bar (
|
|
id INTEGER NOT NULL PRIMARY KEY,
|
|
x INTEGER NOT NULL,
|
|
y INTEGER NOT NULL,
|
|
foo_id INTEGER NOT NULL,
|
|
FOREIGN KEY(foo_id) REFERENCES foo(id),
|
|
)""")
|
|
return True
|
|
|
|
@classmethod
|
|
def _sql_update(cls, execute, version):
|
|
# If version is lesser than 0.0.2, add column to bar table
|
|
major, minor, release = version.strip().split(".")
|
|
if major == minor == "0":
|
|
if int(release) < 2:
|
|
execute("ALTER TABLE bar ADD COLUMN y INTEGER")
|
|
return True
|
|
|
|
@classmethod
|
|
def _sql_load(cls, execute, data = None):
|
|
new = []
|
|
table = execute(
|
|
f"SELECT id, x, y FROM bar WHERE foo_id = {data['id']}"
|
|
)
|
|
for row in table:
|
|
bar = cls(
|
|
id = row[0], x = row[1], y = row[2],
|
|
)
|
|
new.append(bar)
|
|
return new
|
|
|
|
def _sql_save(self, execute, data = None):
|
|
execute("INSERT INTO bar (id,x,y,foo_id) VALUES " +
|
|
f"({self.id}, {self._x}, {self._y}, {data['id']})")
|
|
#+end_src
|
|
|
|
#+NAME: sql-foo
|
|
#+CAPTION: Exemple of class Foo inherits SQLSubModel and contains a list of Bar object (Listing [[sql-bar]]).
|
|
#+begin_src python :python python3 :results output :noweb yes
|
|
class Foo(SQLSubModel):
|
|
_id_cnt = 0
|
|
_sub_classes = [Bar]
|
|
|
|
def __init__(self, id = -1, name = ""):
|
|
self._name = name
|
|
self._bar = []
|
|
# ...
|
|
|
|
@classmethod
|
|
def _sql_create(cls, execute):
|
|
execute("""
|
|
CREATE TABLE foo (
|
|
id INTEGER NOT NULL PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
)
|
|
""")
|
|
return cls._create_submodel(execute)
|
|
|
|
@classmethod
|
|
def _sql_update(cls, excute, version):
|
|
return cls._update_submodel(execute, version)
|
|
|
|
@classmethod
|
|
def _sql_load(cls, execute, data = None):
|
|
new = []
|
|
table = execute(
|
|
"SELECT id, name FROM foo"
|
|
)
|
|
for row in table:
|
|
foo = cls(
|
|
id = row[0],
|
|
name = row[1],
|
|
)
|
|
data = {
|
|
"id": row[0], # Current Foo ID
|
|
}
|
|
foo._bar = Bar._sql_load(execute, data=data)
|
|
new.append(foo)
|
|
return new
|
|
|
|
def _sql_save(self, execute, data = None):
|
|
execute(f"DELETE FROM foo WHERE id = {self.id}")
|
|
execute(f"DELETE FROM bar WHERE foo_id = {self.id}")
|
|
# Save new data
|
|
execute(f"INSERT INTO bar (id,name) VALUES ({self.id}, {self._name})")
|
|
data = {"id": self.id}
|
|
for bar in self._bar:
|
|
bar._sql_save(execute, data=data)
|
|
#+end_src
|
|
|
|
Let see the results database scheme for {{{pamhyr2}}} at version v0.0.7 in
|
|
Figure [[sql_schema]].
|
|
|
|
#+NAME: sql_schema
|
|
#+ATTR_LATEX: :width 16cm
|
|
#+CAPTION: SQLite database scheme at {{{pamhyr2}}} version v0.0.7 (generate with [[https://gitlab.com/Screwtapello/sqlite-schema-diagram]])
|
|
[[./images/schema_v0.0.7.png]]
|
|
|
|
|
|
[fn:sqlite] The SQLite web site: https://www.sqlite.org/index.html
|
|
(last access 2023-09-20)
|
|
|
|
*** List class
|
|
|
|
A abstract class PamhyrModelList is available and provide some of
|
|
basic methods for object list in Model. This abstract class implement
|
|
some classic action in View like: insert new object, delete object,
|
|
sort, move object up, move object down, and so on. An variant exists
|
|
for multiple list with same type of object, each sublist is called
|
|
tab, because in View, this kind of list si prensented in different
|
|
table PamhyrModelListWithTab.
|
|
|
|
*** Dict class
|
|
|
|
A abstract class PamhyrModelDict is available and provide some of
|
|
basic methods for object dictionary in Model. This class is like
|
|
PamhyrModelList but use a dictionary instead of list.
|
|
|
|
** View
|
|
|
|
{{{pamhyr2}}} use Qt as graphical user interface library with the
|
|
application "Qt designer" for windows or widget creation (see [[UI file]])
|
|
and "Qt linguist" for interface translate (see [[Translate]]). In
|
|
addition, we use matplotlib as ploting library (see [[Plot]]).
|
|
|
|
Typically, each model componant have an associated window in
|
|
application to add, delete or edit this componant. At top level of
|
|
View directory we found the =MainWindow.py= file and some
|
|
sub-directories. A view sub-directory contains: A =Window.py= file, a
|
|
=Table.py= file with table model definition if nessessary, one or more
|
|
=Plot*.py= file with plot class definition, a =translate.py= file with
|
|
componant translate, and possible other files or sub-directories.
|
|
|
|
*** UI file
|
|
|
|
We define as possible all {{{pamhyr2}}} windows and custom widgets
|
|
with "Qt designer". This application generate UI file who describes
|
|
interface organisation with table, layout, button, etc. This method is
|
|
faster than hand made windows and widget creation, and saves us some
|
|
purely descriptive code. The UI files are saved into =src/View/ui= for
|
|
window, and =/src/View/ui/Widgets= for custom widget.
|
|
|
|
*** Translate
|
|
|
|
#+NAME: pamhyr-trad
|
|
#+CAPTION: Example of =PamhyrTranslate= class implementation with a global traduction for /FooBar/ and a additional dictionary =table_headers=
|
|
#+begin_src python :python python3 :results output :noweb yes
|
|
from PyQt5.QtCore import QCoreApplication
|
|
from View.Tools.PamhyrTranslate import PamhyrTranslate
|
|
_translate = QCoreApplication.translate
|
|
|
|
class MyTranslate(PamhyrTranslate):
|
|
def __init__(self):
|
|
super(MyTranslate, self).__init__()
|
|
|
|
# Add traduction to global dictionary
|
|
self._dict["My"] = _translate("My", "FooBar")
|
|
# Add an additional translate dictionary
|
|
self._sub_dict["table_headers"] = {
|
|
"foo": _translate("My", "Foo"),
|
|
"bar": _translate("My", "Bar"),
|
|
"baz": _translate("My", "Baz"),
|
|
}
|
|
#+end_src
|
|
|
|
*** Window
|
|
|
|
The abstract class =PamhyrWindow= and =PamhyrDialog= are used for most
|
|
of {{{pamhyr2}}} window. These class allow to create an window for
|
|
{{{pamhyr2}}} GUI and implemente some useful methods. The super class
|
|
method difine some generic value from optional parameters, for
|
|
examples:
|
|
- =self._study=: The study giving in constructor parameters =study=
|
|
(typically a =Model.Study= class object)
|
|
- =self._config=: The configuration giving in constructor parameters =config= (typically a =Config= class object)
|
|
- =self._trad=: The traductor dictionary giving in constructor
|
|
parameters =trad= (typically a =Model.Tools.PamhyrTranslate= class
|
|
object)
|
|
|
|
#+NAME: window
|
|
#+CAPTION: Example of {{{pamhyr2}}} window
|
|
#+begin_src python :python python3 :results output :noweb yes
|
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
|
from View.My.Translate import MyTranslate
|
|
from View.My.Table import MyTableModel
|
|
|
|
class MyWindow(PamhyrWindow):
|
|
_pamhyr_ui = "MyUI"
|
|
_pamhyr_name = "My window"
|
|
|
|
def __init__(self, study=None, config=None,
|
|
my_data=None,
|
|
parent=None):
|
|
self._my_data = my_data
|
|
|
|
super(MyWindow, self).__init__(
|
|
# Window title
|
|
title = self._pamhyr_name + " - " + study.name,
|
|
# Window standard data
|
|
study = study, config = config,
|
|
trad = MyTranslate(),
|
|
parent = parent,
|
|
# Activate undo/redo and copy/paste shortcut
|
|
options = ["undo", "copy"]
|
|
)
|
|
|
|
# Add custom data to hash window computation
|
|
self._hash_data.append(self._my_data)
|
|
|
|
# Setup custom window components
|
|
self.setup_table()
|
|
self.setup_connections()
|
|
|
|
def setup_table(self):
|
|
# Init table(s)...
|
|
|
|
def setup_connections(self):
|
|
# Init action connection(s)...
|
|
|
|
# ...
|
|
#+end_src
|
|
|
|
Typically we called method =setup_*=, the method to initialize some
|
|
window componants or connections.
|
|
|
|
*** Table
|
|
|
|
An abstract class =PamhyrTableModel= is available to define a simple
|
|
QAbstractTableModel shortly. In simple cases, there are only =data=
|
|
and =setData= methode to implement, but the constructor needs more
|
|
information than a classic QAbstractTableModel class.
|
|
|
|
#+NAME: table-src
|
|
#+CAPTION: Definition of a table model from =PamhyrTableModel= in a file =View/My/Table.py=.
|
|
#+begin_src python :python python3 :results output :noweb yes
|
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
|
|
|
class MyTableModel(PamhyrTableModel):
|
|
def data(self, index, role):
|
|
# Retrun data at INDEX...
|
|
|
|
@pyqtSlot()
|
|
def setData(self, index, value, role=Qt.EditRole):
|
|
# Set VALUE at INDEX...
|
|
#+end_src
|
|
|
|
#+CAPTION: Using the table model defined in Listing [[table-src]] in window funtion =setup_table= defined Listing [[window]].
|
|
#+begin_src python :python python3 :results output :noweb yes
|
|
# Table model creation (Window.py: setup_table)
|
|
table_headers = self._trad.get_dict("table_headers")
|
|
self._model = MyTableModel(
|
|
table_view = table, # The table view object
|
|
table_headers = table_headers, # The table column headers dict
|
|
# (with traduction)
|
|
editable_headers = ["foo", "bar"], # List of editable column name
|
|
delegates = {
|
|
"bar": self.my_delegate, # Custom delegate for column 'bar'
|
|
},
|
|
data = self._my_lst, # The data
|
|
undo = self._undo_stack, # The window undo command stack
|
|
)
|
|
#+end_src
|
|
|
|
*** UndoCommand
|
|
|
|
All model modification must be done by an QUndoCommand, this command
|
|
allow to undo and redo an action. This a Qt class wi can inherit to
|
|
define custom undo command (see example Listing [[undo-cmd]])
|
|
|
|
#+NAME: undo-cmd
|
|
#+CAPTION: Example of custom UndoCommand, this command allow to add a node to graph in river network window (method redo), and delete it to graph with undo method
|
|
#+begin_src python :python python3 :results output :noweb yes
|
|
class AddNodeCommand(QUndoCommand):
|
|
def __init__(self, graph, node):
|
|
QUndoCommand.__init__(self)
|
|
|
|
self._graph = graph
|
|
self._node = node
|
|
|
|
def undo(self):
|
|
self._graph.remove_node(self._node)
|
|
|
|
def redo(self):
|
|
self._graph.insert_node(self._node)
|
|
#+end_src
|
|
|
|
All undo command must be push into a =QUndoStack= (see Listing
|
|
[[undo-cmd-push]]) to perform the action and allow user undo and redo this
|
|
action. In =PamhyrWindow= (and =PamhyrDialog=) the undo stack is
|
|
automatically create if the option ="undo"= is activate at window
|
|
creation, this stack is accessible at =self._undo_stack=.
|
|
|
|
#+NAME: undo-cmd-push
|
|
#+CAPTION: Example of UndoCommand push into an undo stack.
|
|
#+begin_src python :python python3 :results output :noweb yes
|
|
self._undo_stack.push(
|
|
AddNodeCommand(
|
|
self._graph,
|
|
node
|
|
)
|
|
)
|
|
#+end_src
|
|
|
|
*** Plot
|
|
|
|
To define a new plot you can create a class who inherit to
|
|
=PamhyrPlot=. The creator need at leaste five argument:
|
|
- A =canvas= of type =MplCanvas=
|
|
- A (optional) =trad= of type =PamhyrTranslate=
|
|
- A =data= used in =draw= and =update= to create and update the plot
|
|
- A optional =toolbar= of type =PamhyrToolbar=
|
|
- A =parent= window
|
|
This class must implement two method =draw= and =update=, the first
|
|
method to draw the plot from scratch, the second to update the plot if
|
|
data has changed.
|
|
|
|
#+begin_src python :python python3 :results output :noweb yes
|
|
from View.Tools.PamhyrPlot import PamhyrPlot
|
|
|
|
class MyPlot(PamhyrPlot):
|
|
def __init__(self, canvas=None, trad=None, toolbar=None
|
|
data=None, parent=None):
|
|
super(MyPlot, self).__init__(
|
|
canvas=canvas,
|
|
trad=trad,
|
|
data=data,
|
|
toolbar=toolbar,
|
|
parent=parent
|
|
)
|
|
|
|
self.label_x = self._trad["x"]
|
|
self.label_y = self._trad["y"]
|
|
|
|
# Optional configuration
|
|
self._isometric_axis = False
|
|
|
|
self._auto_relim_update = True
|
|
self._autoscale_update = True
|
|
|
|
def draw(self):
|
|
# Draw function code...
|
|
|
|
def update(self):
|
|
# Update function code...
|
|
|
|
def clear(self):
|
|
# Clear plot values...
|
|
|
|
# ...
|
|
#+end_src
|
|
|
|
** Solver
|
|
|
|
The {{{pamhyr2}}} architecture allow to define multiple solver. A
|
|
solver is define by a:
|
|
- type
|
|
- name
|
|
- description,
|
|
- path
|
|
- command line pattern
|
|
- (optional) input formater path
|
|
- (optional) input formater command line
|
|
- (optional) output formater path
|
|
- (optional) output formater command line
|
|
|
|
Let see Figure [[graph-multi-solver]], the application can implement
|
|
different solver type, this solver type implement the code for export
|
|
study to solver input format, and read the solver output to study
|
|
results. There exists a generic solver with a generic input and output
|
|
format, the type could be use to use a solver not implemented in
|
|
{{{pamhyr2}}}, but this solver must can read/write input and output
|
|
generic format or use external script. There is possible to define
|
|
different solver with the same type, for example two differents
|
|
version of the same solver. Finaly, with input and output formater is
|
|
possible to execute a code on distant computer, for example, over ssh.
|
|
|
|
#+name: graph-multi-solver
|
|
#+header: :results drawer
|
|
#+header: :exports results
|
|
#+header: :post attr_wrap(width="12cm", data=*this*, name="graph-multi-solver", caption="Scheme of multiple solver configured, one Rubarbe solver and two Mage solver with one on local machine and one on a distant machine accessed over ssh", float="t")
|
|
#+begin_src dot :file "images/auto_graph-multi-solver.png" :cache no
|
|
digraph {
|
|
bgcolor="transparent";
|
|
node[colorscheme=set19,shape=box,style="filled",fillcolor=9];
|
|
edge[colorscheme=set19,color=0];
|
|
|
|
subgraph cluster00 {
|
|
label="User personal computer";
|
|
style=solid;
|
|
|
|
subgraph cluster0 {
|
|
label="Pamhyr2";
|
|
style=solid;
|
|
|
|
// subgraph cluster01 {
|
|
// label="Core";
|
|
//model[label="Model", fillcolor=2];
|
|
//view[label="View", fillcolor=1];
|
|
config[label="Configuration", fillcolor=5];
|
|
|
|
// view -> model -> view;
|
|
// }
|
|
|
|
subgraph cluster02 {
|
|
label="Solver";
|
|
style=dashed;
|
|
subgraph cluster021 {
|
|
label="Solver Classes";
|
|
//classSolverM7[label="Mage7", fillcolor=6];
|
|
classSolverM8[label="Mage8", fillcolor=6];
|
|
classSolverR[label="RubarBE", fillcolor=6];
|
|
}
|
|
//classSolverX[label="Solver X Binding", fillcolor=6];
|
|
|
|
subgraph cluster022 {
|
|
label="Solver Object";
|
|
solverM[label="Mage", fillcolor=6];
|
|
solverM2[label="Mage over ssh", fillcolor=6];
|
|
solverR[label="RubarBE", fillcolor=6];
|
|
//solverX[label="Solver X", fillcolor=6];
|
|
}
|
|
|
|
classSolverM8 -> solverM [style=dashed];
|
|
classSolverM8 -> solverM2[style=dashed];
|
|
classSolverR -> solverR[style=dashed];
|
|
//classSolverX -> solverX[style=dashed];
|
|
}
|
|
|
|
//config -> solverM[style="dotted"];
|
|
//config -> solverR[style="dotted"];
|
|
//config -> solverX[style="dotted"];
|
|
|
|
//model -> solverM;
|
|
//model -> solverM2;
|
|
//model -> solverR;
|
|
//model -> solverX;
|
|
}
|
|
|
|
subgraph cluster1 {
|
|
label="File System";
|
|
style=dashed;
|
|
|
|
mage[label="Mage Binary",shape=note];
|
|
//X[label="Solver X Binary"];
|
|
rubarbe[label="RubarBE Binary",shape=note];
|
|
ssh[label="ssh",shape=note];
|
|
}
|
|
}
|
|
|
|
//config -> X[style=invis];
|
|
//model -> config[style=invis];
|
|
config -> solverM[label="",constraint=true];
|
|
//config -> solverX[label="",constraint=true];
|
|
config -> solverR[label="",constraint=true];
|
|
config -> solverM2[label="",constraint=true];
|
|
|
|
subgraph cluster2 {
|
|
label="Distant server";
|
|
style=solid;
|
|
|
|
sshd[label="sshd"];
|
|
|
|
subgraph cluster21 {
|
|
label="File System";
|
|
style=dashed;
|
|
|
|
mage2[label="Mage Binary",shape=note];
|
|
}
|
|
}
|
|
|
|
solverM -> mage[label="", color=1];
|
|
mage -> solverM[label="", color=2];
|
|
|
|
//solverX -> X[label="", color=1];
|
|
//X -> solverX[label="", color=2];
|
|
|
|
solverR -> rubarbe[label="", color=1];
|
|
rubarbe -> solverR[label="", color=2];
|
|
|
|
solverM2 -> ssh -> sshd -> mage2[label="", color=1];
|
|
mage2 -> sshd -> ssh -> solverM2[label="", color=2];
|
|
}
|
|
#+end_src
|
|
|
|
Let see Figure [[graph-pipeline]] the temporal order of action to run a
|
|
solver and get results:
|
|
- (1) Write solver input file(s) using the study data
|
|
- (2) Run the solver
|
|
- (2.1) The solver read the input file(s)
|
|
- (2.2) The solver compute results and write it to solver output
|
|
file(s)
|
|
- (3) {{{pamhyr2}}} create a =Results= object
|
|
- (3.1) The {{{pamhyr2}}} solver class read solver output file(s) and
|
|
complete Results with readed data
|
|
|
|
#+name: graph-pipeline
|
|
#+header: :results drawer
|
|
#+header: :exports results
|
|
#+header: :post attr_wrap(width="10cm", data=*this*, name="graph-pipeline", caption="Pamhyr2 solver execution pipeline architecture scheme", float="t")
|
|
#+begin_src dot :file "images/auto_graph-pipeline.png" :cache no
|
|
digraph {
|
|
bgcolor="transparent";
|
|
node[colorscheme=set19,shape=box,style="filled",fillcolor=9];
|
|
edge[colorscheme=set19,color=0];
|
|
|
|
subgraph cluster0 {
|
|
label="Pamhyr2"
|
|
config[label="Configuration",fillcolor=5];
|
|
model[label="Model",fillcolor=2];
|
|
obj[label="Solver",fillcolor=6];
|
|
results[label="Results",fillcolor=2];
|
|
view[label="View",fillcolor=1];
|
|
|
|
results -> model[style="dashed"];
|
|
results -> obj[style="dashed"];
|
|
}
|
|
|
|
config -> obj[label=""];
|
|
obj -> model[style="dashed"];
|
|
|
|
subgraph cluster1{
|
|
label="File System";
|
|
style=dashed;
|
|
in[label="Solver input file(s)",shape=note,fillcolor=white];
|
|
out[label="Solver output file(s)",shape=note,fillcolor=white];
|
|
bin[label="Solver binary",shape=note];
|
|
}
|
|
|
|
obj -> in[label="Write (1)",color=1];
|
|
obj -> bin[label="Execute (2)",color=1];
|
|
bin -> in[label="Read (2.1)",color=1];
|
|
bin -> out[label="Write (2.2)",color=2];
|
|
obj -> results[label="Create (3)",color=2];
|
|
obj -> out[label="Read (3.1)", color=2];
|
|
view -> model[style="dashed"];
|
|
view -> results[style="dashed"];
|
|
}
|
|
#+end_src
|
|
|
|
In case of generic solver (or a solver with input and output formater)
|
|
the temporal order of action is prensented in Figure
|
|
[[graph-pipeline-generic]].
|
|
|
|
#+name: graph-pipeline-generic
|
|
#+header: :results drawer
|
|
#+header: :exports results
|
|
#+header: :post attr_wrap(width="12cm", data=*this*, name="graph-pipeline-generic", caption="Pamhyr2 generic solver execution pipeline architecture scheme", float="t")
|
|
#+begin_src dot :file "images/auto_graph-pipeline-generic.png" :cache no
|
|
digraph {
|
|
bgcolor="transparent";
|
|
node[colorscheme=set19,shape=box,style="filled",fillcolor=9];
|
|
edge[colorscheme=set19,color=0];
|
|
|
|
subgraph cluster0 {
|
|
label="Pamhyr2"
|
|
config[label="Configuration",fillcolor=5];
|
|
model[label="Model",fillcolor=2];
|
|
obj[label="Generic solver",fillcolor=6];
|
|
results[label="Results",fillcolor=2];
|
|
view[label="View",fillcolor=1];
|
|
|
|
results -> model[style="dashed"];
|
|
results -> obj[style="dashed"];
|
|
}
|
|
|
|
config -> obj[label=""];
|
|
obj -> model[style="dashed"];
|
|
|
|
subgraph cluster1{
|
|
label="File System";
|
|
style=dashed;
|
|
|
|
gin[label="Generic input file", shape=note,fillcolor=white];
|
|
ibin[label="Input formater", shape=note];
|
|
in[label="Solver input file(s)",shape=note,fillcolor=white];
|
|
out[label="Solver output file(s)",shape=note,fillcolor=white];
|
|
gout[label="Generic results file",shape=note,fillcolor=white];
|
|
obin[label="Output formater", shape=note];
|
|
bin[label="Solver binary",shape=note];
|
|
}
|
|
|
|
gin -> ibin[style=invis];
|
|
ibin -> bin -> obin[style=invis];
|
|
in -> bin[style=invis];
|
|
obin -> gout[style=invis];
|
|
|
|
// Input format
|
|
obj -> gin[label="Write (1)",color=1];
|
|
obj -> ibin[label="Execute (2)",color=1,style=dashed];
|
|
ibin -> gin[label="Read (2.1)",color=1];
|
|
ibin -> in[label="Write (2.2)",color=1];
|
|
|
|
// Solve
|
|
obj -> bin[label="Execute (3)",color=1,style=dashed];
|
|
bin -> in[label="Read (3.1)",color=1];
|
|
bin -> out[label="Write (3.2)",color=2];
|
|
|
|
// Output format
|
|
obj -> obin[label="Execute (4)",color=2,style=dashed];
|
|
obin -> out[label="Read (4.1)",color=2];
|
|
obin -> gout[label="Write (4.2)",color=2];
|
|
|
|
// Read results
|
|
obj -> results[label="Create (5)",color=2];
|
|
obj -> gout[label="Read (5.1)", color=2];
|
|
view -> model[style="dashed"];
|
|
view -> results[style="dashed"];
|
|
}
|
|
#+end_src
|
|
|
|
To implement a Solver in {{{pamhyr2}}}, there exists a abstract class
|
|
=Solver.AbstractSolver=. A class who herits this class, must implement
|
|
different methods:
|
|
- =export=: Export the study to solver input file(s)
|
|
- =input_param=: Return the solver input parameter(s) as string
|
|
- =log_file=: Return the solver log file name as string
|
|
- =results=: Read the solver output file(s) and return a =Model.Results= object.
|
|
|
|
** Unit tests
|
|
|
|
A very small part of {{{pamhyr2}}} has unit test. This part is limited to the Model.
|
|
|
|
#+begin_src shell
|
|
python3 -m venv test
|
|
. test test/bin/activate
|
|
pip3 install -U -r ./full-requirements.txt
|
|
|
|
cd src/
|
|
python3 -Walways -m unittest discovert -v -t .
|
|
#+end_src
|
|
|
|
** The debug mode
|
|
|
|
To activate an deactivate the {{{pamhyr2}}} debug mode you can open
|
|
the configuration window and type "Ctrl+G" or run {{{pamhyr2}}} with
|
|
command line:
|
|
#+begin_src shell
|
|
./Pamhyr2 debug
|
|
#+end_src
|
|
|
|
This mode add some log and add two action in main window menu: "About
|
|
> Debug" open a window with Python Repl in current Python
|
|
environement, and "About > Debug SQLite" who open the application
|
|
SQLiteBrowser (if installed) on current Study to explore the study
|
|
data base file.
|
|
|
|
#+NAME: debug-repl
|
|
#+ATTR_LATEX: :width 14cm
|
|
#+CAPTION: {{{pamhyr2}}} debug Python REPL
|
|
[[./images/python-debug-repl.png]]
|
|
|
|
* Build the project
|
|
|
|
The project uses gitlab-ci runners to build packages, but it is possible
|
|
to build packages manually.
|
|
|
|
** Building packages
|
|
|
|
If you need an hand made package, you can script available in
|
|
{{{file(packages)}}} directory.
|
|
|
|
*** GNU/Linux {{{linux}}}
|
|
|
|
On GNU/Linux building GNU/Linux packages is easy, you just need python
|
|
in version 3.8 must be installed with venv and pyinstaller packages
|
|
(see Listing [[linux-env-deb]] for Debian and derived system). Finally,
|
|
run the {{{file(linux.sh)}}} script (see Listing [[linux-pkg]]).
|
|
|
|
#+NAME: linux-env-deb
|
|
#+CAPTION: Install environment on GNU/Linux
|
|
#+begin_src shell
|
|
sudo apt install python3.8
|
|
python3 -m pip install venv
|
|
python3 -m pip install pyinstaller
|
|
#+end_src
|
|
|
|
#+NAME: linux-pkg
|
|
#+CAPTION: Build GNU/Linux package
|
|
#+begin_src shell
|
|
cd packages
|
|
./linux.sh
|
|
#+end_src
|
|
|
|
*** Windows {{{windows}}}
|
|
|
|
To make the Windows packages you have two choice: If you use Windows
|
|
you can use the script {{{file(packages/windows.bat)}}}, other else
|
|
you can use the script {{{file(packages/wine.sh)}}}. Each script need
|
|
a specific software environment.
|
|
|
|
On windows, you needs python on version 3.8, pyinstaller and
|
|
NSIS[fn:nsis] installed. On GNU/Linux you need wget, wine and
|
|
winetricks installed.
|
|
|
|
[fn:nsis] The NSIS web site: https://sourceforge.net/projects/nsis/
|
|
|
|
** Setup the CI environment
|
|
|
|
{{{pamhyr2}}} need a Linux ci-runner and a Windows ci-runner for building
|
|
package. The windows ci-runner could run on a Wine environement.
|
|
|
|
*** Linux {{{linux}}}
|
|
|
|
The Linux ci-runner need some software and dependencies in addtion of
|
|
gitlab-ci.
|
|
|
|
#+begin_src shell
|
|
sudo apt install \
|
|
emacs emacs-goodies-el \
|
|
texlive-full \
|
|
python3.8 python3.8-venv
|
|
|
|
sudo python3 -m pip install pyinstaller
|
|
#+end_src
|
|
|
|
*** Windows (Wine) {{{windows}}}
|
|
|
|
The ci-runner environment for Wine need at least wine version 8, let
|
|
[[https://www.numetopia.fr/comment-installer-wine-sur-ubuntu-ou-linux-mint/][see who to add wine official depot to your linux distribution]].
|
|
|
|
#+begin_src shell
|
|
sudo apt install wine-stable winetricks
|
|
#+end_src
|
|
|
|
**** Setup environment
|
|
|
|
Export Wine environment variable to set wine as 64 bits architecture
|
|
and set the correct path for wine environment.
|
|
|
|
#+begin_src shell
|
|
export WINARCH=win64
|
|
export WINEPREFIX=$PWD/my-wine-runner-prefix
|
|
#+end_src
|
|
|
|
Setup Wine environment to Windows 10 and install the minimal fonts
|
|
with =winetricks=.
|
|
|
|
#+begin_src shell
|
|
winetricks corefonts win10
|
|
#+end_src
|
|
|
|
**** Install dependencies
|
|
|
|
First install 7zip with help of =winetricks=.
|
|
|
|
#+begin_src shell
|
|
winetricks 7zip
|
|
#+end_src
|
|
|
|
In addition, install in the environment the Windows version of:
|
|
- [[https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe][Python 3.8.10]] (ensure the python path is set and Pip is enable)
|
|
- [[https://git-scm.com/downloads][Git]]
|
|
- [[https://github.com/PowerShell/PowerShell/releases/download/v7.0.1/PowerShell-7.0.1-win-x64.msi][PowerShell]]
|
|
- [[https://freefr.dl.sourceforge.net/project/nsis/NSIS%203/3.08/nsis-3.08-setup.exe][Nsis]]
|
|
|
|
To run a Windows executable into wine environement, use =wine64= command:
|
|
|
|
#+begin_src shell
|
|
wine64 <the-exe-file>
|
|
#+end_src
|
|
|
|
Now, we can install =pyinstaller= on this windows environment:
|
|
#+begin_src shell
|
|
wine python -m pip install pyinstaller
|
|
#+end_src
|
|
|
|
Now, we can download [[https://docs.gitlab.com/runner/install/windows.html][Gitlab-ci]] runner for Windows an put it in the
|
|
current path.
|
|
|
|
**** Setup runner
|
|
|
|
You can configure the runner with command:
|
|
|
|
#+begin_src shell
|
|
wine64 gitlab-runner-windows-amd64.exe register
|
|
#+end_src
|
|
|
|
**** Run the runner
|
|
|
|
Create a new executable shell script =runner.sh= with following lines:
|
|
|
|
#+begin_src shell
|
|
#! /bin/sh
|
|
|
|
export WINARCH=win64
|
|
export WINEPREFIX=$PWD/my-wine-runner-prefix
|
|
|
|
wine64 gitlab-runner-windows-amd64.exe run
|
|
#+end_src
|
|
|
|
Now you can run the runner with command =./runner.sh=.
|
|
|
|
{{{bulb}}} You can run this command into a =screen= terminal, detach
|
|
the terminal and disconnect from runner machine to keep runner alive.
|
|
|
|
* Documentation files
|
|
|
|
This document and the user documentation are org files. This text file
|
|
format is formatted so that it can be exported in different formats:
|
|
PDF (with latex), ODT, HTML, etc. It was originally designed for the
|
|
GNUEmacs[fn:emacs] text editor, but can be edited with any text editor. Here we
|
|
take a look at the different features used in these documents.
|
|
|
|
[fn:org] The org-mode website: https://orgmode.org/ (last access
|
|
2023-09-15)
|
|
[fn:emacs] The GNUEmacs project website: https://gnu.org/s/emacs/
|
|
(last access 2023-09-15)
|
|
|
|
** Org-mode
|
|
*** Document structure
|
|
|
|
Org uses the =*= character to define a new document section. To add a
|
|
sub-section, you can add an additional =*= to the current section[fn::
|
|
See document structure documentation:
|
|
https://orgmode.org/org.html#Headlines (last access 2023-09-15)].
|
|
|
|
#+BEGIN_EXAMPLE
|
|
* Top level headline
|
|
** Second level
|
|
*** Third level
|
|
some text
|
|
*** Third level
|
|
more text
|
|
* Another top level headline
|
|
#+END_EXAMPLE
|
|
|
|
*** Format
|
|
|
|
Org-mode is a markup file, using markup in the text to modify the
|
|
appearance of a portion of text[fn:: See markup documentation:
|
|
https://orgmode.org/org.html#Emphasis-and-Monospace (last access
|
|
2023-09-15)].
|
|
|
|
| Markup | Results |
|
|
|--------------------+------------------|
|
|
| =*Bolt*= | *Bolt* |
|
|
| =/Italic/= | /Italic/ |
|
|
| =_underline_= | _underline_ |
|
|
| ==verbatim== | =verbatim= |
|
|
| =~code~= | ~code~ |
|
|
| =+strike-through+= | +strike-through+ |
|
|
|
|
*** Source code blocks
|
|
|
|
You can add some code blocks[fn:: See org-mode documentation for
|
|
source code: https://orgmode.org/org.html#Working-with-Source-Code
|
|
(last access 2023-09-15)] in the document.
|
|
|
|
Here is an example for python source code:
|
|
#+BEGIN_EXAMPLE
|
|
#+CAPTION: Get os type name in Python code
|
|
#+begin_src python
|
|
import os
|
|
|
|
print(f"Document build on system: {os.name}")
|
|
#+end_src
|
|
#+END_EXAMPLE
|
|
|
|
If you use GNUEmacs, it is also possible to run the code inside a
|
|
block and export (or not) the reuslts in the document.
|
|
|
|
#+OPTIONS: float:nil
|
|
#+CAPTION: Get os type name in Python code
|
|
#+begin_src python :python python3 :results output :exports both :noweb yes
|
|
import os
|
|
|
|
print(f"Document build on system: {os.name}")
|
|
#+end_src
|
|
|
|
#+RESULTS:
|
|
: Document build on system: posix
|
|
|
|
*** LaTeX
|
|
|
|
If we export the file to PDF, org-mode use {{{latex}}}. So we can add
|
|
some piece of {{{latex}}} into the document[fn:: See {{{latex}}} part
|
|
in documentation: https://orgmode.org/org.html#Embedded-LaTeX (last
|
|
access 2023-09-15)]. For exemple, we can add math formula like
|
|
=$E=mc^2$= ($E=mc^2$) or =\[E=mc^2\]=:
|
|
|
|
\[E=mc^2\]
|
|
|
|
But we can also add every type of {{{latex}}}:
|
|
|
|
#+BEGIN_EXAMPLE
|
|
# Add latex in line
|
|
#+LATEX: <my line of latex>
|
|
|
|
# Add multiple line of LaTeX
|
|
#+BEGIN_EXPORT latex
|
|
<my latex here>
|
|
#+END_EXPORT
|
|
#+END_EXAMPLE
|
|
|
|
It is also possible to add specific {{{latex}}} file header with
|
|
=#+LATEX_HEADER=. In this document we use the file
|
|
{{{file(doc/tools/latex.org)}}} for all {{{latex}}} headers.
|
|
|
|
*** Macro
|
|
|
|
In this document, we use a few macros[fn:: See marcos documentation
|
|
https://orgmode.org/org.html#Macro-Replacement (last access
|
|
2023-09-15)] to simplify writing. They allow you to define sequences
|
|
of text to be replaced, so that the macro name is replaced by its
|
|
value. They are defined in the {{{file(doc/tools/macro.org)}}}
|
|
file. Once defined, they can be used in the document as follows:
|
|
={{{<macro-name>}}}=. You can also have macros with arguments, in this
|
|
case: ={{{<macro-name>(arg1,...)}}}=. Les macros peuvent aussi
|
|
utiliser du code emacs-lisp.
|
|
|
|
#+BEGIN_EXAMPLE
|
|
# Exemple of macro définition
|
|
|
|
#+MACRO: toto tata
|
|
#+MACRO: add \(($1 + $2)\)
|
|
#+MACRO: emacs-version (eval (nth 2 (split-string (emacs-version))))
|
|
#+END_EXAMPLE
|
|
|
|
#+MACRO: toto tata
|
|
#+MACRO: add \(($1 + $2)\)
|
|
#+MACRO: emacs-version (eval (nth 2 (split-string (emacs-version))))
|
|
|
|
Macro apply:
|
|
- Marco ={{{toto}}}=: {{{toto}}}
|
|
- Marco ={{{add(x,y)}}}=: {{{add(x,y)}}}
|
|
- Marco ={{{emacs-version}}}=: {{{emacs-version}}}
|
|
|
|
*** Footnotes
|
|
|
|
Footnote in org-mode is define with marker =[fn:...]=[fn:: Create
|
|
footnotes in org-mode documentation
|
|
https://orgmode.org/org.html#Creating-Footnotes (last access
|
|
2023-09-15)]:
|
|
#+BEGIN_EXAMPLE
|
|
The Org website[fn:1] now looks a lot better than it used to.
|
|
...
|
|
[fn:1] The link is: https://orgmode.org
|
|
#+END_EXAMPLE
|
|
or:
|
|
#+BEGIN_EXAMPLE
|
|
The Org website[fn:: The link is: https://orgmode.org] now looks
|
|
a lot better than it used to.
|
|
...
|
|
#+END_EXAMPLE
|
|
|
|
*** References
|
|
|
|
The references use the {{{latex}}} bibtex tools. The bib file is in
|
|
{{{file(/doc/tools/ref.bib)}}} and use for developers and user
|
|
documentation. In document, use ={{{cite(<name>)}}}= to cite a paper.
|
|
|
|
** Export
|
|
|
|
To export the files, a {{{file(build.sh)}}} script is available in the org
|
|
files directories. On GNU/Linux system you can build the documentation
|
|
PDF file with the command =./build.sh=. Texlive package must be
|
|
installed, you can install only needed packages or all texlive
|
|
packages, for example on Debian (and some derived system) use command
|
|
Listing [[texlive-install]].
|
|
|
|
#+NAME: texlive-install
|
|
#+CAPTION: Installation command for texlive full on Debian system
|
|
#+begin_src shell
|
|
sudo apt install texlive-full
|
|
#+end_src
|
|
|
|
Some org-mode configuration used in documentations files are define in
|
|
=/doc/tools/=:
|
|
- {{{file(PamhyrDoc.cls)}}}: The {{{latex}}} document class
|
|
- {{{file(macro.org)}}}: Available macro
|
|
- {{{file(latex.org)}}}: {{{latex}}} configutation for documentations
|
|
files
|
|
- {{{file(setup.el)}}}: GNUEmacs configuration to build documentations
|
|
- {{{file(ref.bib)}}}: Bibtex files for documentations files
|
|
|
|
* How to contribute?
|
|
|
|
{{{pamhyr2}}} is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License[fn:license],
|
|
either version 3 of the License, or any later version.
|
|
|
|
[fn:license] The GPLv3 web page:
|
|
https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
|
|
** Guidelines
|
|
|
|
To contribute to {{{pamhyr2}}}, we expect a minimum of respect between
|
|
contributors. We therefore ask you to respect the following rules
|
|
regarding communication and contribution content:
|
|
+ No gender, racial, religious or social discrimination
|
|
+ No insults, personal attacks or potentially offensive remarks
|
|
+ {{{pamhyr2}}} is free software, and intended to remain so, so take
|
|
care with the licensing of libraries and external content you want
|
|
to add to the project
|
|
+ Humour or hidden easter eggs are welcome if they respect the
|
|
previous rules
|
|
|
|
** Make a contribution
|
|
|
|
There are several ways to contribute: you can report a bug by creating
|
|
an issue on the project's gitlab page[fn:p2-gitlab], or you can create
|
|
a merge request on the same page with the changes you have made to the
|
|
code, translation or documentation.
|
|
|
|
The {{{pamhyr2}}} copyright is owned by INRAE[fn:inrae], but we keep a
|
|
record of each contributors. If you made a modification to
|
|
{{{pamhyr2}}} software, please add your name at the end of
|
|
{{{file(AUTHORS)}}} file and respect the Listing [[auth-format]]
|
|
format. You can update this file information for following
|
|
contribution.
|
|
|
|
#+NAME: auth-format
|
|
#+CAPTION: =AUTHORS= file format
|
|
#+begin_src text
|
|
<first name> <last name> [(optional) email], <organisation>, <years>
|
|
#+end_src
|
|
|
|
#+CAPTION: Current =AUTHORS= file
|
|
#+INCLUDE: "../../AUTHORS" src text
|
|
|
|
[fn:p2-gitlab] The {{{pamhyr2}}} Gitlab project page:
|
|
https://gitlab.com/pamhyr/pamhyr2
|
|
[fn:inrae] The INRAE web site: https://www.inrae.fr/
|
|
|
|
** Translate
|
|
|
|
You can improve or add translation for the project. To contribute to
|
|
{{{pamhyr2}}} translate, you need to use Qt
|
|
Linguist[fn:qt-linguist]. Open Qt-linguist and edit the translation
|
|
({{{file(.ts)}}}) file, finally, commit the new version of file and
|
|
make a merge request.
|
|
|
|
If you want add a new language, edit the script
|
|
{{{file(src/lang/create_ts.sh)}}} like Listing [[ts-it]]. Run the script
|
|
and open the new file with Qt-linguist, setup target language (Figure
|
|
[[qt-linguist-setup]]) and complete translation. Finally, commit the new
|
|
file and make a merge request.
|
|
|
|
#+NAME: ts-it
|
|
#+CAPTION: Example of modified {{{file(src/lang/create_ts.sh)}}} to add italian (it) translate for {{{pamhyr2}}}
|
|
#+begin_src shell
|
|
...
|
|
LANG="fr it"
|
|
...
|
|
#+end_src
|
|
|
|
#+NAME: qt-linguist-setup
|
|
#+ATTR_LATEX: :width 8cm
|
|
#+CAPTION: Qt linguist lang setup example with italian.
|
|
[[./images/Qt-linguist-setup-lang.png]]
|
|
|
|
[fn:qt-linguist] The Qt linguist documentation web page:
|
|
https://doc.qt.io/qt-5/qtlinguist-index.html (last access 2023-09-18)
|
|
|
|
** Code contribution
|
|
|
|
If you are developper you can improve and/or add features to
|
|
{{{pamhyr2}}}. Please, follow the architecture described in section
|
|
[[Architecture]] as closely as possible. Keep the code simple, clear and
|
|
efficient as possible. The master branch is reserved for the project
|
|
maintainer; you can create a new branch or fork the project before the
|
|
request.
|
|
|
|
|
|
{{{biblio}}}
|