## 一、 環境準備
1. 使用Anaconda進行版本控制、包管理,其管理工具`conda` 與`npm`一樣方便的存在
```
brew cask install Anaconda
```
1. 安裝Node.js與npm
2. 創建虛擬環境
```
conda create -n envName # envName 是環境名
```
這里順便提供環境管理的幾個常用命令
- 激活環境
```
activate envName
```
- 推出環境
```
deactivate envName
```
- 刪除環境
```
conda remove -n flowers --all
```
- 查看所有環境
```
conda info --envs
```
## 二、 Jupyter Notebook使用
使用Anaconda安裝python后,就已經集成Jupyter nodebook了,如果notebook與conda的環境和包沒有關聯,可以執行以下命令進行關聯
### 1. 安裝
```
conda install nb_conda
```
### 2.使用
- 可以在Conda類目下對conda環境和包進行一系列操作。
- 可以在筆記本內的“Kernel”類目里的“Change
kernel”切換內核。
詳細參考,見:
[1. Jupyter Notebook介紹、安裝及使用教程](https://zhuanlan.zhihu.com/p/33105153)
[2. 給初學者的 Jupyter Notebook 教程](https://juejin.im/post/5af8d3776fb9a07ab7744dd0#heading-10)
## 三、二次開發
Jupyter Notebook的[項目地址](https://github.com/jupyter/notebook)
在創建的虛擬環境中,運行一下操作
```
git clone https://github.com/jupyter/notebook # clone project
cd notebook
pip install -e . # install packages under the workspace
```
Notebook后端使用tornado框架,分為多個模塊。對應的于templates不同的頁面。
### 配置文件
執行`jupyter notebook --generate-config`
在用戶目錄生成配置文件,mac中,進入finder, 選擇`~/.jupyter/jupyter_notebook_config.py` 進行配置
常用配置如下:
```
# 解決跨域問題
c.NotebookApp.tornado_settings = {
'headers': {
'Content-Security-Policy': "frame-ancestors self *; report-uri /api/security/csp-report",
}
}
# 可訪問的IP地址
c.NotebookApp.ip = '*'
# 端口
c.NotebookApp.port = 9123
# 啟動服務端時是否打開瀏覽器
c.NotebookApp.open_browser = False
# 去掉密碼驗證
c.NotebookApp.token = ""
# 是否開啟新建終端
c.NotebookApp.terminals_enabled = False
# 是否可以通過前端修改密碼
c.NotebookApp.allow_password_change = False
# 前端是否展示退出按鈕
c.NotebookApp.quit_button = False
# 默認打開的目錄路徑
c.NotebookApp.notebook_dir = "workspace"
```
### 前端二次開發
項目采用Tornado的自帶系統模版,該模板本身是HTML文件,包含有python的反控制結構和表達式。所以,對于前端頁面的二次開發,主要包含兩個部分:
- 模板文件及CSS樣式的修改
- 控制模塊修改(python和js)
其中,jupyter的核心,cell及在線編輯的功能是基于`Code Mirror`, 該項目是網頁實現的代碼編輯器,支持語法高亮、自動縮緊等,因此,對于一些cell相關的二次開發,需要對`Code Mirror` 熟悉。
`Code Mirror` 的簡介如下:
[1. 在線代碼編輯器CodeMirror](https://zhuanlan.zhihu.com/p/22163474)
[2.官方文檔](https://codemirror.net/doc/manual.html#usage)
#### 目錄結構
項目按照模塊劃分,包括`notebook`、`edit`、`nbconvert`等,前端比較關注的是
- `notebook->static->templates` 各頁面的模板文件
- `notebook->static->static` 各頁面的樣式文件CSS和渲染控制文件JS
項目的其他文件和目錄暫不做分析,先從簡單的修改開始
#### 本地啟動
經過以上配置,進入項目文件夾,執行以下代碼:
```
jupyter notebook
```
這里需要注意的是,因為這里關注前端的二次開發,因此可以運行`npm run build:watch`用來監聽js的修改和構建。
#### toolbar的修改
工具欄在notebook的主頁面中,打開`templates->notebook.html` 查看其模板結構,可以看到其工具欄的模板代碼非常簡單
```
<div id="maintoolbar" class="navbar">
<div class="toolbar-inner navbar-inner navbar-nobg">
<div id="maintoolbar-container" class="container"></div>
</div>
</div>
```
即完全是靜態頁面,也就說,工具欄的按鈕和標簽是通過腳本動態添加的。
同樣在`static`目錄下,可以看到名為`maintoolbar.js`,這里定了`maintoolbar`對象,在該對象的原型上綁定了一些方法,比如:
```
MainToolBar.prototype._pseudo_actions.add_celltype_list = function () {
var that = this;
var multiselect = $('<option/>').attr('value','multiselect').attr('disabled','').text('-');
var sel = $('<select/>')
.attr('id','cell_type')
.attr('aria-label', i18n.msg._('combobox, select cell type'))
.attr('role', 'combobox')
.addClass('form-control select-xs')
.append($('<option/>').attr('value','code').text(i18n.msg._('Code')))
.append($('<option/>').attr('value','markdown').text(i18n.msg._('Markdown')))
.append($('<option/>').attr('value','raw').text(i18n.msg._('Raw NBConvert')))
.append($('<option/>').attr('value','heading').text(i18n.msg._('Heading')))
.append(multiselect);
this.notebook.keyboard_manager.register_events(sel);
...
```
這里即動態添加工具欄內容的代碼,也就可以從這里入手,根據自己的實際需求,修改相應的前端展示內容。
##### 下拉選項修改

比如一個簡單需求:修改工具欄下拉的內容,并能通過與父級通訊,實現在下拉切換時,調用外部的方法。
- 修改下拉的內容
直接修改動態加載頁面的js文件,去除不需要的下拉內容,這里不過多說明
- 與父組件進行
notebook在項目中會作為iframe嵌在頁面中,可考慮iframe父子通訊的方法。在本系統中,因為頁面存在跨域問題,因此無法直接使用`window.parent.fn();` 或者 `window.top.fn()` 。
這里使用`window.postMessage`方法,實現跨域的通訊,在下拉的change事件中,添加如下代碼
```
/**
* 與父級通訊,調用外部方法
*/
window.top.postMessage({
selected: data.cell_type,
eventType: 'languageChanged'
}, '*')
```
這樣,在父級頁面,只需要添加監聽,即可實現通訊,具體如下:
```
window.addEventListener('message', event => {
if (event.data.eventType === 'languageChanged') {
console.log(event.data.selected) // 修改為具體的需要執行的函數即可
}
})
```
對iframe跨域同學的兩種方案:
[1. postMessage方法](https://forrany.github.io/2019/12/09/Jupyter-Notebook-前端二次開發初探/[https://gjincai.github.io/2017/05/22/子頁面iframe跨域執行父頁面定義的JS方法/](https://gjincai.github.io/2017/05/22/子頁面iframe跨域執行父頁面定義的JS方法/))
[2. iframe代理方法](https://www.jianshu.com/p/9d90d3333215)
#### notebook功能模塊
核心功能模塊的目錄為
- 編輯器相關功能 `/notebook/notebook/static/edit/js`
- notebook功能模塊 `/notebook/notebook/static/notebook/js`
其中編輯器包含了導航欄、編輯器功能板的動態頁面生成以及相關action的調用;
notebook的功能模塊,則囊括了整個jupyter-notebook的各項基礎功能,包括自動保存、自動保存配置、編輯器狀態、事件注冊等。
##### notebook.js
在notebook目錄下,`notebook.js`定義了Notebook的類,可以將其看作項目的容器,將各基礎模塊和功能集成并接入進來,這里舉一個簡單的例子。
比如編輯器的自動保存功能,notebook的屬性上有
- `autosave_interval`
可以配置自動保存的事件間隔,默認為2分鐘。
------
**實例解釋**
又比如在iframe嵌套jupyter-notebook中,外層想要獲取編輯器狀態,避免在為保存狀態下刷新。
編輯器的未保存狀態刷新,其本身是有保護機制的,在源碼中,使用一下子方法做了處理
```
window.onbeforeunload = function() {
...
}
```
但是,該方法是在外部刷新iframe時才會觸發,現在想要外部去調用內部刷新前,就先判斷是否可刷新,并給出更友好的提示。
這樣開發思路可以為:
1. 外部通過`postMessage`查詢iframe(notebook)的狀態,詢問是否可刷新
2. 內部監聽`message`,并根據編輯狀態返回信息
3. 外部拿到編輯器狀態,決定是否刷新,并提示用戶。
這樣,問題就簡化成了,獲取編輯器狀態即可,編輯器狀態在`Notebook`的`dirty`屬性上,當該屬性為 `true`時,表示當前編輯器未保存。
因此,在Notebook.js中可以加入以下代碼:
```
//...
window.addEventListener('message', event => {
if (event.data.eventType === 'getEditStatus') {
window.top.postMessage({
status: that.dirty,
eventType: 'editStatus'
}, '*')
}
})
```
同理,父級也只需要通過`postMessage`實現通訊,即可根據狀態信息,完成二次開發,給出更友好的提示。
其他的模塊、內容類似,等之后深入研究后,繼續補充前端二次開發的踩坑經歷。
## 特殊功能
### 配置視圖顯示
```
cd /root/anaconda3/etc/jupyter/nbconfig
```
vim notebook.json
```json
{
"Notebook": {
"Header": false,
"Toolbar": true
}
}
```
## 參考資料
[SQL Interface within JupyterLab](https://www.datacamp.com/community/tutorials/sql-interface-within-jupyterlab)
[Jupyter Notebook Tutorial: The Definitive Guide](https://www.datacamp.com/community/tutorials/tutorial-jupyter-notebook)
[jupyter notebook 中文文檔](https://www.osgeo.cn/jupyter/extending/frontend_extensions.html)
[jupyter notebook document](https://jupyter-notebook.readthedocs.io/en/latest/extending/frontend_extensions.html#installing-and-enabling-extensions)
[tornado web 服務器框架](http://www.ttlsa.com/docs/tornado/)
[jupyter notebook 二次開發經驗](https://forrany.github.io/2019/12/09/Jupyter-Notebook-%E5%89%8D%E7%AB%AF%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E5%88%9D%E6%8E%A2/)
- 關于本書
- 引言
- 準備工作
- 安裝 Go語言開發環境
- 開始使用Go
- 創建一個Go模塊
- 第一章 手把手系列
- 1.1 教你搭建Nginx教程
- 1.2 教你搭建Jupyter教程
- 1.3 教你搭建Node教程
- 1.4 教你搭建Fabric教程
- 1.5 教你搭建Ethereum教程
- 1.6 教你搭建Bitcoin教程
- 1.7 教你搭建Systemd教程
- 第二章 架構師之路
- 2.1 微服務開發筆記
- 2.2 Docker開發筆記
- 2.3 ElasticSearch開發筆記
- 2.4 Linux開發筆記
- 2.5 Mysql開發筆記
- 2.6 Nginx開發筆記
- 2.7 Redis開發筆記
- 第三章 區塊鏈教程
- 3.1 Bitcoin開發筆記
- 3.2 Ethereum開發筆記
- 3.3 USDT開發筆記
- 第四章 網絡知識庫
- 4.1 比特幣白皮書
- 4.2 以太坊白皮書
- 第五章 技術博客園
- 5.1 Fabric架構詳解
- 5.2 技術開發指南
- 5.3 共識機制詳解
- 第六章 項目管理
- 6.1 項目運行環境
- 6.2 項目經理的角色
- 6.3 第6、7、8章框架
- 第七章 公務員考公
- 7.1 程序員成功上岸經歷
- 7.2 程序員備考的最佳實踐
- 7.3 程序員備考過程中會遇到哪些問題?
- 7.4 公考公平嗎,35歲再去考可以么?
- 7.5 資料、工具推薦和擴展閱讀
- 結論
- 附錄