Streamlit: برنامه ها را در پایتون بسیار ساده ایجاد کنید

مدتی است که یادگیری ویتنامی را شروع کرده ام. برای این کار، مانند بسیاری از افراد در حال حاضر، در برنامه Duolingo ثبت نام کردم. این برنامه واقعا بد نیست، یادگیری را سریع و سرگرم کننده می کند.
بله، اما مانند بسیاری از برنامه های کاربردی این روزها، Duolingo یک پیشنهاد Freemium ارائه می دهد و پتانسیل کامل آن تنها با یک اشتراک منتشر می شود. به طور خاص، یکی از ویژگی های جالبی که اشتراک ارائه می دهد، امکان انجام یک مسابقه ترجمه ساده ویتنامی / انگلیسی با تمام کلماتی که از شروع درس ها آموخته شده است. سپس از خودم پرسیدم “چگونه می توانم این تمرین را که در واقع بسیار ساده است، تکرار کنم تا بتوانم بدون پرداخت هزینه اشتراک Duolingo تمرین کنم؟”
در این مقاله سعی می کنم نحوه بازسازی این تمرین را با فریم ورک Streamlit و کمی کد به شما نشان دهم.
اول، داده ها
برای شروع، من به یک مجموعه داده اولیه برای انجام این پروژه نیاز دارم. این خوب است، Duolingo در وب سایت خود، تمام کلماتی را که از ابتدای درس هایم یاد گرفته ام، همراه با ترجمه انگلیسی آنها فهرست می کند (من فقط می توانم ویتنامی را از انگلیسی از برنامه یاد بگیرم).
یک کپی/پیست سریع لیست کلمات در یک فایل متنی (با عرض پوزش Duolingo…)، و در اینجا من با مجموعه داده کوچکی از 131 کلمه ویتنامی با ترجمه انگلیسی مربوطه هستم. هنوز هم باقی مانده است که همه چیز را با یک اسکریپت سریع پایتون قالب بندی کنم، و من یک JSON دارم که حاوی داده های اساسی بازی آینده من است.
{
"ترجمه ها": [
{
"VN": "ga",
"EN": "station, gas"
},
{
"VN": "trạm",
"EN": "station, stations"
},
{
"VN": "đặt chỗ",
"EN": "make a reservation, reservation"
},
...
Streamlit
Plusieurs choix s’offraient à moi pour créer mon application. Ce que je voulais, c’était avoir quelque chose de relativement simple à implémenter, pouvant être facilement déployé pour pouvoir y accéder partout, et apprendre quelque chose en travaillant sur ce projet. Du coup, quoi faire ? Faire un projet HTML/JS simple, essayer de me plonger dans un framework JS (React ou Vue.js), ou trouver une autre alternative ?
Le projet HTML/JS ? Oui, pourquoi pas, mais pas sûr d’en apprendre quelque chose d’intéressant.
Faire une application via un framework JS ? Oui, ça serait super, mais je connais ce genre de projet pour y avoir déjà goûté plusieurs fois… Ce sont de supers frameworks, mais il faut quand même apprendre à s’en servir (j’ai su faire du React il y a quelques années, mais ça évolue tellement vite qu’aujourd’hui je dois repartir de zéro) pour ne pas faire n’importe quoi, et il faut passer 2 semaines à apprendre un framework pour faire une application de quelques lignes qui prend 4 heures à implémenter.
De plus, je ne suis plus super à l’aise avec JS…
Alors pourquoi ne pas me tourner vers autre chose ? Je suis un pythonista dans mon quotidien, et j’ai entendu parler du framework Streamlit au travail. Il paraît que l’on peut faire des applications sympas avec. Alors, y jeter un coup d’œil 🕵️
Rapide focus sur le Framework
Allez sur le site de Streamlit, et vous verrez s’afficher devant vous : “A faster way to build and share data apps”. Ça semble être ce que l’on cherche 😄. Quelques minutes à parcourir la documentation me le confirment. C’est simple à installer, à utiliser, à déployer, et c’est du full Python. Voyons si ça peut faire l’affaire !
L’installation
L’installation du framework est assez simple et se réalise en quelques commandes :
mkdir translation-exercice-app
cd translation-exercice-app
# assurez vous d'avoir une version de python >= 3.8
python3 --version
python3 -m venv .venv
source .venv/bin/activate
pip install streamlit
Et c’est tout. Simple, non ? Pour la suite de l’article, la seule commande Streamlit que l’on utilisera sera streamlit run app.py
.
C’est parti pour l’implémentation
Avec votre éditeur favori, ouvrez le dossier translation-exercise-app
. Normalement, votre dossier .venv devrait déjà s’y trouver. Ajoutez le fichier JSON contenant le dataset initialement généré, puis créez un fichier app.py
.
# app.py
import streamlit as st
st.text("Hello World!")
Maintenant, utilisez la commande streamlit run app.py
, et une page de votre navigateur devrait s’ouvrir en affichant “Hello World!”. Première victoire facile 💪 !
Je ne vais pas vous expliquer un par un tous les composants Streamlit que je vais utiliser. Je préfère vous mettre les liens vers la documentation officielle, qui est bien meilleure que toutes mes explications pourront l’être. Je vais plutôt vous montrer comment je les utilise pour créer mon app d’exercice de traduction.
Un peu de design d’abord
Qu’est-ce que je veux faire exactement ?
Je me suis dit que, pour le moment, j’allais faire une app très simple. Son but sera d’afficher un mot en vietnamien pris dans le dataset de base, ainsi que 4 traductions anglaises, une correspondant à l’exacte traduction du mot vietnamien choisie, les 3 autres étant juste des mots anglais pris au hasard dans le reste du dataset. Le joueur devra trouver la bonne traduction anglaise parmi les 4 proposées. On aura un score affiché à l’écran qui comptera le nombre de bonnes réponses d’affilée du joueur. Ce score retombera à 0 en cas d’erreur. On affichera également le meilleur score obtenu.
Charger les données dans l’app
Pour que l’app fonctionne, on doit lui mettre à disposition le dataset. Alors c’est parti:
import json
import streamlit as s
# On crée une fonction qui va lire et charger les données dans un dictionnaire Python
def load_data() -> dict:
data = {}
with open("vn_en_words_translations.json") as fd:
data = json.load(fd)
return data
# On vérifie si les données sont dans le "session_state" (ou cache) de Streamlit
# Si ce n'est pas le cas, alors on utilise la fonction pour charger les données
if "words_dict" not in st.session_state:
st.session_state["words_dict"] = load_data()
به لطف این قطعه کد، می توانم داده های خود را بارگیری کرده و در آن ذخیره کنم وضعیت جلسه از Streamlit (بعدا توضیح خواهم داد که وضعیت جلسه چیست). این بسیار ساده است و به من اجازه می دهد تا از طریق هر نقطه از فایلم به مجموعه داده های خود دسترسی داشته باشم st.session_state["words_dict"]
، که کاملا کاربردی است.
ایجاد مجموعه داده آزمون
ما در حال مقابله با “سخت ترین” بخش هستیم (در واقع، بسیار ساده است، نترسید) از نظر منطق برنامه خود. ایده در اینجا ایجاد تابعی است که به طور تصادفی یک کلمه ویتنامی و 4 کلمه انگلیسی را انتخاب می کند که یکی از آنها ترجمه کلمه ویتنامی ما خواهد بود. برای انجام این کار، تصمیم گرفتم از یک ساختار داده ساده استفاده کنم: دیکشنری با دو کلید. یکی از آنها با لیستی همراه است که حاوی کلمه ویتنامی است (این لیست همیشه اندازه 1 خواهد بود). مورد دیگر با فهرستی همراه است که شامل 4 ترجمه انگلیسی است و مراقب باشید که همیشه ترجمه صحیح را در ابتدای لیست قرار دهید (نمایه 0).
import random
def select_quizz_words(words_dict: dict) -> dict:
# On créer le dictionnaire
selected_words = {
"VN": [],
"EN": []
}
# On détermine au hasard un index que l'on utilise pour prendre un mot vietnamien
# et son équivalent anglais dans notre dataset que l'on place en début de liste.
selected_word_index = random.randrange(0, len(words_dict["translations"]))
selected_words["VN"].append(words_dict["translations"][selected_word_index]["VN"])
selected_words["EN"].append(words_dict["translations"][selected_word_index]["EN"])
# Ainsi, selected_words["EN"][0] sera toujours la bonne traduction de selected_words["VN"][0]
سپس عملکرد خود را ادامه می دهیم تا سه کلمه انگلیسی دیگر را به صورت تصادفی انتخاب کنیم و اطمینان حاصل کنیم که این کلمات دو شرط زیر را دارند:
- آنها نباید مطابقت داشته باشند
selected_words["EN"][0]
، در غیر این صورت پاسخ صحیح به صورت تکراری در 4 گزاره ما ظاهر می شود. - در 4 پیشنهاد ما نباید موارد تکراری وجود داشته باشد.
import random
def select_quizz_words(words_dict: dict) -> dict:
selected_words = {
"VN": [],
"EN": []
}
selected_word_index = random.randrange(0, len(words_dict["translations"]))
selected_words["VN"].append(words_dict["translations"][selected_word_index]["VN"])
selected_words["EN"].append(words_dict["translations"][selected_word_index]["EN"])
find_other_words = True
# On boucle tant que 3 autres mots n'ont pas été choisis
while find_other_words:
# On détermine au hasard un index que l'on utilise pour prendre mot anglais dans notre dataset
selected_en_word_index = random.randrange(0, len(words_dict["translations"]))
# Bien sûr, ce mot ne doit pas être celui choisi plus haut
if selected_en_word_index != selected_word_index:
# Ni déjà être dans notre liste
if words_dict["translations"][selected_en_word_index]["EN"] not in selected_words["EN"]:
# Si les deux conditions sont remplies, alors on l'ajoute à notre liste
selected_words["EN"].append(words_dict["translations"][selected_en_word_index]["EN"])
if len(selected_words["EN"]) == 4:
find_other_words = False
return selected_words
ما اکنون داده های مسابقه خود را داریم.
محتوای برنامه
بنابراین همه چیز خوب و خوب است، اما در حال حاضر برنامه ما هنوز مانند یک صفحه خالی به نظر می رسد که سلام می کند. وقت آن است که مقداری محتوا در آنجا قرار دهید! بیایید با قرار دادن دو یا سه جمله شروع کنیم که به بازیکن توضیح دهد که در آنجا چه می کند.
selected_words_dict = select_quizz_words(words_dict=st.session_state["words_dict"])
st.title("Hello Learners :wave:!")
st.subheader("Let's make a small game. I give you vietnamese word, and you try to give me the good english translation. Let's go?")
st.write(f"What is the english translation of the word **{selected_words_dict['VN'][0]}**?")
این یک شروع است. ابتدا داده های مسابقه خود را در یک متغیر جهانی به نام بارگذاری می کنیم selected_words_dict
با استفاده از تابع نوشته شده در بالا سپس به سرعت قوانین را با نمایش متن بسیار ساده از طریق توضیح می دهیم st.title()
et st.subheader()
، و ما کلمه ویتنامی را برای ترجمه از طریق پیشنهاد می کنیم st.write()
. میتوانید تمام جزئیات عملکردهای Streamlit را که به شما امکان میدهد متن را روی صفحه نمایش دهید، در اینجا بیابید.
اکنون بیایید تا 4 پیشنهاد ترجمه به زبان انگلیسی را به کاربر برنامه پیشنهاد دهیم. ما باید راهی پیدا کنیم تا به بازیکن اجازه دهیم با انتخاب کردن با برنامه ما تعامل داشته باشد. من در اینجا تصمیم گرفتم از کامپوننت Streamlit استفاده کنم st.button()
. من 4 دکمه را در کنار هم به صورت افقی نمایش می دهم که هر کدام یکی از 4 پاسخ ممکن را نشان می دهد. سپس کاربر می تواند با کلیک بر روی یکی از آنها انتخاب کند. در اینجا کد پیاده سازی این دکمه ها آمده است:
# On 'copie' notre liste de propositions en anglais puis on la mélange comme un bon cocktail
selected_en_words_dict_shuffled = selected_words_dict["EN"].copy()
random.shuffle(selected_en_words_dict_shuffled)
# On crée un layout Streamlit composé de 4 colonnes.
col1, col2, col3, col4 = st.columns(4)
# Et pour chacun d'entre eux, on y insère un bouton cliquable contenant la proposition
with col1:
word = selected_en_words_dict_shuffled[0]
st.button(label=word, key=word, on_click=check_result, args=[word], use_container_width=True)
with col2:
word = selected_en_words_dict_shuffled[1]
st.button(label=word, key=word, on_click=check_result, args=[word], use_container_width=True)
with col3:
word = selected_en_words_dict_shuffled[2]
st.button(label=word, key=word, on_click=check_result, args=[word], use_container_width=True)
with col4:
word = selected_en_words_dict_shuffled[3]
st.button(label=word, key=word, on_click=check_result, args=[word], use_container_width=True)
تصمیم گرفتم جدول پاسخ هایم را به هم بزنم، زیرا در غیر این صورت، به یاد داشته باشید، ما همیشه با پاسخ صحیح در جایگاه اول قرار می گرفتیم، که تعلیق زیادی را از تمرین می گرفت …
در مرحله بعد از سیستم چیدمان Streamlit برای ایجاد 4 ستون استفاده می کنیم. برای هر یک از آنها، یک دکمه قابل کلیک اضافه می کنیم که یکی از پاسخ ها را نشان می دهد. توجه داشته باشید که:
-
label
متن دکمه است. -
key
کلیدی است که توسط Streamlit برای داشتن دکمه های منحصر به فرد استفاده می شود. -
on_click
یک روش برگشت تماس است که بلافاصله در مورد آن صحبت خواهیم کرد. -
args
آرایه ای از پارامترها است که به متد برگشت فراخوان داده می شود. بنابراین ما پیشنهاد دکمه را به آن ارسال می کنیم تا کلیک را تایید یا نه. -
use_container_width
یک بولی ساده است که به دکمه اجازه می دهد بداند که می تواند کل عرض ظرفی را که در آن قرار دارد، در اینجا ستون را بگیرد (و برای رسیدن به این هدف نیازی به 5 ساعت CSS نداشتم، که یک معجزه است 🙏) .
مطمئناً می توان این قسمت از کد را فاکتور گرفت، اما من در حال حاضر راه درستی برای انجام آن پیدا نکرده ام. این شاید موضوع مقاله آینده باشد.
تایید یا عدم تایید نتیجه
برنامه ما شروع به محتوا می کند. تنها چیزی که باقی می ماند این است که نتیجه مسابقه را تأیید کنیم. همانطور که در بالا گفته شد، ما به بازیکن اجازه می دهیم امتیاز را ببیند. ما همچنین به او اجازه می دهیم بهترین امتیاز خود را ببیند. پس بیایید با تعریف این دو متغیر شروع کنیم:
#...
if "words_dict" not in st.session_state:
st.session_state["words_dict"] = load_data()
if "score" not in st.session_state:
st.session_state["score"] = 0
if "best_score" not in st.session_state:
st.session_state["best_score"] = 0
# ...
در مرحله بعد، بیایید روی روش پاسخ به تماس تمرکز کنیم check_result
به دکمه های ما بالاتر رفت:
def check_result(choice):
if choice == selected_words_dict["EN"][0]:
st.session_state["score"] += 1
else:
if st.session_state["score"] > st.session_state["best_score"]:
st.session_state["best_score"] = st.session_state["score"]
st.session_state["score"] = 0
این روش بسیار ساده است. اگر انتخاب کاربر ما با اولین عنصر در لیست گزاره های انگلیسی ما مطابقت داشته باشد، آن جکپات است و ما امتیاز آنها را افزایش می دهیم. در غیر این صورت، اگر امتیاز دوم به شدت کمتر از امتیاز فعلی باشد، بهترین امتیاز را به روز می کنیم، سپس آن را روی 0 قرار می دهیم.
اکنون تنها کاری که باید انجام دهیم این است که نمرات خود را نشان دهیم:
st.subheader(body=f"Your current score is: {st.session_state['score']}")
st.subheader(body=f"Your best score is: {st.session_state['best_score']}")
و شما بروید!
وقت تست است
استفاده كردن streamlit run app.py
، می توانیم به سرعت برنامه خود را راه اندازی و آزمایش کنیم.
بنابراین بله، خیلی زیبا نیست، اما همچنان قابل ارائه است، و این بدون حتی یک خط CSS است. ما در نهایت با یک بازی کوچک روبرو می شویم که به ما امکان می دهد کمی روی ویتنامی خود کار کنیم. به نظر می رسد هدفی که برای خود تعیین کرده ایم محقق شده است! بله 🎉!
زیر کاپوت چه خبر است؟
به نظر من در این سطح مهم است که بر مکانیزم Streamlit تأکید کنم که اگر میخواهید از این چارچوب استفاده کنید، همه باید در نظر داشته باشند. Streamlit این اسکریپت کوچک پایتون را که ما به تازگی در آن پیاده سازی کرده ایم، دوباره بارگذاری می کند هر یک تعاملی که با اپلیکیشن داریم. در مورد ما، این بدان معنی است که هر بار که کاربر روی یک دکمه کلیک می کند، کل اسکریپت مجدداً راه اندازی می شود. این چند چیز را توضیح می دهد …
این توضیح می دهد که چرا برخی از “متغیرهای” من، مانند مجموعه داده اولیه، به این شکل بارگذاری می شوند:
if "words_dict" not in st.session_state:
st.session_state["words_dict"] = load_data()
در اینجا، من با سیستم «کش» Streamlit بازی میکنم تا از بارگذاری مجدد این دادهها در هر تعامل جلوگیری کنم. این همان چیزی است که به من امکان می دهد با وجود بارگذاری مجدد پی در پی امتیاز خود را حفظ کنم. در واقع، هر بار که اسکریپت دوباره اجرا می شود، Streamlit آزمایش می کند که آیا فرهنگ لغت یا نه st.session_state
حاوی یک عنصر مرتبط با "words_dict"
(که همینطور است) و بنابراین آن را شارژ نمی کند.
این در نهایت توضیح می دهد که چرا اکثر متغیرها و دستورالعمل های Streamlit در محدوده جهانی اسکریپت اجرا می شوند. به عنوان مثال، گزاره های مورد استفاده در هر سؤال در حوزه جهانی بارگذاری می شوند:
selected_words_dict = select_quizz_words(words_dict=st.session_state["words_dict"])
من این کار را انجام می دهم زیرا نیاز دارم که این دیکشنری هر بار بازسازی شود، فقط برای تغییر گزاره ها و جالب کردن بازی، فرض کنید … جالب! دکمه ها نیز هر بار با مقادیر جدید بازسازی می شوند که هدف ما همین است.
در نتیجه
به لطف Streamlit، من موفق شدم این برنامه کوچک را در کمتر از 100 خط کد و در چند ساعت پیاده سازی کنم. بنابراین، البته، من در پایتون از ابتدا شروع نمیکنم، و برنامه هم از نظر فنی و هم از نظر بصری بسیار عالی است، اما از نتیجه کاملا راضی هستم. من از سهولت استفاده از چارچوب و نتیجه بسیار شگفت زده شدم.
این واقعیت که اسکریپت هر بار به طور کامل بارگذاری می شود باعث می شود فکر کنم چارچوب باید برای برنامه های پیچیده تر محدودیت هایی داشته باشد. علاوه بر این، اجزای موجود، اگرچه متعدد هستند، اما همه موارد استفاده را برای کاربردهای پیچیدهتر پوشش نمیدهند. اما برای نمونه های اولیه کوچک و برنامه های کاربردی ساده مانند آنچه در این مقاله ارائه شده است، این کار را انجام می دهد.
در مورد درخواستم، میخواهم در آینده بهبودهایی ایجاد کنم:
- در صورت درست بودن نتیجه یک پیغام سبز رنگ یا در صورت خطا به رنگ قرمز اضافه کنید و پاسخ صحیح را توضیح دهید.
- به کاربر اجازه دهید جهت تمرین را انتخاب کند: از ویتنامی به انگلیسی یا از انگلیسی به ویتنامی.
- به کاربر اجازه دهید بین تمرین ترجمه کلمه یا جمله یکی را انتخاب کند.
- درخواست من را به صورت آنلاین مستقر کنید.
- چند فاکتورسازی کد را انجام دهید.
- ویژگی های دیگری که بعداً به ذهنم می رسد را اضافه کنید، امیدوارم 🤔.
این موضوع مطمئناً موضوع مقالات بعدی خواهد بود.
همانطور که گفته شد، من به کاوش در این چارچوب ادامه خواهم داد که یکی از مشکلات من را برطرف می کند: ایجاد موفقیت آمیز برنامه های کاربردی ساده در پایتون بدون نیاز به صرف چندین روز برای یادگیری نحوه استفاده از یک چارچوب پیچیده.
به زودی شما را برای ماجراجویی های جدید در دنیای ساده و موثر Streamlit می بینیم. برای یافتن کد مقاله از مراجعه به مخزن Gitlab شک نکنید.
PS: این مقاله با استفاده از هوش مصنوعی تصحیح شده است.