Skip to content

Commit f1c289e

Browse files
author
lindakladivova
committed
new start dialog for working dir and template settings
1 parent d6468b4 commit f1c289e

File tree

4 files changed

+166
-21
lines changed

4 files changed

+166
-21
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""
2+
@package jupyter_notebook.dialog
3+
4+
@brief Integration of Jupyter Notebook to GUI.
5+
6+
Classes:
7+
- dialog::JupyterStartDialog
8+
9+
(C) 2025 by the GRASS Development Team
10+
11+
This program is free software under the GNU General Public License
12+
(>=v2). Read the file COPYING that comes with GRASS for details.
13+
14+
@author Linda Karlovska <linda.karlovska seznam.cz>
15+
"""
16+
17+
from pathlib import Path
18+
import grass.script as gs
19+
20+
import wx
21+
22+
23+
class JupyterStartDialog(wx.Dialog):
24+
"""Dialog for selecting working directory and Jupyter startup options."""
25+
26+
def __init__(self, parent):
27+
wx.Dialog.__init__(
28+
self, parent, title=_("Start Jupyter Notebook"), size=(500, 300)
29+
)
30+
env = gs.gisenv()
31+
mapset_path = Path(env["GISDBASE"]) / env["LOCATION_NAME"] / env["MAPSET"]
32+
self.default_dir = mapset_path / "notebooks"
33+
34+
self.selected_dir = self.default_dir
35+
self.create_template = True
36+
37+
sizer = wx.BoxSizer(wx.VERTICAL)
38+
39+
# Working directory section
40+
dir_box = wx.StaticBox(self, label=_("Notebook working directory"))
41+
dir_sizer = wx.StaticBoxSizer(dir_box, wx.VERTICAL)
42+
43+
self.radio_default = wx.RadioButton(
44+
self,
45+
label=_("Use default: {}").format(self.default_dir),
46+
style=wx.RB_GROUP,
47+
)
48+
self.radio_custom = wx.RadioButton(self, label=_("Select another directory:"))
49+
50+
self.dir_picker = wx.DirPickerCtrl(
51+
self,
52+
message=_("Choose a working directory"),
53+
style=wx.DIRP_USE_TEXTCTRL | wx.DIRP_DIR_MUST_EXIST,
54+
)
55+
self.dir_picker.Enable(False)
56+
57+
dir_sizer.Add(self.radio_default, 0, wx.ALL, 5)
58+
dir_sizer.Add(self.radio_custom, 0, wx.ALL, 5)
59+
dir_sizer.Add(self.dir_picker, 0, wx.EXPAND | wx.ALL, 5)
60+
sizer.Add(dir_sizer, 0, wx.EXPAND | wx.ALL, 10)
61+
62+
# Jupyter startup section
63+
options_box = wx.StaticBox(self, label=_("Options"))
64+
options_sizer = wx.StaticBoxSizer(options_box, wx.VERTICAL)
65+
66+
self.checkbox_template = wx.CheckBox(self, label=_("Create example notebook"))
67+
self.checkbox_template.SetValue(True)
68+
self.checkbox_template.SetToolTip(
69+
_(
70+
"If selected, a welcome notebook (welcome.ipynb) will be created,\n"
71+
"but only if the selected directory contains no .ipynb files."
72+
)
73+
)
74+
options_sizer.Add(self.checkbox_template, 0, wx.ALL, 5)
75+
76+
info = wx.StaticText(
77+
self,
78+
label=_(
79+
"Note: Template will be created only if the directory contains no .ipynb files."
80+
),
81+
)
82+
83+
options_sizer.Add(info, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM, 8)
84+
85+
sizer.Add(options_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 10)
86+
87+
# OK / Cancel buttons
88+
btns = self.CreateSeparatedButtonSizer(wx.OK | wx.CANCEL)
89+
sizer.Add(btns, 0, wx.EXPAND | wx.ALL, 10)
90+
91+
self.SetSizer(sizer)
92+
93+
self.radio_default.Bind(wx.EVT_RADIOBUTTON, self.OnRadioToggle)
94+
self.radio_custom.Bind(wx.EVT_RADIOBUTTON, self.OnRadioToggle)
95+
96+
self.Fit()
97+
self.Layout()
98+
self.SetMinSize(self.GetSize())
99+
self.CentreOnParent()
100+
101+
def OnRadioToggle(self, event):
102+
"""Enable/disable directory picker based on user choice."""
103+
self.dir_picker.Enable(self.radio_custom.GetValue())
104+
105+
def GetValues(self):
106+
"""Return selected working directory and template preference."""
107+
if self.radio_custom.GetValue():
108+
self.selected_dir = Path(self.dir_picker.GetPath())
109+
else:
110+
self.selected_dir = self.default_dir
111+
112+
return {
113+
"directory": self.selected_dir,
114+
"create_template": self.checkbox_template.GetValue(),
115+
}

gui/wxpython/jupyter_notebook/panel.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def __init__(
3535
title=_("Jupyter Notebook"),
3636
statusbar=None,
3737
dockable=False,
38+
workdir=None,
39+
create_template=False,
3840
**kwargs,
3941
):
4042
"""Jupyter main panel."""
@@ -44,11 +46,13 @@ def __init__(
4446
self.parent = parent
4547
self._giface = giface
4648
self.statusbar = statusbar
49+
self.workdir = workdir
4750

4851
self.SetName("Jupyter")
4952

50-
self.directory_manager = JupyterDirectoryManager()
51-
self.workdir = self.directory_manager.workdir
53+
self.directory_manager = JupyterDirectoryManager(
54+
workdir=self.workdir, create_template=create_template
55+
)
5256
self.server_manager = JupyterServerInstance(workdir=self.workdir)
5357

5458
self.toolbar = JupyterToolbar(parent=self)

gui/wxpython/main_window/frame.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -909,9 +909,28 @@ def OnGModeler(self, event=None, cmd=None):
909909
def OnJupyterNotebook(self, event=None, cmd=None):
910910
"""Launch Jupyter Notebook page. See OnJupyterNotebook documentation"""
911911
from jupyter_notebook.panel import JupyterPanel
912+
from jupyter_notebook.dialogs import JupyterStartDialog
913+
914+
dlg = JupyterStartDialog(parent=self)
915+
result = dlg.ShowModal()
916+
917+
if result != wx.ID_OK:
918+
dlg.Destroy()
919+
return
920+
921+
values = dlg.GetValues()
922+
dlg.Destroy()
923+
924+
workdir = values["directory"]
925+
create_template = values["create_template"]
912926

913927
jupyter_panel = JupyterPanel(
914-
parent=self, giface=self._giface, statusbar=self.statusbar, dockable=True
928+
parent=self,
929+
giface=self._giface,
930+
statusbar=self.statusbar,
931+
dockable=True,
932+
workdir=workdir,
933+
create_template=create_template,
915934
)
916935
jupyter_panel.SetUpPage(self, self.mainnotebook)
917936
jupyter_panel.SetUpNotebookInterface()

python/grass/workflows/directory.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,17 @@
3333
class JupyterDirectoryManager:
3434
"""Manage a directory of Jupyter notebooks tied to the current GRASS mapset."""
3535

36-
def __init__(self):
37-
"""Initialize the Jupyter notebook directory and load existing files."""
38-
self._workdir = self._get_workdir()
39-
self._files = None
36+
def __init__(self, workdir=None, create_template=False):
37+
"""Initialize the Jupyter notebook directory and load existing files.
38+
39+
:param workdir: Optional custom working directory (Path). If not provided,
40+
the default MAPSET notebooks directory is used.
41+
:param create_template: If True, create a welcome notebook if the directory is empty.
42+
"""
43+
self._workdir = workdir or self._get_workdir()
44+
self._workdir.mkdir(parents=True, exist_ok=True)
45+
self._files = []
46+
self._create_template = create_template
4047

4148
@property
4249
def workdir(self):
@@ -54,7 +61,7 @@ def files(self):
5461

5562
def _get_workdir(self):
5663
"""
57-
:return: Path to working directory, it is created if it does not exist (Path).
64+
:return: Path to default working directory, it is created if it does not exist (Path).
5865
"""
5966
env = gs.gisenv()
6067
mapset_path = "{gisdbase}/{location}/{mapset}".format(
@@ -67,6 +74,19 @@ def _get_workdir(self):
6774
workdir.mkdir(parents=True, exist_ok=True)
6875
return workdir
6976

77+
def prepare_files(self):
78+
"""
79+
Populate the list of files in the working directory.
80+
"""
81+
# Find all .ipynb files in the notebooks directory
82+
83+
self._files = [
84+
f for f in Path(self._workdir).iterdir() if str(f).endswith(".ipynb")
85+
]
86+
87+
if self._create_template and not self._files:
88+
self.create_welcome_notebook()
89+
7090
def import_file(self, source_path, new_name=None, overwrite=False):
7191
"""Import an existing notebook file to the working directory.
7292
@@ -201,16 +221,3 @@ def create_new_notebook(self, new_name, template_name="new.ipynb"):
201221
self._files.append(target_path)
202222

203223
return target_path
204-
205-
def prepare_files(self):
206-
"""
207-
Populate the list of files in the working directory.
208-
Creates a welcome template if does not exist.
209-
"""
210-
# Find all .ipynb files in the notebooks directory
211-
self._files = [
212-
f for f in Path(self._workdir).iterdir() if str(f).endswith(".ipynb")
213-
]
214-
if not self._files:
215-
# If no .ipynb files are found, create a welcome ipynb file
216-
self.create_welcome_notebook()

0 commit comments

Comments
 (0)