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

آخر الأخبار

جاري التحميل ...

الدرس 33 — استخدام QDockWidget (النوافذ العائمة)

الفكرة العامة

QDockWidget عنصر واجهة رسومية يسمح بإضافة نوافذ فرعية يمكن:

  • تثبيتها على جوانب النافذة (يمين / يسار / أعلى / أسفل).
  • سحبها وتحويلها إلى نوافذ مستقلة عائمة (Floating).
  • إخفاؤها / إظهارها حسب الحاجة.

يُستخدم كثيرًا في برامج معقدة مثل:

  • بيئات التطوير (IDE) حيث تظهر قائمة الملفات أو وحدة الإخراج كـ Dock.
  • برامج التصميم مثل فوتوشوب حيث تظهر لوحات الأدوات بشكل Dock.

QDockWidget

استيراد المكتبات

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QDockWidget, QListWidget, QLabel
from PyQt5.QtCore import Qt

مثال عملي — نافذة رئيسية مع Dock جانبي

الكود التالي ينشئ نافذة رئيسية تحتوي على:

  • محرر نص في الوسط.
  • Dock جانبي فيه قائمة ملفات.

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QDockWidget, QListWidget, QLabel
from PyQt5.QtCore import Qt

class DockExample(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("مثال QDockWidget - النوافذ العائمة")
        self.resize(800, 500)

        # المحرر الرئيسي في الوسط
        editor = QTextEdit()
        editor.setPlainText("مرحبًا! هذا هو المحرر الرئيسي.")
        self.setCentralWidget(editor)

        # إنشاء Dock Widget
        dock = QDockWidget("قائمة الملفات", self)
        dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        # محتوى الـ Dock
        file_list = QListWidget()
        file_list.addItems([f"ملف_{i}.txt" for i in range(1, 6)])
        dock.setWidget(file_list)

        # إضافة الـ Dock للنافذة (افتراضيًا على اليسار)
        self.addDockWidget(Qt.LeftDockWidgetArea, dock)

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

ملاحظات على الكود

  • QMainWindow ضروري لاستخدام Dock Widgets (لأنها تعتمد على مناطق Dock).
  • setCentralWidget() لتعيين الودجت الرئيسي (هنا محرر النص).
  • QDockWidget("العنوان", parent) لإنشاء Dock جديد.
  • setAllowedAreas() لتحديد أي الجوانب يمكن أن يوضع فيها Dock.
  • addDockWidget(Qt.LeftDockWidgetArea, dock) لإضافته إلى الجانب المطلوب.


مزايا إضافية

  • يمكن جعل الـ Dock قابل للإخفاء من قائمة View عبر setFeatures(QDockWidget.DockWidgetClosable | QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable).
  • يمكن إضافة أكثر من Dock (مثلاً Dock للأدوات، Dock للمعاينة).
  • يمكن للمستخدم سحب Dock خارج النافذة ليصبح نافذة مستقلة.


تمرين عملي (قبل الحل)

أنشئ تطبيق يحتوي على:

  1. محرر نص (QTextEdit) في الوسط.
  2. Dock على اليسار يحتوي على قائمة ملفات.
  3. Dock آخر على اليمين يحتوي على وصف / معلومات (QLabel).
  4. اجعل المستخدم قادرًا على سحب أي Dock للخارج كنافذة مستقلة.


فكرة تطويرية

  • اجعل إعدادات الموقع (حجم النوافذ ومكان الـ Dock) تُحفظ في ملف (JSON) وتُسترجع عند الفتح.
  • أضف زر إظهار / إخفاء Dock من قائمة رئيسية.
  • اجعل محتوى Dock يتفاعل مع المحرر (مثلاً عند اختيار ملف من القائمة يظهر اسمه داخل المحرر).


📝 تمرين عملي — تطبيق متعدد النوافذ باستخدام QDockWidget

المطلوب:

  1. محرر نص رئيسي في الوسط.
  2. Dock على اليسار يحتوي على قائمة ملفات.
  3. Dock على اليمين يحتوي على معلومات / وصف.
  4. دعم سحب Dock كنافذة مستقلة (Floating).
  5. تفاعل بسيط: عند اختيار ملف من القائمة، يظهر اسمه في Dock اليمين + يضاف داخل المحرر.


✅ الحل

import sys
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QTextEdit, QDockWidget, QListWidget, QLabel, QWidget, QVBoxLayout
)
from PyQt5.QtCore import Qt


class DockApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("تطبيق متعدد النوافذ باستخدام QDockWidget")
        self.resize(1000, 600)

        # 🔹 1. المحرر الرئيسي
        self.editor = QTextEdit()
        self.editor.setPlainText("مرحبًا بك! اختر ملفًا من القائمة...")
        self.setCentralWidget(self.editor)

        # 🔹 2. Dock اليسار (قائمة الملفات)
        self.file_dock = QDockWidget("قائمة الملفات", self)
        self.file_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        self.file_list = QListWidget()
        self.file_list.addItems([f"ملف_{i}.txt" for i in range(1, 6)])
        self.file_list.currentTextChanged.connect(self.show_file_info)

        self.file_dock.setWidget(self.file_list)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.file_dock)

        # 🔹 3. Dock اليمين (معلومات)
        self.info_dock = QDockWidget("معلومات الملف", self)
        self.info_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        info_widget = QWidget()
        info_layout = QVBoxLayout()
        self.info_label = QLabel("لم يتم اختيار أي ملف بعد.")
        info_layout.addWidget(self.info_label)
        info_widget.setLayout(info_layout)

        self.info_dock.setWidget(info_widget)
        self.addDockWidget(Qt.RightDockWidgetArea, self.info_dock)

        # 🔹 4. تمكين السحب (Float) والإغلاق
        self.file_dock.setFeatures(
            QDockWidget.DockWidgetClosable | QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable
        )
        self.info_dock.setFeatures(
            QDockWidget.DockWidgetClosable | QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable
        )

    # 🔹 5. التفاعل
    def show_file_info(self, file_name):
        if file_name:
            self.info_label.setText(f"المعلومات:\nاسم الملف: {file_name}\nالحجم: تجريبي")
            self.editor.setPlainText(f"محتوى {file_name}\n\n(هذا نص تجريبي يمكن استبداله بمحتوى حقيقي)")


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

