انتقل إلى المحتوى الرئيسي

الوحدة 2 — مكوّنات الإدخال والعرض

بعد نموذج التنفيذ في الوحدة السابقة، ننزل الآن إلى المكوّنات التي تُشكّل واجهة أيّ تطبيق Streamlit. الغرض من هذه الوحدة عمليّ: بناء نموذج تسجيل عميل واحد لفريق المبيعات، من الحقول الأولى (اسم، عمر، اشتراك) إلى العرض النهائيّ لدرجة الخطر. كلّ مكوّن سنستعمله يقع في إحدى فئتَين: مكوّن إدخال يُعيد قيمة، أو مكوّن عرض يُظهر شيئًا بلا إعادة قيمة.

القاعدة الذهبيّة: كلّ مكوّن إدخال يُعيد قيمة

Streamlit يبسِّط ما تعقّده أُطر الويب الأخرى. st.text_input("الاسم") لا يُنشئ حقلًا ينتظر معالج حدث؛ بل يُعيد قيمة الحقل الحاليّة كسلسلة نصّيّة. st.slider("العمر", 18, 90, 35) يُعيد عددًا صحيحًا. st.checkbox("مشترك في العرض") يُعيد True أو False. القاعدة موحّدة: المكوّن يُعيد قيمته الحاليّة، وقيمته الحاليّة هي ما يراه المستخدم على الشاشة الآن.

هذه القاعدة تجعل الشيفرة تُقرأ كأنّها استمارة استفتاء عاديّة:

import streamlit as st

nom = st.text_input("اسم العميل")
age = st.slider("العمر", 18, 90, 35)
type_offre = st.selectbox("العرض", ["Basic", "Premium", "Family"])
mensuel = st.number_input("المبلغ الشهريّ (USD)", min_value=0.0, value=39.99)
est_client_pro = st.checkbox("عميل مهنيّ")

st.write("قيمة الحقول الحاليّة:", {
"nom": nom, "age": age, "offre": type_offre,
"mensuel": mensuel, "pro": est_client_pro,
})

عند كلّ تغيير في أيّ حقل، يُعاد تنفيذ الملفّ، وتُعيد كلّ الاستدعاءات القيم الجديدة، وتُعرض النتيجة الجديدة. لا استماع، لا onChange.

عائلات مكوّنات الإدخال

Streamlit يعرض عائلة كاملة، وكلّ واحدة تُجاوب مسألة تصميم مختلفة.

النصّ: st.text_input لسطر واحد، st.text_area لعدّة أسطر. المعلمة max_chars تحدّ من الطول. المعلمة placeholder تعرض نصًّا رماديًّا يختفي بمجرّد الكتابة.

الأرقام: st.number_input لعدد قابل للتحرير مع أزرار ± ومعلمة step. st.slider لمنزلق. الفرق العمليّ: number_input يسمح بإدخال قيمة دقيقة، slider يُحسّن التصفّح السريع لنطاق. لعمر عميل، slider(18, 90) كافٍ؛ لمبلغ فاتورة، number_input أدقّ.

الاختيارات: st.selectbox لاختيار واحد من قائمة، st.multiselect لعدّة اختيارات، st.radio لعرض الخيارات كلّها مرئيّة (مناسب لقائمة قصيرة، 2 إلى 5)، st.select_slider لسلّم مرتَّب (مثل «صغير، متوسّط، كبير»).

التواريخ والأوقات: st.date_input يُعيد datetime.date، ويقبل مجالًا بتمرير أنبوبَين. st.time_input يُعيد datetime.time.

الحالة الثنائيّة: st.checkbox لصندوق تأشير، st.toggle لمفتاح مُحوَّل (نفس السلوك، مظهر مختلف).

الملفّات: st.file_uploader لرفع ملفّ واحد أو عدّة ملفّات. تفصيله في الوحدة 7.

عائلات مكوّنات العرض

بعد جمع القيم، نعرض النتائج. أربع فئات تغطّي 90 ٪ من الاستعمالات.

النصّ: st.write (السحريّ: يعرف نوع ما يستقبله ويُنسّقه: نصّ، إطار بيانات، رسم…)، st.markdown (لتنسيق Markdown صريح مع دعم HTML اختياريّ)، st.text (نصّ خام بدون تنسيق)، st.code (كود مع تلوين نحويّ).

المقاييس والمعلومات: st.metric(label, value, delta) لعرض رقم بارز مع تغيّر (سهم أخضر أو أحمر). st.info وst.success وst.warning وst.error لصناديق حالة ملوّنة. st.exception لعرض استثناء بايثون كاملًا (مفيد أثناء التطوير، ممنوع في الإنتاج لأنّه يكشف الأثر).

الجداول: st.dataframe تفاعليّ (فرز، تصفّح، تحديد)، st.table جامد (مناسب للطباعة). عند شكّ، st.dataframe أفضل.

الرسوم: تفصيلها في الوحدة 4. للتذكير: st.line_chart، st.bar_chart، st.area_chart مباشرة على إطار بيانات.

