streamlit如何實(shí)現(xiàn)多頁(yè)面

0 引入

我們?cè)?a href="http://www.reibang.com/p/abd05b381a23" target="_blank">streamlit搭建ML前端demo這篇文章里簡(jiǎn)單介紹了streamlit這個(gè)搭建前端頁(yè)面的神器堂竟。我們可以通過python腳本編寫實(shí)現(xiàn)一個(gè)簡(jiǎn)單的展示頁(yè)面忆肾。那如果你同時(shí)有N個(gè)頁(yè)面想要去展示泡态,應(yīng)該怎么樣去做呢蕊连?今天就探討一下怎樣利用streamlit實(shí)現(xiàn)多頁(yè)面展示访忿。

1 腦洞大開

我在這里列了幾種可以實(shí)現(xiàn)多頁(yè)面展示的方法菩彬,供大家參考潮梯。

  1. 通過server.port參數(shù)指定運(yùn)行端口骗灶,同時(shí)開多個(gè)頁(yè)面。
  2. 多個(gè)頁(yè)面合并在一個(gè)頁(yè)面內(nèi)秉馏,通過某個(gè)組件實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)耙旦。
  3. 看看有沒有開源工具包可以用,避免重復(fù)造輪子萝究。
    下面就來講下這幾種實(shí)現(xiàn)方法免都。

2 多頁(yè)面實(shí)現(xiàn)

2.1 同時(shí)開多個(gè)頁(yè)面

我們之前在streamlit搭建ML前端demo里介紹過,streamlit運(yùn)行的時(shí)候是通過命令行執(zhí)行服務(wù)啟動(dòng)命令的帆竹。運(yùn)行命令如下

streamlit run your_script.py [-- script args]

注意:這里傳入自己需要用到的參數(shù)時(shí)绕娘,必須使用雙橫線,否則參數(shù)就被視為streamlit的參數(shù)栽连,而非你腳本的參數(shù)险领。

也可以傳入配置選項(xiàng)來修改應(yīng)用端口绢陌、禁用自動(dòng)保存等特性。要查看可用選型熔恢,運(yùn)行 如下命令:

streamlit run --help

執(zhí)行后的結(jié)果如下圖所示:


配置選項(xiàng)

這里的--global.disableWatchdogWarning就是streamlit的一個(gè)配置選項(xiàng)脐湾,類型是BOOLEAN,下邊是對(duì)這個(gè)參數(shù)的描述叙淌。

那我們可以找到--server.port這個(gè)參數(shù)秤掌,它是INTEGER類型的愁铺。運(yùn)行不同app.py腳本的時(shí)候,指定一下這個(gè)參數(shù)即可椒拗。

streamlit run app.py --server.port 5002

就可以看到這個(gè)頁(yè)面是在5002端口啟動(dòng)的。


server.port參數(shù)

2.2 多頁(yè)面跳轉(zhuǎn)

我主要參考了這個(gè)github提到的多頁(yè)面配置辦法获黔。通過新建一個(gè)MultiApp類蚀苛,做多個(gè)頁(yè)面的添加和運(yùn)行,這樣只需要在一個(gè)端口運(yùn)行服務(wù)玷氏。

那如何在不同頁(yè)面之間跳轉(zhuǎn)呢堵未?那就需要用到streamlit內(nèi)置的一些前端組件了,如st.sidebar.selectbox(顯示列表選擇框)或者st.sidebar.radio(顯示單選框)盏触。就可以實(shí)現(xiàn)多個(gè)頁(yè)面之間跳轉(zhuǎn)了渗蟹。

來,上代碼赞辩,咱們學(xué)習(xí)下人家的idea雌芽。

import streamlit as st

