Примеры компонентов Bootstrap для отзывчивых панелей мониторинга
Почему адаптивный дизайн
Мобильный просмотр огромен, на него приходится примерно половина мирового веб-трафика, и он, вероятно, останется одним из самых быстрорастущих способов использования веб-медиа. Поскольку мобильный просмотр широко распространен, важно понимать, понадобится ли пользователям ваших информационных панелей адаптивный дизайн. Например, на работе я создаю информационные панели для деловых людей, которые путешествуют и взаимодействуют с клиентами. Мне нужно убедиться, что визуализация и навигация не сломаются, если моим пользователям потребуется отображать данные со своего телефона или планшета.
Хотя я знаю достаточно CSS, чтобы стилизовать простой веб-сайт, я считаю создание таблиц стилей немного утомительным. Dash упрощает применение ваших собственных таблиц стилей, если вы умеете их создавать; однако, если вы не знакомы с CSS или не хотите тратить время на создание своего собственного, Dash может использовать Bootstrap CSS, который упрощает объединение стиля и макета страницы. Bootstra - одна из самых популярных в мире интерфейсных сред для создания адаптивных, удобных для мобильных устройств сайтов.
Знакомство с CSS и Dash
В этой статье я рассмотрю основы макета CSS-сетки Bootstrap и рассмотрю некоторые интересные dash-bootstrap-components и обратные вызовы, такие как Accordion , Modal и Jumbotron .
Dash - это фреймворк для Python, написанный поверх Flask, Plotly.js и React.js. Приложения Dash состоят из макетов и обратных вызовов:
Макет
Макет состоит из дерева компонентов, которые описывают, как выглядит приложение и как пользователи воспринимают контент.
Обратные вызовы
Обратные вызовы делают приложения Dash интерактивными. Обратные вызовы - это функции Python, которые автоматически вызываются при изменении свойства input.
Если вы не знакомы с Dash или нуждаетесь в быстром обновлении, ознакомьтесь с моими вводными статьями. Весь код доступен в конце статьи!
Что такое CSS?
Каскадные таблицы стилей, или CSS, обычно представляют собой внешние файлы, которые могут описывать, как элементы HTML на вашем веб-сайте отображаются на устройствах. Таблицы стилей полезны, потому что они могут влиять на несколько страниц одновременно, тем самым экономя много времени и усилий, если на веб-сайте несколько страниц. CSS позволяет мне по существу определять стиль моих информационных панелей, и это влияет на макет на экранах разных размеров.
В традиционном файле CSS вы увидите подобный синтаксис, описывающий, как должны выглядеть элементы HTML:
Обратите внимание, как таблица стилей описывает стиль и цвета для H1, H2 и P HTML теги. Таблицы стилей можно импортировать в Dash.
Загрузочный CSS в Dash
Подобно тому, как библиотека dash-html-components позволяет применять HTML с помощью Python, библиотека dash-bootstrap-components позволяет применять Начальные компоненты начальной загрузки, на которые влияет CSS-фреймворк Bootstrap. Процитируем документацию:
dash-bootstrap-components полагается на Twitter Bootstrap. Чтобы использовать этот пакет, вставьте таблицу стилей Bootstrap в свое приложение. Для удобства ссылки на Bootstrap CSS, размещенные на bootstrapcdn, включены как часть модуля
themes
Чтобы настроить приложение для запуска с использованием Bootstrap CSS, вам необходимо включить этот фрагмент стандартного кода. Обратите внимание, что здесь устанавливается внешняя таблица стилей. Поскольку я использую Bootstrap CSS, я установил его с помощью [dbc.themes.BOOTSTRAP].
app = dash.Dash(__name__, external_stylesheets =[dbc.themes.BOOTSTRAP])
Установка и зависимости
Легко установите dash-bootstrap-components с помощью pip или Anaconda:
pip install dash-bootstrap-components
ORconda install -c conda-forge dash-bootstrap-components
При импорте зависимостей включите библиотеку dash_bootstrap_components и присвойте ей псевдоним dbc.
import dash import dash_bootstrap_components as dbc import dash_core_components as dcc import dash_html_components as html
Изучение компонентов макета Bootstrap
На мой взгляд, Компоненты компоновки - это самое большое преимущество использования библиотеки, но после обзора сетки я покажу несколько других интересных компонентов.
Адаптивная сетка и удобные оболочки контейнера допускают множество настроек. Сетка Bootstrap использует серию контейнеров, строк и столбцов для компоновки и выравнивания содержимого. Они были включены в качестве компонентов в библиотеку dash-bootstrap-components как Container
, Row
и Col
.
Компонент
Row
- это оболочка для столбцов. Макет вашего приложения должен быть построен как ряд строк столбцов.
При использовании макета сетки содержимое следует размещать в столбцах, и только компоненты
Col
должны быть непосредственными дочерними элементамиRow
.
Сетка Bootstrap содержит 12 столбцов и пять уровней отзывчивости. Визуализировать столбцы легко с помощью компонента dbc.Alert ().
import dash import dash_bootstrap_components as dbc import dash_core_components as dcc import dash_html_components as html app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) body = html.Div([ html.H1("Bootstrap Grid System Example") , dbc.Row(dbc.Col(html.Div(dbc.Alert("This is one column", color="primary")))) , dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of three columns", color="primary"))) , dbc.Col(html.Div(dbc.Alert("One of three columns", color="primary"))) , dbc.Col(html.Div(dbc.Alert("One of three columns", color="primary"))) ]) ]) app.layout = html.Div([body]) if __name__ == "__main__": app.run_server(debug = True)
Обратите внимание, что тело построено с использованием серии строк и столбцов, как это предлагается в документации. Обратите внимание: dbc.Col является непосредственным дочерним элементом dbc.Row, но за dbc.Col следует знакомый Dash HTML компоненты.
По умолчанию столбец расширяется и занимает весь экран. Чтобы лучше контролировать расширение столбцов, попробуйте обернуть сетку в dbc.Container или использовать аргумент width, чтобы присвоить ширину колонка.
Обратите внимание, что между столбцами есть зазор. Эти пробелы называются Промежуточными полями и являются частью компонента dbc.Row. Чтобы удалить желоба, установите no_gutters = True.
Ниже приведен более подробный пример того, как можно стилизовать сетку. Поскольку строки составляют 12 столбцов, проще всего использовать числа с коэффициентом 12 при работе с несколькими столбцами в строке. Как видно из примера, я использую несколько комбинаций размеров столбцов.
body = html.Div([html.H1("Bootstrap Grid System Example") , html.H4("no_gutters = False") , dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3) ]) , html.H4("no_gutters = True") , dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3) ], no_gutters = True) , html.H3("Examples of justify property") , html.H4("start, center, end, between, around") , dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of two columns")), width=4), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=4), ], justify="start") , dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), ], justify="center") , dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of three columns")), width=3) , dbc.Col(html.Div(dbc.Alert("One of three columns")), width=3) , dbc.Col(html.Div(dbc.Alert("One of three columns")), width=3) ], justify="end") , dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3) , dbc.Col(html.Div(dbc.Alert("One of three columns")), width=3) ], justify="between") , dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of two columns")), width=4), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=4), ], justify="around") , html.H4("Container Example") , dbc.Container([ dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3) ]) , html.H4("no_gutters = True") , dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3), dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3) ], no_gutters = True) ]) ])
Обратите внимание, что желоба можно легко удалить, а строки можно выровнять.
Обратите внимание, как пример контейнера по умолчанию центрирует и сжимает столбцы.
Помимо использования аргумента width, настройте размер, порядок и смещение ваших столбцов на различных устройствах с помощью xs
, sm
, md
, lg
и xl
аргументы ключевого слова.
body = html.Div([ html.H1("Bootstrap Grid System Example") , dbc.Row(dbc.Col(html.Div(dbc.Alert("This is one column", color="primary")))) , dbc.Row([ dbc.Col(html.Div(dbc.Alert("One of three columns", color="primary")), lg=3, md=4, xs=12) , dbc.Col(html.Div(dbc.Alert("One of three columns", color="primary")), lg=3, md=4, xs=12) , dbc.Col(html.Div(dbc.Alert("One of three columns", color="primary")), lg=3, md=4, xs=12) ]) ])
Обратите внимание, что размер столбца меняется по мере уменьшения размеров экранов. Например, использование xs = 12 приводит к тому, что столбец занимает всю строку, когда экран слишком мал, поскольку система сетки состоит из 12 столбцов.
Вкратце, в этом заключается простота и мощность адаптивной сеточной системы Bootstrap. С помощью нескольких строк кода можно создать адаптивный макет, который будет работать на экранах разных размеров. Сетка легко применяется к существующим панелям мониторинга.
Улучшение приборной панели
В предыдущей статье я объяснил, как получить данные из Reddit и отобразить их с помощью библиотеки компонентов Таблица данных в Dash. Чтобы дать более реалистичные примеры применения компонентов Bootstrap, я применю их к панели управления Reddit. Если вы уже знакомы с Reddit, я включаю в эту статью весь приведенный ниже стартовый код; в противном случае обратитесь к предыдущей статье для получения инструкций по созданию ключей Reddit API!
Я объясню, как преобразовать исходную панель Reddit Dashboard в изображение ниже с помощью Bootstrap CSS. Я добавлю Jumbotron, C ollapse Accordion и Modal, который генерирует всплывающее окно, когда Создать графики нажата кнопка:
Стартовый код
Это начальный код, который предоставит вам основную панель управления Reddit. Предполагается, что вы используете файл конфигурации для передачи ключей API в код. По умолчанию используется subreddit wallstreetbets, поэтому обновите это значение, если хотите, чтобы оно по умолчанию было другим.
import dash import dash_html_components as html import dash_core_components as dcc import dash_table import pandas as pd import praw import pandas as pd from dash.dependencies import Input, Output, State from dash.exceptions import PreventUpdate from config import cid, csec, uag reddit = praw.Reddit(client_id= cid, client_secret= csec, user_agent= uag) posts = [] new_bets = reddit.subreddit('wallstreetbets').new(limit=100) for post in new_bets: posts.append([post.title, post.score, post.num_comments, post.selftext, post.created, post.pinned, post.total_awards_received]) posts = pd.DataFrame(posts,columns=['title', 'score', 'comments', 'post', 'created', 'pinned', 'total awards']) def get_features(dataframe): df = posts.copy() df['words'] = df['post'].apply(lambda x : len(x.split())) df['chars'] = df['post'].apply(lambda x : len(x.replace(" ",""))) df['word density'] = (df['words'] / (df['chars'] + 1)).round(3) df['unique words'] = df['post'].apply(lambda x: len(set(w for w in x.split()))) df['unique density'] = (df['unique words'] / df['words']).round(3) return df df = get_features(posts) app.layout = html.Div([ html.P(html.Button('Refresh', id='refresh')) ,html.P(html.Div(html.H3('Enter Subreddit'))) ,dcc.Input(id='input-1-state', type='text', value='wallstreetbets') ,dash_table.DataTable( id='table' ,columns=[{"name": i, "id": i} for i in df.columns] ,fixed_rows={ 'headers': True, 'data': 0 } ,data=df.to_dict('records') ) ]) @app.callback(Output('table', 'data'), [Input('refresh', 'n_clicks')], [State('input-1-state', 'value') ]) def update_data(n_clicks, subreddits): dff = df if subreddits is None: subreddits = 'wallstreetbets' else: subreddits if n_clicks is None: raise PreventUpdate else: posts = [] new_bets = reddit.subreddit(subreddits).hot(limit=100) for post in new_bets: posts.append([post.title, post.score, post.num_comments, post.selftext, post.created, post.pinned, post.total_awards_received]) posts = pd.DataFrame(posts,columns=['title', 'score', 'comments', 'post', 'created', 'pinned', 'total awards']) dff = get_features(posts) return dff.to_dict('records')
Добавление Jumbotron
Jumbotron - легкий компонент, который помогает контенту выделяться. Он занимает много места на экране, поэтому отлично подходит для отображения кнопок и сообщений. Джамботрон - это большая серая рамка, окружающая поле ввода и кнопку.
Вот часть кода, изменяемая для компонента Bootstrap:
# Original html.Div([ html.P(html.Button('Refresh', id='refresh')) ,html.P(html.Div(html.H3('Enter Subreddit'))) ,dcc.Input(id='input-1-state', type='text', value='wallstreetbets') ])
Я буду использовать dbc.Jumbotron, чтобы обернуть html.Div, который содержит компоненты ввода и кнопки. Жирным шрифтом выделен исходный код.
dbc.Jumbotron([ dbc.Row(dbc.Col([ html.P(html.Div(html.H3('Enter Subreddit'))) , dcc.Input(id='input-1-state', type='text', value='wallstreetbets') , html.P(html.Button('Generate Graphs', id='refresh')) ], width = 6) , justify = 'center')# end row ], fluid = True)
Обратите внимание, что dbc.Jumbotron оборачивает dbc.Row и dbc.Col, поэтому я могу воспользоваться преимуществами ширины и обосновать аргументы.
Обратите внимание, что я выравниваю содержимое по центру, чтобы его было легче заметить.
Обратите внимание, что я установил fluid = True аргумент в пользу Jumbotron. Жамботрон будет иметь полную ширину и квадратные углы.
Добавление модального окна
Модальные окна - еще один полезный инструмент для привлечения внимания к сообщениям или функциям. Используйте компонент dbc.Modal для добавления пользовательских уведомлений или настраиваемого содержимого, которое появляется после того, как пользователь выполняет действие. Например, когда пользователь нажимает кнопку Создать графики, Modal создает всплывающее окно, которое позволяет пользователю узнать, что графики были созданы.
Модальное окно помещается в Jumbotron. Я его туда положил, так как он взаимодействует с кнопкой.
dbc.Jumbotron([ dbc.Row(dbc.Col([ html.P(html.Div(html.H3('Enter Subreddit'))) , dcc.Input(id='input-1-state', type='text', value='wallstreetbets') , html.P(html.Button('Generate Graphs', id='refresh')) , dbc.Modal( [ dbc.ModalHeader("Generating"), dbc.ModalBody("The graphs have generated"), dbc.ModalFooter( dbc.Button("Close", id="close", className="ml-auto") ), ], id="modal", ) ], width = 6) , justify = 'center')# end row ], fluid = True)
Обратите внимание, что модальный создается с использованием компонентов Header, Body и Footer. По умолчанию модальное окно можно закрыть, щелкнув за пределами модального окна или нажав клавишу escape.
Размер модального окна можно настроить с помощью свойства размер. Возможные варианты: sm
, lg
или xl
для маленького, большого или очень большого модального окна.
Для работы модальное окно использует обратный вызов. Используя обратные вызовы, можно установить для свойства is_open значение Истина или Ложь. , в зависимости от того, как я хочу его отображать.
@app.callback( Output("modal", "is_open"), [Input("open", "n_clicks"), Input("close", "n_clicks")], [State("modal", "is_open")], ) def toggle_modal(n1, n2, is_open): if n1 or n2: return not is_open return is_open
Добавление аккордеона с свертыванием
Используя компонент dbc.Collapse, можно переключать видимость контента. Комбинируя компоненты Свернуть и Карточка, вы можете расширить стандартное поведение свертывания, чтобы создать гармошку.
Создать аккордеон сложно, потому что Dash выдаст ошибку, если компоненты имеют одинаковый идентификатор. Таким образом, с помощью функции я могу программно создавать уникальные идентификаторы для компонентов Свернуть и Карточка в аккордеоне. Это также затрудняет заполнение карточек в аккордеоне, но я использую список в функции, чтобы упростить логику пополнения!
def make_item(i): # we use this function to make the example items to avoid code duplication graph_info = [0,'Displays words on x axis vs score on y axis.' , 'Displays words on x axis vs word density on y axis.' , 'Displays words on x axis vs unique density on y axis.'] return dbc.Card( [ dbc.CardHeader( html.H2( dbc.Button( f"Graph {i} Info", color="link", id=f"group-{i}-toggle", ) ) ), dbc.Collapse( dbc.CardBody(graph_info[i]), id=f"collapse-{i}", ), ] )
Функция make_item (i) принимает числовое значение и выводит компонент dbc.Card, который является оболочкой для Collapse используется для создания эффекта аккордеона.
Обратите внимание на список graph_info. Это список, который я использую для заполнения содержимого dbc.CardBody, поскольку я могу передать соответствующее числовое значение в индекс списка. Например, если я передаю 1 в make_item (1), я верну значение с индексом 1 в моем списке graph_info. Элемент в первом индексе следующий:
Displays words on x axis vs score on y axis.
Конструирование аккордеона
Чтобы построить аккордеон, я вызываю функции и заключаю их в компонент html.Div.
accordion = html.Div([make_item(1), make_item(2), make_item(3)], className="accordion")
Я добавляю его в основной список html.Div между jumbotron и таблицей данных.
Аккордеонный обратный звонок
Я взял шаблонный обратный вызов из документации. В зависимости от количества карточек в гармошке увеличьте диапазон для вывода, ввода и состояния.
@app.callback( [Output(f"collapse-{i}", "is_open") for i in range(1, 4)], [Input(f"group-{i}-toggle", "n_clicks") for i in range(1, 4)], [State(f"collapse-{i}", "is_open") for i in range(1, 4)], ) def toggle_accordion(n1, n2, n3, is_open1, is_open2, is_open3): ctx = dash.callback_context if not ctx.triggered: return "" else: button_id = ctx.triggered[0]["prop_id"].split(".")[0] if button_id == "group-1-toggle" and n1: return not is_open1, False, False elif button_id == "group-2-toggle" and n2: return False, not is_open2, False elif button_id == "group-3-toggle" and n3: return False, False, not is_open3 return False, False, False
Полная панель управления
После заполнения панели мониторинга легко увидеть, как Bootstrap CSS for Dash представляет собой мощную библиотеку компонентов, которая упрощает форматирование содержимого для просмотра на мобильных устройствах. Я прошелся по компонентам Адаптивная сетка, Jumbotron, Свернуть и Модальный, чтобы продемонстрировать некоторые из более продвинутых способов. Библиотека компонентов Bootstrap может повысить удобство использования приборной панели. Ниже приведен полный код панели управления. Ознакомьтесь с другими моими руководствами, если вы тоже новичок в программировании или науке о данных!
Благодарю вас!
- Если вам понравилось, подписывайтесь на меня на Medium, чтобы узнать больше
- Получите ПОЛНЫЙ ДОСТУП и помогите поддержать мой контент, подписавшись
- Давайте подключимся к LinkedIn
- Анализировать данные с помощью Python? Загляните на мой сайт
Код
Вот полный код. Помните, предполагается, что вы используете файл конфигурации для импорта ключей API.
import dash import dash_html_components as html import dash_core_components as dcc import dash_table import pandas as pd import praw import pandas as pd from dash.dependencies import Input, Output, State from dash.exceptions import PreventUpdate from config import cid, csec, uag reddit = praw.Reddit(client_id= cid, client_secret= csec, user_agent= uag) posts = [] new_bets = reddit.subreddit('wallstreetbets').hot(limit=100) for post in new_bets: posts.append([post.title, post.score , post.num_comments, post.selftext , post.created, post.pinned, post.total_awards_received]) posts = pd.DataFrame(posts,columns=['title', 'score', 'comments', 'post', 'created', 'pinned', 'total awards']) def get_features(dataframe): df = posts.copy() df['words'] = df['post'].apply(lambda x : len(x.split())) df['chars'] = df['post'].apply(lambda x : len(x.replace(" ",""))) df['word density'] = (df['words'] / (df['chars'] + 1)).round(3) df['unique words'] = df['post'].apply(lambda x: len(set(w for w in x.split()))) df['unique density'] = (df['unique words'] / df['words']).round(3) return df df = get_features(posts) def make_item(i): # we use this function to make the example items to avoid code duplication graph_info = [0,'Displays words on x axis vs score on y axis.' , 'Displays words on x axis vs word density on y axis.' , 'Displays words on x axis vs unique density on y axis.'] return dbc.Card( [ dbc.CardHeader( html.H2( dbc.Button( f"Graph {i} Info", color="link", id=f"group-{i}-toggle", ) ) ), dbc.Collapse( dbc.CardBody(graph_info[i]), id=f"collapse-{i}", ), ] ) accordion = html.Div( [make_item(1), make_item(2), make_item(3)], className="accordion" ) app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) app.layout = html.Div([ dbc.Jumbotron([ dbc.Row(dbc.Col([ html.P(html.Div(html.H3('Enter Subreddit'))) , dcc.Input(id='input-1-state', type='text', value='wallstreetbets') , html.P(html.Button('Generate Graphs', id='refresh')) , dbc.Modal( [ dbc.ModalHeader("Generating"), dbc.ModalBody("The graphs have generated"), dbc.ModalFooter( dbc.Button("Close", id="close", className="ml-auto") ), ], id="modal", ) ], width = 6) , justify = 'center')# end row ], fluid = True) , html.Div(dbc.Row(dbc.Col(accordion ,width = 8),justify = 'center')) , html.Div([ dbc.Row(dbc.Col(dash_table.DataTable( id='table' , columns=[{"name": i, "id": i} for i in df.columns] , data=df.to_dict('records') #, fixed_rows={ 'headers': True, 'data': 0 } , style_cell_conditional=[ {'if': {'column_id': 'title'}, 'width': '150px' ,'padding': '15px'}, {'if': {'column_id': 'post'}, 'width': '400px' } ,{'if': {'column_id': 'words'}, 'width': '65px' } ,{'if': {'column_id': 'chars'}, 'width': '65px' } ] ,style_cell={ 'overflowX': 'hidden', 'textOverflow': 'ellipsis', 'maxWidth': '25px', 'textAlign': 'left' } , style_table={ 'maxHeight': '550px' #,'maxWidth': '800px' ,'overflowY': 'scroll' ,'overflowX': 'hidden' } ), width = 10), justify = 'center')# end dt ], style = {'background-color': 'lightblue' #,'margin-bottom': '5px' , 'padding' : '50px' }) #end div , dbc.Container( html.Div(id = 'graph-container') ) ])#end div @app.callback( [Output(f"collapse-{i}", "is_open") for i in range(1, 4)], [Input(f"group-{i}-toggle", "n_clicks") for i in range(1, 4)], [State(f"collapse-{i}", "is_open") for i in range(1, 4)], ) def toggle_accordion(n1, n2, n3, is_open1, is_open2, is_open3): ctx = dash.callback_context if not ctx.triggered: return "" else: button_id = ctx.triggered[0]["prop_id"].split(".")[0] if button_id == "group-1-toggle" and n1: return not is_open1, False, False elif button_id == "group-2-toggle" and n2: return False, not is_open2, False elif button_id == "group-3-toggle" and n3: return False, False, not is_open3 return False, False, False @app.callback( Output("modal", "is_open"), [Input("refresh", "n_clicks"), Input("close", "n_clicks")], [State("modal", "is_open")], ) def toggle_modal(n1, n2, is_open): if n1 or n2: return not is_open return is_open @app.callback( Output('graph-container', "children"), [Input('table', "data")]) def update_graph(rows): dff = pd.DataFrame(rows) return html.Div( [ dcc.Graph( id=column, figure={ "data": [ { "x": dff["words"], "y": dff[column] if column in dff else [], "type": "bar", "marker": {"color": "#0074D9"}, } ], "layout": { "xaxis": {"automargin": True}, "yaxis": {"automargin": True}, "height": 250, "margin": {"t": 50, "l": 10, "r": 10}, "title": column }, }, ) for column in ["score", "word density", "unique density"] ] ) @app.callback(Output('table', 'data'), [Input('refresh', 'n_clicks')], [State('input-1-state', 'value') ]) def update_data(n_clicks, subreddits): dff = df if subreddits is None: subreddits = 'wallstreetbets' else: subreddits if n_clicks is None: raise PreventUpdate else: posts = [] new_bets = reddit.subreddit(subreddits).hot(limit=100) for post in new_bets: posts.append([post.title, post.score, post.num_comments, post.selftext, post.created, post.pinned, post.total_awards_received]) posts = pd.DataFrame(posts,columns=['title', 'score', 'comments', 'post', 'created', 'pinned', 'total awards']) dff = get_features(posts) return dff.to_dict('records') if __name__ == '__main__': app.run_server(debug=True)