60 行 Python 代碼開發在線 markdown 編輯器

本文示例代碼已上傳至我的Github倉庫 https://github.com/CNFeffery/DataScienceStudyNotes

1 簡介

這是我的系列教程**「Python+Dash 快速 web 應用開發」**的第六期,在上一期的文章中,我們完成了對Dash中回調交互高級特性的探討,在今後陸續推出的教程內容中,我們將一起來學習Dash生態中那些豐富的**「頁面部件」**,從而賦予我們打造各種強大交互式 web 應用的能力。

而在今天的教程內容中,我將帶大家學習Dash中實用的一些基礎性的**「靜態部件」**,它們可以幫助我們打造更加正式的 web 應用,並在**最後**教大家如何**僅僅 60 行代碼就開發出一個在線 markdown 編輯器**。

圖 1

2 Dash 中的基礎靜態部件

我們在這裏所說的靜態頁面部件,主要指的是其本身不具備直接的交互功能,而是以**「呈現內容」**爲主要功能,就像下面的簡單對比一樣:

app1.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc
import dash_core_components as dcc

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            html.Br(),
            html.H1('靜態部件示例'),
            html.Hr(),
            html.H2('這是二級標題'),
            html.H3('這是三級標題'),
            html.H4('這是四級標題'),
            html.P(
                [
                    '這是一個',
                    html.A('鏈接'href='#'),
                    ',而這是一段',
                    html.Strong('加粗文字'),
                    ',這是一段帶上下標的文字:',
                    '測試',
                    html.Sup('上標'),
                    ',測試',
                    html.Sub('下標')
                ]
            ),

            html.Br(),
            html.H1('交互部件示例'),
            html.Br(),
            dcc.Dropdown(
                options=[
                    {'label''測試1''value''測試1'},
                    {'label''測試2''value''測試2'},
                    {'label''測試3''value''測試3'},
                ]),
            html.Br(),
            dcc.Checklist(
                options=[
                    {'label''測試1''value''測試1'},
                    {'label''測試2''value''測試2'},
                    {'label''測試3''value''測試3'},
                ],
                value=['測試1']
            ),
            html.Br(),
            dcc.RangeSlider(
                min=0,
                max=20,
                step=0.5,
                value=[5, 15]
            )
        ]
    )
)

if __name__ == '__main__':
    app.run_server(debug=True)

圖 2

可以看到,靜態部件其實就是我們平時瀏覽網頁看到的各種內容元素,他們本身不直接承擔回調交互功能,只能配合其他交互部件來實現交互功能。

2.1 Dash 中常用的基礎靜態部件

Dash中所集成的一些常用基礎性靜態部件,其實就是對一些常見html元素的遷移,對應着dash_html_components中封裝的衆多類,這裏我們只介紹部分比較常用的:

2.1.1 與文字格式相關的常用部件

首先我們來介紹Dash衆多基礎靜態部件中,與組織頁面或文字格式相關的一些:

dash_html_components中,H1()H6()分別對應着 1 級到 6 級標題。

dash_html_components中的Br()表示換行,而Hr()則表示水平分割線,這在我們佈局元素時經常使用到。

P()用於表示一段文字或內容,典型如我們在博客中看到的每一段落內容都是由P()標籤所組織的,配合css中的text-indent屬性可以用來設置首行縮進。

A()用於表示一個可點擊的鏈接,其參數href用於填入對應跳轉的地址,也可以配合id,實現點擊重新定位到頁面內的其它元素,其target參數用於設置跳轉方式,譬如target="_blank"會在新標籤頁跳轉打開,具體內容可參考(https://www.w3school.com.cn/tags/att_a_target.asp)。

I()主要用於在段落中將包裹的文字內容變爲斜體,Code()用於在一段文字中表示代碼片段U()用於給所包含內容添加下劃線,Mark()則用於高亮標註文字。

以上所介紹的這些靜態部件可以通過下面的小例子直觀的感受一下:

app2.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            html.H1('一級標題'id='demo1'),
            html.H2('二級標題'),
            html.H3('三級標題'),
            html.H4('四級標題'),
            html.H5('五級標題'),
            html.H6('六級標題'),
            html.Br()# 換行
            html.Hr()# 水平分割線
            html.P('這是一段文字。'*20),
            html.P('這是另一段帶有首行縮進的文字。'*10, style={'text-indent''3rem'}),
            html.A('跳轉到費弗裏的Github倉庫',
                   target='_blank',
                   href='https://github.com/CNFeffery/DataScienceStudyNotes')# 跳轉到外部鏈接
            html.Br(),
            html.A('跳轉到六級標題'href='#demo2'),
            html.P(
                [
                    '一段文字中出現了',
                    html.I('斜體'),
                    ',以及代碼片段',
                    html.Code('import dash'),
                    ',還有一段',
                    html.U('帶下劃線的文字'),
                    ',一段',
                    html.Mark('高亮標註文字'),
                    ',以及另一段',
                    html.Mark('不同顏色的高亮標註文字。'style={'background-color''lightblue'})
                 ]
            )
        ] + [html.Br()] * 50 + [html.A('回到頂端一級標題'href='#demo1'),
                                html.H1('頁內元素跳轉示例標題'id='demo2')]
    )
)