class MultiApp:
    """Framework for combining multiple streamlit applications.
    Usage:
        def foo():
            st.title("Hello Foo")
        def bar():
            st.title("Hello Bar")
        app = MultiApp()
        app.add_app("Foo", foo)
        app.add_app("Bar", bar)
        app.run()
    It is also possible keep each application in a separate file.
        import foo
        import bar
        app = MultiApp()
        app.add_app("Foo", foo.app)
        app.add_app("Bar", bar.app)
        app.run()
    """
    def __init__(self):
        self.apps = []

    def add_app(self, title, func):
        """Adds a new application.
        Parameters
        ----------
        func:
            the python function to render this app.
        title:
            title of the app. Appears in the dropdown in the sidebar.
        """
        self.apps.append({
            "title": title,
            "function": func
        })

    def run(self):
        app = st.sidebar.radio(
            'Go To',
            self.apps,
            format_func=lambda app: app['title'])

        app['function']()

其實(shí)很簡(jiǎn)單,去除注釋辨嗽,代碼沒幾行世落。主要實(shí)現(xiàn)的功能是,通過一個(gè)列表apps存儲(chǔ)不同的頁(yè)面糟需,利用st.sidebar.radio選擇title對(duì)應(yīng)的頁(yè)面function并執(zhí)行屉佳。每個(gè)頁(yè)面有一個(gè)title和一個(gè)function組成,這里的function是每個(gè)頁(yè)面上需要展示的內(nèi)容洲押。

那我們自己運(yùn)行一下他給的例子吧武花,代碼如下。

def foo():
    st.title("Hello Foo")
def bar():
    st.title("Hello Bar")
app = MultiApp()
app.add_app("Foo", foo)
app.add_app("Bar", bar)
app.run()

執(zhí)行下杈帐,默認(rèn)是選擇了第一個(gè)頁(yè)面Foo体箕,咱們點(diǎn)擊第二個(gè)Bar,也能跳轉(zhuǎn)到第二個(gè)頁(yè)面上去挑童。


第一個(gè)頁(yè)面
第二個(gè)頁(yè)面

再來點(diǎn)擊下第一個(gè)Foo干旁,結(jié)果發(fā)現(xiàn),哎炮沐,頁(yè)面展示的不對(duì)勁争群。怎么內(nèi)容還停留在第二個(gè)頁(yè)面呢。


再次點(diǎn)擊第一個(gè)頁(yè)面

再去命令行里邊看看大年,里邊也有報(bào)錯(cuò)换薄。報(bào)錯(cuò)內(nèi)容如下:

報(bào)錯(cuò)信息

去github看看有沒有issue玉雾,還真找到一個(gè)同樣的問題∏嵋看里邊作者提供的解決方案是复旬,重寫了run()函數(shù),重寫后的代碼內(nèi)容如下冲泥。

    def run(self):
        app_state = st.experimental_get_query_params()
        app_state = {k: v[0] if isinstance(v, list) else v for k, v in app_state.items()} # fetch the first item in each query string as we don't have multiple values for each query string key in this example
        
        #st.write('before', app_state)
        
        titles = [a['title']  for a in self.apps]
        functions = [a['function'] for a in self.apps]
        default_radio = titles.index(app_state["page"]) if "page" in app_state else 0

        title = st.sidebar.radio(
            'Go To',
            titles,
            index=default_radio,
            key='radio')

        app_state['page'] = st.session_state.radio
        #st.write('after', app_state)

        st.experimental_set_query_params(**app_state)
        functions[titles.index(title)]()

看起來就麻煩一些了驹碍,需要對(duì)每次的app狀態(tài)參數(shù)page做一個(gè)獲取,然后跳轉(zhuǎn)到一個(gè)頁(yè)面后及時(shí)更新這個(gè)page參數(shù)凡恍。

那還有沒有更簡(jiǎn)單的操作呢志秃?筆者看報(bào)錯(cuò)信息發(fā)現(xiàn),在點(diǎn)擊Foo的時(shí)候嚼酝,報(bào)的是Bar這個(gè)dict不在列表里浮还,難道是列表在點(diǎn)擊的時(shí)候有啥變化?

那咱們?cè)?code>app['function']()這行之前加個(gè)print函數(shù)闽巩,打印一下self.apps看看吧钧舌。結(jié)果真的發(fā)現(xiàn)了問題,如下圖所示涎跨。每次點(diǎn)擊一個(gè)頁(yè)面洼冻,self.apps里邊的function函數(shù)在內(nèi)存里邊就變了,這顯然是個(gè)小bug隅很。

打印結(jié)果

