Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added gui/icons/grass/jupyter-inactive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
101 changes: 101 additions & 0 deletions gui/icons/grass/jupyter-inactive.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added gui/icons/grass/jupyter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 85 additions & 0 deletions gui/icons/grass/jupyter.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions gui/wxpython/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ include $(MODULE_TOPDIR)/include/Make/Python.make
DSTDIR = $(GUIDIR)/wxpython

SRCFILES := $(wildcard icons/*.py xml/*) \
$(wildcard animation/*.py core/*.py datacatalog/*.py history/*.py dbmgr/*.py gcp/*.py gmodeler/*.py \
$(wildcard animation/*.py core/*.py datacatalog/*.py jupyter_notebook/*.py history/*.py dbmgr/*.py gcp/*.py gmodeler/*.py \
gui_core/*.py iclass/*.py lmgr/*.py location_wizard/*.py main_window/*.py mapwin/*.py mapdisp/*.py \
mapswipe/*.py modules/*.py nviz/*.py psmap/*.py rdigit/*.py \
rlisetup/*.py startup/*.py timeline/*.py vdigit/*.py \
Expand All @@ -19,7 +19,7 @@ SRCFILES := $(wildcard icons/*.py xml/*) \
DSTFILES := $(patsubst %,$(DSTDIR)/%,$(SRCFILES)) \
$(patsubst %.py,$(DSTDIR)/%.pyc,$(filter %.py,$(SRCFILES)))

PYDSTDIRS := $(patsubst %,$(DSTDIR)/%,animation core datacatalog history dbmgr gcp gmodeler \
PYDSTDIRS := $(patsubst %,$(DSTDIR)/%,animation core datacatalog jupyter_notebook history dbmgr gcp gmodeler \
gui_core iclass lmgr location_wizard main_window mapwin mapdisp modules nviz psmap \
mapswipe vdigit wxplot web_services rdigit rlisetup startup \
vnet timeline iscatt tplot photo2image image2target)
Expand Down
122 changes: 122 additions & 0 deletions gui/wxpython/jupyter_notebook/dialogs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""
@package jupyter_notebook.dialogs
@brief Integration of Jupyter Notebook to GUI.
Classes:
- dialog::JupyterStartDialog
(C) 2025 by the GRASS Development Team
This program is free software under the GNU General Public License
(>=v2). Read the file COPYING that comes with GRASS for details.
@author Linda Karlovska <linda.karlovska seznam.cz>
"""

import os
from pathlib import Path

import wx

from grass.workflows.directory import get_default_jupyter_workdir


class JupyterStartDialog(wx.Dialog):
"""Dialog for selecting Jupyter startup options."""

def __init__(self, parent):
super().__init__(parent, title=_("Start Jupyter Notebook"), size=(500, 300))

self.default_dir = get_default_jupyter_workdir()

self.selected_dir = self.default_dir
self.create_template = True

sizer = wx.BoxSizer(wx.VERTICAL)

# Working directory section
dir_box = wx.StaticBox(self, label=_("Notebook working directory"))
dir_sizer = wx.StaticBoxSizer(dir_box, wx.VERTICAL)

self.radio_default = wx.RadioButton(
self,
label=_("Use default: {}").format(self.default_dir),
style=wx.RB_GROUP,
)
self.radio_custom = wx.RadioButton(self, label=_("Select another directory:"))

self.dir_picker = wx.DirPickerCtrl(
self,
message=_("Choose a working directory"),
style=wx.DIRP_USE_TEXTCTRL | wx.DIRP_DIR_MUST_EXIST,
)
self.dir_picker.Enable(False)

dir_sizer.Add(self.radio_default, 0, wx.ALL, 5)
dir_sizer.Add(self.radio_custom, 0, wx.ALL, 5)
dir_sizer.Add(self.dir_picker, 0, wx.EXPAND | wx.ALL, 5)
sizer.Add(dir_sizer, 0, wx.EXPAND | wx.ALL, 10)

# Template preference section
options_box = wx.StaticBox(self, label=_("Options"))
options_sizer = wx.StaticBoxSizer(options_box, wx.VERTICAL)

self.checkbox_template = wx.CheckBox(self, label=_("Create welcome notebook"))
self.checkbox_template.SetValue(True)
self.checkbox_template.SetToolTip(
_(
"If selected, a welcome notebook (welcome.ipynb) will be created,\n"
"but only if the selected directory contains no .ipynb files."
)
)
options_sizer.Add(self.checkbox_template, 0, wx.ALL, 5)

info = wx.StaticText(
self,
label=_(
"Note: The welcome notebook will be created only if the directory contains no .ipynb files."
),
)

options_sizer.Add(info, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM, 8)

sizer.Add(options_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 10)

btns = self.CreateSeparatedButtonSizer(wx.OK | wx.CANCEL)
sizer.Add(btns, 0, wx.EXPAND | wx.ALL, 10)

self.SetSizer(sizer)

self.radio_default.Bind(wx.EVT_RADIOBUTTON, self.OnRadioToggle)
self.radio_custom.Bind(wx.EVT_RADIOBUTTON, self.OnRadioToggle)

self.Fit()
self.Layout()
self.SetMinSize(self.GetSize())
self.CentreOnParent()

def OnRadioToggle(self, event):
"""Enable/disable directory picker based on user choice."""
self.dir_picker.Enable(self.radio_custom.GetValue())

def GetValues(self):
"""Return selected working directory and template preference."""
if self.radio_custom.GetValue():
path = Path(self.dir_picker.GetPath())

if not os.access(path, os.W_OK) or not os.access(path, os.X_OK):
wx.MessageBox(
_("You do not have permission to write to the selected directory."),
_("Error"),
wx.ICON_ERROR,
)
return None
self.selected_dir = path
else:
self.selected_dir = self.default_dir

return {
"directory": self.selected_dir,
"create_template": self.checkbox_template.GetValue(),
}
Loading
Loading