if __name__ == '__main__':
    app.run_server(debug=True)

圖 3

2.1.2 與內容組織相關的常用部件

前面我們針對常用的一些與文字格式相關的靜態部件進行了介紹,而在實際應用中我們不僅要展示文字內容,還需要展示圖片、音頻、視頻等多媒體內容,下面我們來學習如何在Dash中構造更加豐富的內容展示形式:

利用dash_html_components中的Blockquote(),我們可以直接傳入字符串,或嵌套其他元素,從而構造出塊引用,就像markdown中的>所包含渲染的內容那樣,參考下面的例子:

app3.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        html.Blockquote(
            html.P('這是一段由塊引用包裹的文字內容' * 10),
            style={
                'background-color''rgba(211, 211, 211, 0.25)',
                'text-indent''3rem'
            }
        )
    )
)

if __name__ == "__main__":
    app.run_server(debug=True)

圖 4

利用Ol()嵌套多個Li(),可以自動渲染出帶序號的有序列表,就像下面這個簡單的例子:

app4.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        html.Ol(
            [
                html.Br(),
                html.Br(),
                html.Li('待辦事項1'),
                html.Li('待辦事項2'),
                html.Li('待辦事項3'),
                html.Li('待辦事項4')
            ]
        )
    )
)

if __name__ == "__main__":
    app.run_server(debug=True)

圖 5

而除了與Ol()相互配合之外,Li()還可以嵌套在Ul()中渲染帶層級關係的列表:

app5.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        html.Ul(
            [
                html.Br(),
                html.Br(),
                html.Li('1'),
                html.Li('2'),
                html.Ul(
                    [
                        html.Li('2.1'),
                        html.Li('2.2'),
                        html.Li('2.3'),
                        html.Ul(
                            [
                                html.Li('2.1.1'),
                                html.Li('2.1.2'),
                                html.Li('2.1.3'),
                            ]
                        )
                    ]
                ),
                html.Li('3'),
                html.Li('4')
            ]
        )
    )
)

if __name__ == "__main__":
    app.run_server(debug=True)

圖 6

Img()等價於html中的img標籤,我們通過src參數傳入圖片地址來渲染出圖片,以我以前一篇博客的作品圖片爲例:

app6.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            html.H5('(在模仿中精進數據可視化05)疫情期間市值增長top25公司'),
            html.Img(src='https://img2020.cnblogs.com/blog/1344061/202011/1344061-20201129183046286-1089258422.png',
                     style={'width''100%'})
        ]
    )
)

if __name__ == "__main__":
    app.run_server(debug=True)

圖 7

利用Audio()Video(),我們可以通過參數src傳入對應音頻與視頻文件的 url 地址,從而實現在網頁中嵌入音頻與視頻,其中參數controls必須設置爲True否則不會正常渲染:

app7.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            html.H5('音頻示例:'),
            html.Audio(src='https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3',
                       controls=True),
            html.H5('視頻示例:'),
            html.Video(src='https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4',
                       controls=True,
                       style={'width''100%'}),
        ]
    )
)

if __name__ == "__main__":
    app.run_server(debug=True)

圖 8

類似iframe標籤,我們也可以利用Iframe()來在網頁中嵌入其他網頁,可以通過src參數直接傳入目標網頁 url,也可以通過srcDoc參數傳入整個網頁的 html 源碼字符串:

app8.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            html.Iframe(src='https://www.baidu.com/',
                        style={'width''100%''height''800px'})
        ]
    )
)