نموذج تسجيل عميل واحد

نُجمِّع الآن الشيفرة الكاملة لنموذج تسجيل عميل، مع محاكاة نموذج للخطر إلى أن نربطه في الوحدة 8:

import streamlit as st
import numpy as np

st.set_page_config(page_title="تسجيل عميل", layout="centered")
st.title("تسجيل احتمال ترك عميل")

col_gauche, col_droite = st.columns(2)

with col_gauche:
st.subheader("معلومات أساسيّة")
identifiant = st.text_input("المعرّف", placeholder="مثال: C0042")
age = st.slider("العمر", 18, 90, 42)
anciennete = st.number_input("الأقدميّة (شهر)", min_value=0, max_value=240, value=24, step=1)

with col_droite:
st.subheader("العرض والاستهلاك")
offre = st.selectbox("نوع العرض", ["Basic", "Standard", "Premium", "Family"])
mensuel_usd = st.number_input("المبلغ الشهريّ (USD)", min_value=0.0, value=39.99, step=1.0)
a_reclamation = st.checkbox("تقدَّم بشكوى في آخر 90 يومًا")

# محاكاة نموذج: قاعدة بسيطة لعرض ما سيصير حقيقيًّا في الوحدة 8.
def score_simule(age, anciennete, mensuel, reclamation):
base = 0.15
base += 0.30 if reclamation else 0
base += 0.20 if anciennete < 6 else 0
base -= 0.10 if anciennete > 36 else 0
base += 0.10 if mensuel > 60 else 0
return float(np.clip(base, 0, 1))

score = score_simule(age, anciennete, mensuel_usd, a_reclamation)
seuil = 0.5

st.divider()
col_m1, col_m2, col_m3 = st.columns(3)
col_m1.metric("درجة الخطر", f"{score:.2f}")
col_m2.metric("العتبة", f"{seuil:.2f}")
col_m3.metric("العرض", offre)

if score >= seuil:
st.warning(f"العميل {identifiant or '(بلا معرّف)'} في منطقة الخطر، يُنصح باتّصال احتفاظ خلال 48 ساعة.")
else:
st.success(f"العميل {identifiant or '(بلا معرّف)'} في المنطقة الآمنة، متابعة عاديّة.")

خمس نقاط تصميم في هذه الشيفرة. الأعمدة تُقسم الشاشة بصريًّا بلا فوضى؛ سيأتي شرحها موسّعًا في الوحدة 3. العنوان الفرعيّ يُنظّم الحقول بأنساب معنى، لا بترتيب زمنيّ. placeholder يُرشد المستخدم بلا فرض قيمة. st.divider يفصل الإدخال عن العرض بصريًّا. st.warning وst.success يُترجمان قرار العمل، لا الرقم فقط — لأنّ فريق المبيعات ينظر إلى «افعل ماذا؟»، لا إلى رقم الخطر بحدّ ذاته.

الفخّ الشائع: عرض النموذج داخل شرط زرّ

عرض النموذج بشكل مباشر (كما هو أعلاه) يستفيد من إعادة التنفيذ في الوقت الحقيقيّ: كلّ تعديل يُحدّث الدرجة فورًا. البديل المُغري هو وضع الحساب داخل if st.button("سجّل"):، لكنّه يُلزم المستخدم بضغط زرّ عند كلّ تعديل. ما لم يكن الحساب مكلفًا (نموذج ضخم، استدعاء API)، العرض المباشر أفضل؛ الوحدة 6 ستُبيّن متى نلجأ إلى st.form بدلًا من ذلك، لضبط التسجيل في دفعة واحدة عند التصميم لتفاعل أثقل.

المفاتيح ثابتة

كلّ مكوّن إدخال يقبل key="...". أنصحك بإسناد مفتاح صريح لكلّ حقل مهمّ من اليوم الأوّل. سبب واحد: session_state[key] يمنحك القيمة نفسها من أيّ مكان في الشيفرة، بلا الحاجة إلى إعادة تنظيم الملفّ. وثمّة سبب ثانٍ: ثبات المفتاح يضمن ثبات القيمة عبر إعادات التنفيذ حين تُعيد ترتيب الملفّ.

الخلاصة

  • كلّ مكوّن إدخال يُعيد قيمته الحاليّة كأنّها قراءة استفتاء عاديّة؛ ولا حاجة لمعالج حدث.
  • عائلات الإدخال: نصّ، أرقام، اختيارات، تواريخ، ثنائيّ، ملفّات. كلّ عائلة لها استعمال مثاليّ (منزلق للتصفّح، number_input للدقّة).
  • عائلات العرض: نصّ، مقاييس/معلومات، جداول، رسوم. st.write سحريّ لكنّه ليس بديلًا عن اختيار صريح.
  • ترجم الرقم إلى قرار عمل (warning أو success) بدل عرضه وحده؛ فريق المبيعات ينظر إلى «افعل ماذا؟».

الوحدة التالية: تنظيم كلّ هذه المكوّنات في تخطيط قابل للقراءة بأعمدة وتبويبات وشريط جانبيّ.