تقنيات نور التعليمية تقنيات نور التعليمية
في الوقت الحالي

جاري صيانة و تحديث للمدونة

في الوقت الحالي
جاري التحميل ...

الدرس 29 – أداة QSpinBox و QDoubleSpinBox

في الدرس 29 من سلسلة PyQt سنتعلم عن QSpinBox و QDoubleSpinBox، وهي أدوات تسمح للمستخدم باختيار أرقام بسهولة مع إمكانية تحديد الحد الأدنى والأقصى وخطوة التغيير.

QSpinBox و QDoubleSpinBox

1. ما هي QSpinBox و QDoubleSpinBox؟

  • QSpinBox: أداة لاختيار أعداد صحيحة (Integers) باستخدام أسهم زيادة ونقصان أو إدخال يدوي.
  • QDoubleSpinBox: مشابهة ولكن للأعداد العشرية (Floats).


2. أهم الخصائص

الخاصية الوصف
setMinimum(value) تعيين الحد الأدنى للقيمة.
setMaximum(value) تعيين الحد الأقصى للقيمة.
setRange(min, max) تعيين الحدين الأدنى والأقصى معًا.
setSingleStep(step) تحديد مقدار التغيير عند النقر على الأسهم.
value() الحصول على القيمة الحالية.
setValue(value) تعيين قيمة مبدئية.
valueChanged إشارة تُطلق عند تغيير القيمة.

3. مثال عملي

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QSpinBox, QDoubleSpinBox