if __name__ == "__main__":
    app.run_server(debug=True)

圖 9

有時候我們需要構造出一個能供用戶輸入大段文字的輸入框,譬如很多的在線編輯器,而在Dash中我們可以使用dash_core_components中的Textarea()來實現這個功能,並且dcc.Textarea()同樣具有valueplaceholder屬性,可以配合回調函數實現很多功能。

譬如下面的例子中我們編寫了一個簡單的髒話和諧工具,會將用戶輸入的所有他媽替換爲 “**”😁:

app9.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc
import dash_core_components as dcc
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            html.Br(),
            dcc.Textarea(style={'width''100%''height''300px'},
                         id='input',
                         value='',
                         placeholder='請輸入文字內容!'),
            html.P(id='output')
        ]
    )
)

@app.callback(
    Output('output''children'),
    Input('input''value')
)
def mask_dirty_talk(value):

    return value.replace('他媽''**')


if __name__ == "__main__":
    app.run_server(debug=True)

圖 10

2.2 dcc.Markdown()——Dash 中特殊的靜態部件

Dash中還存在一個比較特別的用於呈現靜態內容的部件——dcc.Markdown(),它的children參數接受markdown代碼,並自動在網頁中呈現出渲染後的效果,其主要參數如下:

「children」:字符型markdown源碼

「dangerously_allow_html」:bool 型,用於設置是否允許解析出markdown源碼中的 html 代碼並渲染,默認爲 False 即不進行渲染

「dedent」:bool 型,用於設置是否忽略每行文字開頭的代碼,默認爲 True

效果如下:

app10.py

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            dcc.Markdown('''
> 本文示例代碼已上傳至我的`Github`倉庫[https://github.com/CNFeffery/DataScienceStudyNotes](https://github.com/CNFeffery/DataScienceStudyNotes)

# 1 簡介

   這是我的系列教程**Python+Dash快速web應用開發**的第五期,在上一期的文章中,我們針對`Dash`中有關回調的一些技巧性的特性進行了介紹,使得我們可以更愉快地爲`Dash`應用編寫回調交互功能。

  而今天的文章作爲**回調交互**系統性內容的最後一期,我將帶大家get一些`Dash`中實際應用效果驚人的**高級回調特性**,繫好安全帶,我們起飛~

<p align="center"><img src="https://img2020.cnblogs.com/blog/1344061/202102/1344061-20210207194037614-808613819.png" style="zoom:100%;" /></p>

''',
                         dangerously_allow_html=True,
                         dedent=False)
        ]
    )
)

if __name__ == "__main__":
    app.run_server(debug=True)

圖 11

有了Markdown()部件的加持,我們就可以在某些情況下直接利用markdown快速編寫網頁,譬如編寫在線文檔說明頁面~

3 利用 Dash 自制在線 Markdown 編輯器

在掌握了今天的教程所涉及知識之後,我們就可以自己動手書寫一些具有實際交互功能的界面,譬如自制一個在線 Markdown 編輯器。

思路很簡單,利用今天所學的Textarea()部件的value屬性作爲回調的Input(),再將Markdown()部件的children元素作爲回調的Output(),再略微美化一下佈局,便實現瞭如下的效果~

圖 12

對應的代碼如下:

app11.py

import dash
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        dbc.Row(
            [
                dbc.Col(
                    dcc.Textarea(
                        id='md-input',
                        placeholder='請輸入你的markdown源碼!',
                        style={
                            'width''100%',
                            'height''100%'
                        }
                    ),
                    width=6,
                    style={
                        'padding-right': 0,
                        'border''border:5px solid red'
                    }
                ),
                dbc.Col(
                    dcc.Markdown(id='md-output',
                                 dangerously_allow_html=True,
                                 style={
                                     'position''absolute',
                                     'width''100%',
                                     'height''100%'
                                 }),
                    width=6,
                    style={
                        'position''relative',
                        'overflow''auto',
                        'padding-left'0
                    }
                ),
            ],
            style={
                'position''fixed',
                'top': 0,
                'bottom': 0,
                'left': 0,
                'right'0
            }
        )
    ),
    style={
        'font-size''2rem'
    }
)

@app.callback(
    Output('md-output''children'),
    Input('md-input''value')
)
def online_markdown(raw_text):
    return raw_text

if __name__ == '__main__':
    app.run_server(debug=True)

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/B1PMlshXkF9jkBfgWUCeqg