那么碘赖,我們就不要這么存了就行了呀,列表里只存title外构,function根據(jù)點(diǎn)擊的title去獲取普泡,問題迎刃而解。自己嘗試了一種新的代碼寫法审编。

import streamlit as st

class MultiApp:
    def __init__(self):
        self.apps = []
        self.app_dict = {}

    def add_app(self, title, func):
        if title not in self.apps:
            self.apps.append(title)
            self.app_dict[title] = func

    def run(self):
        title = st.sidebar.radio(
            'Go To',
            self.apps,
            format_func=lambda title: str(title))
        self.app_dict[title]()

再去執(zhí)行一下看看撼班,結(jié)果正常,不會(huì)再報(bào)錯(cuò)了垒酬。

2.3 開源工具包

在谷歌上搜了下砰嘁,還真有開源的工具包,streamlit-multipage勘究。項(xiàng)目地址在這里矮湘,支持pip安裝。
項(xiàng)目支持的功能比較多口糕,除了單頁(yè)面缅阳、多頁(yè)面之外,還可以自定義命名空間景描、自定義UI等等十办。缺點(diǎn)就是秀撇,你得花時(shí)間熟悉別人的代碼思路,改起來會(huì)有點(diǎn)費(fèi)勁向族。其中多頁(yè)面部分代碼如下呵燕。

import streamlit as st
from streamlit_multipage import MultiPage


def input_page(st, **state):
    st.title("Body Mass Index")

    weight_ = state["weight"] if "weight" in state else 0.0
    weight = st.number_input("Your weight (Kg): ", value=weight_)

    height_ = state["height"] if "height" in state else 0.0
    height = st.number_input("Your height (m): ", value=height_)

    if height and weight:
        MultiPage.save({"weight": weight, "height": height})


def compute_page(st, **state):
    st.title("Body Mass Index")

    if "weight" not in state or "height" not in state:
        st.warning("Enter your data before computing. Go to the Input Page")
        return

    weight = state["weight"]
    height = state["height"]

    st.metric("BMI", round(weight / height ** 2, 2))

app = MultiPage()
app.st = st

app.add_app("Input Page", input_page)
app.add_app("BMI Result", compute_page)

app.run()

3 總結(jié)

本文主要介紹了如何利用streamlit實(shí)現(xiàn)多個(gè)前端頁(yè)面的幾種方法。由易到難的介紹了改端口件相、封裝類再扭、引入開源包的思路。希望對(duì)大家有所幫助夜矗。

4 參考文獻(xiàn)

  1. streamlit開發(fā)手冊(cè)
  2. streamlit-multiapps
  3. Streamlit Multipage
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泛范,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子侯养,更是在濱河造成了極大的恐慌,老刑警劉巖澄干,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逛揩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡麸俘,警方通過查閱死者的電腦和手機(jī)辩稽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來从媚,“玉大人逞泄,你說我怎么就攤上這事“菪В” “怎么了喷众?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)紧憾。 經(jīng)常有香客問我到千,道長(zhǎng),這世上最難降的妖魔是什么赴穗? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任憔四,我火速辦了婚禮,結(jié)果婚禮上般眉,老公的妹妹穿的比我還像新娘了赵。我一直安慰自己,他們只是感情好甸赃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布柿汛。 她就那樣靜靜地躺著,像睡著了一般埠对。 火紅的嫁衣襯著肌膚如雪苛茂。 梳的紋絲不亂的頭發(fā)上已烤,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音妓羊,去河邊找鬼胯究。 笑死,一個(gè)胖子當(dāng)著我的面吹牛躁绸,可吹牛的內(nèi)容都是我干的裕循。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼净刮,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼剥哑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起淹父,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤株婴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后暑认,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體困介,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年蘸际,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了座哩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粮彤,死狀恐怖根穷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情导坟,我是刑警寧澤屿良,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站惫周,受9級(jí)特大地震影響管引,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闯两,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一褥伴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧漾狼,春花似錦重慢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春核芽,著一層夾襖步出監(jiān)牢的瞬間囚戚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工轧简, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驰坊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓哮独,卻偏偏與公主長(zhǎng)得像拳芙,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子皮璧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容