class SpinBoxExample(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("مثال QSpinBox و QDoubleSpinBox")
        self.resize(300, 200)

        layout = QVBoxLayout()

        # QSpinBox للأعداد الصحيحة
        self.int_spin = QSpinBox()
        self.int_spin.setRange(0, 100)  # من 0 إلى 100
        self.int_spin.setSingleStep(5)  # التغيير كل 5
        self.int_spin.valueChanged.connect(self.show_values)
        layout.addWidget(self.int_spin)

        # QDoubleSpinBox للأعداد العشرية
        self.double_spin = QDoubleSpinBox()
        self.double_spin.setRange(0.0, 10.0)  # من 0.0 إلى 10.0
        self.double_spin.setSingleStep(0.5)  # التغيير كل 0.5
        self.double_spin.setDecimals(2)  # عرض منزلتين عشريتين
        self.double_spin.valueChanged.connect(self.show_values)
        layout.addWidget(self.double_spin)

        # النص لعرض القيم
        self.label = QLabel("القيم المختارة ستظهر هنا")
        layout.addWidget(self.label)

        self.setLayout(layout)

    def show_values(self):
        int_value = self.int_spin.value()
        double_value = self.double_spin.value()
        self.label.setText(f"القيمة الصحيحة: {int_value} | القيمة العشرية: {double_value}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = SpinBoxExample()
    window.show()
    sys.exit(app.exec_())

4. شرح الكود

  1. QSpinBox

    • يسمح باختيار أعداد صحيحة من 0 إلى 100.

    • التغيير يتم بمقدار 5 عند الضغط على الأسهم.

  2. QDoubleSpinBox

    • يسمح باختيار أعداد عشرية بين 0 و 10.

    • التغيير يتم بمقدار 0.5.

    • عرض رقم عشري حتى منزلتين.

  3. عند تغيير أي قيمة، يتم عرضها في النص.


5. استخدامات عملية

  • اختيار الكمية في تطبيقات التسوق.
  • تحديد درجة الصوت أو السطوع.
  • اختيار القيم الرقمية في النماذج.



 تمرين على QSpinBox و QDoubleSpinBox بحيث:

  • تختار كمية المنتج باستخدام QSpinBox.
  • تحدد سعر الوحدة باستخدام QDoubleSpinBox.
  • يتم حساب السعر الإجمالي تلقائيًا عند أي تغيير.


التمرين – حساب السعر الإجمالي

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QSpinBox, QDoubleSpinBox, QHBoxLayout

class PriceCalculator(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("حساب السعر الإجمالي")
        self.resize(300, 200)

        layout = QVBoxLayout()

        # إدخال الكمية
        quantity_layout = QHBoxLayout()
        quantity_layout.addWidget(QLabel("الكمية:"))
        self.quantity_spin = QSpinBox()
        self.quantity_spin.setRange(1, 100)  # من 1 إلى 100
        self.quantity_spin.setSingleStep(1)
        self.quantity_spin.valueChanged.connect(self.calculate_total)
        quantity_layout.addWidget(self.quantity_spin)
        layout.addLayout(quantity_layout)

        # إدخال سعر الوحدة
        price_layout = QHBoxLayout()
        price_layout.addWidget(QLabel("سعر الوحدة:"))
        self.price_spin = QDoubleSpinBox()
        self.price_spin.setRange(0.0, 1000.0)
        self.price_spin.setSingleStep(0.5)
        self.price_spin.setDecimals(2)  # منزلتان عشريتان
        self.price_spin.valueChanged.connect(self.calculate_total)
        price_layout.addWidget(self.price_spin)
        layout.addLayout(price_layout)

        # عرض السعر الإجمالي
        self.total_label = QLabel("السعر الإجمالي: 0.00")
        layout.addWidget(self.total_label)

        self.setLayout(layout)

    def calculate_total(self):
        quantity = self.quantity_spin.value()
        price_per_unit = self.price_spin.value()
        total = quantity * price_per_unit
        self.total_label.setText(f"السعر الإجمالي: {total:.2f}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = PriceCalculator()
    window.show()
    sys.exit(app.exec_())

شرح التمرين

  1. QSpinBox للكمية:

    • يحدد عدد الوحدات (من 1 إلى 100).

  2. QDoubleSpinBox لسعر الوحدة:

    • يحدد السعر بدقة منزلتين عشريتين.

  3. حساب السعر الإجمالي يتم مباشرة عند أي تغيير في الكمية أو السعر.

  4. النتيجة تظهر في QLabel بشكل فوري.


سأطور لك التمرين بحيث:

  • تستطيع إضافة أكثر من منتج مع الكمية وسعر الوحدة.
  • كل منتج يتم حفظه في جدول QTableWidget.
  • يتم حساب المجموع الكلي لجميع المنتجات تلقائيًا.


النسخة المطورة – إدارة المنتجات مع المجموع الكلي

import sys
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QLabel, QSpinBox, QDoubleSpinBox,
    QHBoxLayout, QPushButton, QLineEdit, QTableWidget, QTableWidgetItem
)

class ProductManager(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("إدارة المنتجات")
        self.resize(500, 400)

        layout = QVBoxLayout()

        # إدخال اسم المنتج
        product_layout = QHBoxLayout()
        product_layout.addWidget(QLabel("اسم المنتج:"))
        self.product_name = QLineEdit()
        product_layout.addWidget(self.product_name)
        layout.addLayout(product_layout)

        # إدخال الكمية
        quantity_layout = QHBoxLayout()
        quantity_layout.addWidget(QLabel("الكمية:"))
        self.quantity_spin = QSpinBox()
        self.quantity_spin.setRange(1, 100)
        quantity_layout.addWidget(self.quantity_spin)
        layout.addLayout(quantity_layout)

        # إدخال سعر الوحدة
        price_layout = QHBoxLayout()
        price_layout.addWidget(QLabel("سعر الوحدة:"))
        self.price_spin = QDoubleSpinBox()
        self.price_spin.setRange(0.0, 1000.0)
        self.price_spin.setDecimals(2)
        self.price_spin.setSingleStep(0.5)
        price_layout.addWidget(self.price_spin)
        layout.addLayout(price_layout)

        # زر إضافة المنتج
        self.add_button = QPushButton("إضافة المنتج")
        self.add_button.clicked.connect(self.add_product)
        layout.addWidget(self.add_button)

        # جدول عرض المنتجات
        self.table = QTableWidget()
        self.table.setColumnCount(4)
        self.table.setHorizontalHeaderLabels(["اسم المنتج", "الكمية", "سعر الوحدة", "الإجمالي"])
        layout.addWidget(self.table)

        # عرض المجموع الكلي
        self.total_label = QLabel("المجموع الكلي: 0.00")
        layout.addWidget(self.total_label)

        self.setLayout(layout)

    def add_product(self):
        name = self.product_name.text().strip()
        quantity = self.quantity_spin.value()
        price_per_unit = self.price_spin.value()

        if name == "" or price_per_unit == 0:
            return  # تجاهل إذا البيانات ناقصة

        total = quantity * price_per_unit

        # إضافة الصف للجدول
        row = self.table.rowCount()
        self.table.insertRow(row)
        self.table.setItem(row, 0, QTableWidgetItem(name))
        self.table.setItem(row, 1, QTableWidgetItem(str(quantity)))
        self.table.setItem(row, 2, QTableWidgetItem(f"{price_per_unit:.2f}"))
        self.table.setItem(row, 3, QTableWidgetItem(f"{total:.2f}"))

        # تحديث المجموع الكلي
        self.update_total()

        # مسح المدخلات
        self.product_name.clear()
        self.quantity_spin.setValue(1)
        self.price_spin.setValue(0.0)

    def update_total(self):
        total_sum = 0.0
        for row in range(self.table.rowCount()):
            total_sum += float(self.table.item(row, 3).text())
        self.total_label.setText(f"المجموع الكلي: {total_sum:.2f}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = ProductManager()
    window.show()
    sys.exit(app.exec_())

مميزات النسخة المطورة

✅ إدخال اسم المنتج + الكمية + سعر الوحدة.
✅ عرض المنتجات في جدول منظم.
✅ حساب المجموع الكلي تلقائيًا.
✅ إعادة تعيين الحقول بعد كل إضافة.


التمرين المطور بحيث:

  • كل منتج يضاف يتم حفظه مباشرة في ملف Excel باسم products.xlsx.
  • عند تشغيل البرنامج يتم تحميل البيانات السابقة من الملف تلقائيًا إذا كان موجودًا.


النسخة النهائية – الحفظ التلقائي في Excel

import sys
import os
import pandas as pd
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QLabel, QSpinBox, QDoubleSpinBox,
    QHBoxLayout, QPushButton, QLineEdit, QTableWidget, QTableWidgetItem
)

class ProductManager(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("إدارة المنتجات")
        self.resize(500, 400)

        layout = QVBoxLayout()

        # إدخال اسم المنتج
        product_layout = QHBoxLayout()
        product_layout.addWidget(QLabel("اسم المنتج:"))
        self.product_name = QLineEdit()
        product_layout.addWidget(self.product_name)
        layout.addLayout(product_layout)

        # إدخال الكمية
        quantity_layout = QHBoxLayout()
        quantity_layout.addWidget(QLabel("الكمية:"))
        self.quantity_spin = QSpinBox()
        self.quantity_spin.setRange(1, 100)
        quantity_layout.addWidget(self.quantity_spin)
        layout.addLayout(quantity_layout)

        # إدخال سعر الوحدة
        price_layout = QHBoxLayout()
        price_layout.addWidget(QLabel("سعر الوحدة:"))
        self.price_spin = QDoubleSpinBox()
        self.price_spin.setRange(0.0, 1000.0)
        self.price_spin.setDecimals(2)
        self.price_spin.setSingleStep(0.5)
        price_layout.addWidget(self.price_spin)
        layout.addLayout(price_layout)

        # زر إضافة المنتج
        self.add_button = QPushButton("إضافة المنتج")
        self.add_button.clicked.connect(self.add_product)
        layout.addWidget(self.add_button)

        # جدول عرض المنتجات
        self.table = QTableWidget()
        self.table.setColumnCount(4)
        self.table.setHorizontalHeaderLabels(["اسم المنتج", "الكمية", "سعر الوحدة", "الإجمالي"])
        layout.addWidget(self.table)

        # عرض المجموع الكلي
        self.total_label = QLabel("المجموع الكلي: 0.00")
        layout.addWidget(self.total_label)

        self.setLayout(layout)

        # تحميل البيانات من الملف إذا كان موجود
        self.load_from_excel()

    def add_product(self):
        name = self.product_name.text().strip()
        quantity = self.quantity_spin.value()
        price_per_unit = self.price_spin.value()

        if name == "" or price_per_unit == 0:
            return  # تجاهل إذا البيانات ناقصة

        total = quantity * price_per_unit

        # إضافة الصف للجدول
        row = self.table.rowCount()
        self.table.insertRow(row)
        self.table.setItem(row, 0, QTableWidgetItem(name))
        self.table.setItem(row, 1, QTableWidgetItem(str(quantity)))
        self.table.setItem(row, 2, QTableWidgetItem(f"{price_per_unit:.2f}"))
        self.table.setItem(row, 3, QTableWidgetItem(f"{total:.2f}"))

        # تحديث المجموع الكلي
        self.update_total()

        # حفظ في ملف Excel
        self.save_to_excel()

        # مسح المدخلات
        self.product_name.clear()
        self.quantity_spin.setValue(1)
        self.price_spin.setValue(0.0)

    def update_total(self):
        total_sum = 0.0
        for row in range(self.table.rowCount()):
            total_sum += float(self.table.item(row, 3).text())
        self.total_label.setText(f"المجموع الكلي: {total_sum:.2f}")

    def save_to_excel(self):
        data = []
        for row in range(self.table.rowCount()):
            data.append([
                self.table.item(row, 0).text(),
                int(self.table.item(row, 1).text()),
                float(self.table.item(row, 2).text()),
                float(self.table.item(row, 3).text())
            ])
        df = pd.DataFrame(data, columns=["اسم المنتج", "الكمية", "سعر الوحدة", "الإجمالي"])
        df.to_excel("products.xlsx", index=False)

    def load_from_excel(self):
        if os.path.exists("products.xlsx"):
            df = pd.read_excel("products.xlsx")
            for _, row_data in df.iterrows():
                row = self.table.rowCount()
                self.table.insertRow(row)
                self.table.setItem(row, 0, QTableWidgetItem(str(row_data["اسم المنتج"])))
                self.table.setItem(row, 1, QTableWidgetItem(str(row_data["الكمية"])))
                self.table.setItem(row, 2, QTableWidgetItem(f"{row_data['سعر الوحدة']:.2f}"))
                self.table.setItem(row, 3, QTableWidgetItem(f"{row_data['الإجمالي']:.2f}"))
            self.update_total()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = ProductManager()
    window.show()
    sys.exit(app.exec_())

مميزات هذه النسخة

حفظ تلقائي لكل عملية إضافة منتج في ملف products.xlsx.
تحميل تلقائي للبيانات السابقة عند فتح البرنامج.
✅ عرض المجموع الكلي دائمًا محدث.

عن الكاتب

Tamer Ahmed

التعليقات


اتصل بنا

إذا أعجبك محتوى مدونتنا نتمنى البقاء على تواصل دائم ، فقط قم بإدخال بريدك الإلكتروني للإشتراك في بريد المدونة السريع ليصلك جديد المدونة أولاً بأول ، كما يمكنك إرسال رساله بالضغط على الزر المجاور ...

جميع الحقوق محفوظة

تقنيات نور التعليمية