⚙️ شرح سريع:

1-QDockWidget أنشأنا Dock لليسار (قائمة ملفات) و Dock لليمين (معلومات).
2-عند اختيار ملف من القائمة (currentTextChanged):

  • يتم تحديث Dock اليمين بمعلومات عن الملف.
  • يتم عرض نص داخل المحرر في الوسط.

3-DockWidgetFloatable تسمح بسحب الـ Dock كنافذة مستقلة.
4-DockWidgetClosable تسمح بإغلاق الـ Dock.


💡 تطوير إضافي:

  • استبدال "محتوى تجريبي" بقراءة محتوى الملف الفعلي من الهارد.
  • إضافة قائمة عليا (MenuBar) فيها خيار "إظهار/إخفاء Docks".
  • حفظ أماكن وأحجام الـ Dock عند الإغلاق (باستخدام saveState() و restoreState()).


🚀 النسخة المطورة — حفظ حالة الـ Dock وإعادتها تلقائيًا

✅ الكود

import sys
import os
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QTextEdit, QDockWidget, QListWidget, QLabel, QWidget, QVBoxLayout
)
from PyQt5.QtCore import Qt, QSettings


class DockApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("تطبيق Dock مطور - حفظ الحالة")
        self.resize(1000, 600)

        # 🔹 1. المحرر الرئيسي
        self.editor = QTextEdit()
        self.editor.setPlainText("مرحبًا بك! اختر ملفًا من القائمة...")
        self.setCentralWidget(self.editor)

        # 🔹 2. Dock اليسار (قائمة الملفات)
        self.file_dock = QDockWidget("قائمة الملفات", self)
        self.file_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        self.file_list = QListWidget()
        self.file_list.addItems([f"ملف_{i}.txt" for i in range(1, 6)])
        self.file_list.currentTextChanged.connect(self.show_file_info)

        self.file_dock.setWidget(self.file_list)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.file_dock)

        # 🔹 3. Dock اليمين (معلومات)
        self.info_dock = QDockWidget("معلومات الملف", self)
        self.info_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        info_widget = QWidget()
        info_layout = QVBoxLayout()
        self.info_label = QLabel("لم يتم اختيار أي ملف بعد.")
        info_layout.addWidget(self.info_label)
        info_widget.setLayout(info_layout)

        self.info_dock.setWidget(info_widget)
        self.addDockWidget(Qt.RightDockWidgetArea, self.info_dock)

        # 🔹 4. تمكين السحب (Float) والإغلاق
        for dock in [self.file_dock, self.info_dock]:
            dock.setFeatures(
                QDockWidget.DockWidgetClosable
                | QDockWidget.DockWidgetMovable
                | QDockWidget.DockWidgetFloatable
            )

        # 🔹 5. تحميل الحالة السابقة إذا وجدت
        self.settings = QSettings("tamer_company", "dock_app")
        self.restoreGeometry(self.settings.value("geometry", b""))
        self.restoreState(self.settings.value("windowState", b""))

    def show_file_info(self, file_name):
        """تحديث Dock اليمين + المحرر عند اختيار ملف"""
        if file_name:
            self.info_label.setText(f"المعلومات:\nاسم الملف: {file_name}\nالحجم: تجريبي")
            self.editor.setPlainText(
                f"محتوى {file_name}\n\n(هذا نص تجريبي يمكن استبداله بمحتوى حقيقي)"
            )

    def closeEvent(self, event):
        """حفظ الحالة عند الإغلاق"""
        self.settings.setValue("geometry", self.saveGeometry())
        self.settings.setValue("windowState", self.saveState())
        event.accept()


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

⚙️ شرح الإضافات الجديدة

  • QSettings("tamer_company", "dock_app") → يخزن الإعدادات في ملف تلقائي (على ويندوز في الريجستري، وعلى لينكس/ماك في ملف إعدادات).
  • saveGeometry() و saveState() → يحفظ حجم النافذة وحالة الـ Dock.
  • restoreGeometry() و restoreState() → يعيد الحالة كما كانت آخر مرة.
  • closeEvent → عند إغلاق النافذة، نخزن الحالة قبل الخروج.


💡 التطوير المستقبلي

  • إضافة زر إعادة ضبط الواجهة لمسح الحالة المحفوظة.
  • جعل حفظ الحالة في ملف JSON بدل QSettings لو عايز تحكم أوضح في التخزين.
  • ربط Dock القائمة بفتح ملفات حقيقية من الجهاز بدل النص التجريبي.

عن الكاتب

Tamer Ahmed

التعليقات


اتصل بنا

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

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

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