الفئة QSettings

أحيانا عدة في البرمجة تحتاج إلى حفظ خصائص برنامجك أو إعداداته كحجم النافذة ومكان البرنامج وخصائصه المفعلة وغير المفعلة، لتقوم بتفعليها مباشرة عند تشغيل البرنامج،

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

طريقة العمل:

تتمثل طريقة عمل هذه الفئة بشكل أساسي في حفظ البيانات على شكل مفتاح تقابله قيمة معينة نوع المفتاح يمكن أن يأخذ جميع أنواع الفئة QVariant مثل QString و QRect و QImage وغيره كما يمكن إضافة مفاتيح فرعية لهيكلة البيانات بشكل منظم

هناك ثلاث أنواع لحفظ البيانات يمكن إختيارها:

البداية:

لفهم الأمور لابد من تطبيق عملي، سنقوم بكتابة برنامج صغير للتجربة ثم نحاول حفظ خصائصه بـ QSettings
قم بفتح بيئة التطوير أو محرر النصوص المفضل لديك وانشئ مشروعا جديدا يتضمن مايلي في ملف main.cpp الخاص به:

#include <QtGui/QApplication>
#include <Qwidget>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    w.show();
    return a.exec();
}

لنلق نظرة على Constructor الخاص بالفئة Qsettings:

QSettings::QSettings ( const QString & organization, const QString & application = QString(), QObject * parent = 0 )

كما هو ظاهر يجب تحديد إسم الفريق والبرنامج في المعاملين الأول والثاني، أما الثالث فيتكفل بإفراع الذاكرة إن قمت بحجز يدوي لأحدى الفئات عند الإستدعاء

إسم الملف سيأخذ إسم البرنامج وينشَؤ داخل مجلد يأخذ إسم الفريق، هكذا للتفرقة بين كل برنامج وملف إعداداته ولتنظيم المجلدات
لنقم بإضافته لبرنامجنا:

#include <QtGui/QApplication>
#include <Qwidget>
#include <QSettings>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    w.show();

    QSettings settings("qt-ar", "MySoft");

    return a.exec();
}

بعد التصنيف ستظهر شاشة فارغة، لكن سيقوم البرنامج بإنشاء الملف MySoft.conf أو MySoft.plist أو إضافة المدخلة في الرجيستري حسب نوع نظام التشغيل (هناك من الأنظمة من لا تنشئه حتى تكون له قيمة أي يحتوي على خصائص)، ويكون فارغا في البداية

كتابة البيانات:

لكتابة البيانات في الملف نستعمل الدالة التالية:

void QSettings::setValue ( const QString & key, const QVariant & value )

تأخذ معاملين المفتاح والقيمة، هذه الأخيرة نوعها QVariant أي ممكن تأخذ العديد من الأنواع كما أشرنا سابقا ويكفي وضع القيمة كما هي وستقوم المكتبة بتحويلها آليا، لنقم بتجربة ذلك:

#include <QtGui/QApplication>
#include <Qwidget>
#include <QSettings>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    w.show();

    QSettings settings("qt-ar", "MySoft");
    settings.setValue("test", true);

    return a.exec();
}

سيتم كتابة القيمة true مقابل المفتاح test، مثلا على لينوكس محتويات الملف تكون كتالي:

نلاحظ كتابة المفتاح والقيمة ضمن القسم العام ([General]) إفتراضيا لأننا لم نحدد القسم، يمكن تحديده بإضافة / على شكل مسار، مثلا:

#include <QtGui/QApplication>
#include <Qwidget>
#include <QSettings>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    w.show();

    QSettings settings("qt-ar", "MySoft");
    settings.setValue("test", "false");
    settings.setValue("User/name", "mohamed");
    settings.setValue("User/age", 19);
    return a.exec();
}

لتعديل قيمة ما قم بتغييرها من خلال المفتاح مع قيمة أخرى، كما في مثالنا test أخذت false وسيتم تعديلها في الملف مباشرة.

أما لحذف قيمة ما نستعمل الدالة التالية:

void QSettings::remove ( const QString & key )

ستقوم بحذف المفتاح مع قيمته، بالإضافة للمفاتيح التابعة له إن كان عبارة عن قسم مثل User في مثالنا السابق سيحذف الإسم مع العمر

إذا كان هنالك أقسام كثيرة في برنامجك فليس عمليا أن نقوم بكتابة إسم القسم و / مع كل مدخلة
لذا يمكن إستعمال الدالة beginGroup و endGroup:

#include <QtGui/QApplication>
#include <Qwidget>
#include <QSettings>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    w.show();

    QSettings settings("qt-ar", "MySoft");
    settings.beginGroup("User");
    settings.setValue("name", "mohamed");
    settings.setValue("age", 19);
    settings.endGroup();
    return a.exec();
}

البرنامج يقوم بكتابة البيانات بشكل جيد كما لاحظنا، ولم يتبق سوى قراءتها

قراءة البيانات:

لقراءة البيانات نستعمل الدالة التالية:

QVariant QSettings::value ( const QString & key, const QVariant & defaultValue = QVariant() ) const

نعطي الدالة المفتاح المراد قراءة قيمته وترجعه لنا على شكل QVariant دائما، لذا وجب علينا تحويل الناتج إلى نوعه المناسب، شاهد المثال:

#include <QtGui/QApplication>
#include <Qwidget>
#include <Qsettings>
#include <QLineEdit>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QLineEdit lineEdit;
    lineEdit.show();

    QSettings settings("qt-ar", "MySoft");
    lineEdit.setText(settings.value("User/name").toString());

    return a.exec();
}

كود بسيط يقوم بقراءة الإسم المحفوظ (من مثال السابق) ويعرضه في QLineEdit، الملاحظ هو إضافة toString لأن القيمة المرجعة عبارة عن QVariant و lineEdit يستقبل QString من خلال setText لذا وجب التحويل للتتناسب الأنواع.

إذا صادف وكانت القيمة غير متوفرة بعد في الملف، يمكن المعامل الثاني من الدالة من إعطاء قيمة إفتراضية للقيمة، كمايلي:

lineEdit.setText(settings.value("User/name", "amine").toString());

إن لم تتوفر قيمة للمفتاح name في ملف الإعدادات فستقوم الدالة بإعطاء القيمة amine إلى ذلك المفتاح وبالتالي إظهار amine في lineEdit

الدالة allKeys() تقوم بإرجاع قائمة من نوع QStringList تحتوي على جميع مفاتيح المجموعة الحالية والمجموعات الفرعية:

settings.setValue("fridge/color", Qt::white);
settings.setValue("fridge/size", Qsize(32, 96));
settings.setValue("sofa", true);
settings.setValue("tv", false);

QStringList keys = settings.allKeys();
// keys: ["fridge/color", "fridge/size", "sofa", "tv"]

الدالة childKeys() تقوم بإرجاع قائمة من نوع QStringList تحتوي على جميع مفاتيح المجموعة الحالية فقط:

settings.setValue("fridge/color", Qt::white);
settings.setValue("fridge/size", Qsize(32, 96));
settings.setValue("sofa", true);
settings.setValue("tv", false);

QStringList keys = settings.childKeys();
// keys: ["sofa", "tv"]

الدالة childGroups() تقوم بإرجاع قائمة من نوع QStringList تحتوي على جميع مفاتيح القسم الذي يحتوي أبناءً بالنسبة للمجموعة الحالية :

settings.setValue("fridge/color", Qt::white);
settings.setValue("fridge/size", Qsize(32, 96));
settings.setValue("sofa", true);
settings.setValue("tv", false);

QStringList keys = settings.childGroups();
// keys: ["fridge"]

الدالة contains(QString key) تقوم بإرجاع صح إن كان هناك مفتاح يحمل الإسم key وإلا فخطأ:

settings.setValue("fridge/color", Qt::white);
settings.setValue("fridge/size", Qsize(32, 96));
settings.setValue("sofa", true);
settings.setValue("tv", false);

bool exists = settings.contains("name");
// exists = false

تطبيق عملي:

طبعا الأوامر السابقة تبقى كمدخل فقط، التطبيق العملي لهذه الخاصية (حفظ واسترجاع البيانات) يكون عادة عند إيقاف وتشغيل البرنامج، هنا تقوم المكتبة Qt بتوفير فئة QEvent تحتوي على عدة أحداث منها حدث إغلاق البرنامج، يتم عبر الدالة التالية:

void closeEvent(QCloseEvent *event)

يتم توريث هذه الدالة من الفئة QWidget. نقوم بإعادة كتابة محتواها هكذا مثلا:

void MainWindow::closeEvent(QCloseEvent *event)
{
    writeSettings();
    event->accept();
}

void MainWindow::writeSettings()
{
    QSettings settings("Qt Soft", "Prog");

    settings.beginGroup("MainWindow");
    settings.setValue("size", size());
    settings.setValue("pos", pos());
    settings.endGroup();
}

قمنا بفصل أوامر الحفظ في الدالة writeSettings تحتوي على أوامر عادية سبق وشرحناها، أما السطر الثاني فهو مهم لقبول حدث الإغلاق وإلا فلن يغلق البرنامج.

عملية قراءة البيانات تتم عادة عند تشغيل البرنامج، وفي الـ constructor تحديدا، ويفضل فصلها في دالة أخرى لتنظيم الكود، ثم لحفظ الخصائص التي تم قراءتها طوال تشغيل البرنامج وبدلا من أن نستدعي دالة القراءة كل مرة، نقوم بحفظ الخصائص في متغيرات، مثال يوضح العملية:

MainWindow::MainWindow()
{
    ...
    readSettings();
}

void MainWindow::readSettings()
{
    QSettings settings("Qt Soft", "Prog");

    settings.beginGroup("MainWindow");
    size = settings.value("size", QSize(400, 400)).toSize();
    pos = settings.value("pos", QPoint(400, 130)).toPoint();
    settings.endGroup();

    resize(size);
    move(mov);
}

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

شارك التدوينة :
[del.icio.us] [Digg] [Facebook] [Google] [Mixx] [Sphinn] [StumbleUpon] [Technorati] [Windows Live] [Yahoo!]
مواضيع عشوائية
التعليقات

ياااه أخيراً يا عم أمين رجعت تنشط المدونة وتنفض التراب عنها

مُبارك علينا يا سيدي

أهلا أحمد، شرفت المدونة صديقي :)
الغياب طال بسبب الدراسة، وهذه العودة مؤقتة بفضل الإجازة لكنها إنتهت بسرعة للأسف :( سأحاول المواضبة على الكتابة إن استطعت طبعا
شكرا لك أحمد

شكرا لك على الدرس من جد جزاك الله خير

ممتاز كل يوم يمر تثبت لي مكتبات Qt انها الاقوي
ساحاول تطبيق الدرس في PyQt ان شاء الله
جزاك الله خير

شكرا محمد إن شاء استفدت

أهلا أحمد، بالفعل Qt توفر مزايا كثيرة تسهل كثيرا البرمجة وتضفي عليها متعة أيضا، بالتوفيق

مرحبا بكما في المدونة :)

مشكور كثيرا
لقد استفدت من الموضوع بشكل جيد وكم كنت بحاجة لمثل هذا

السلام عليكم،

برافو أمين
مدونة جميلة و معلومات و مواضيع و دروس أجمل
ربي يزيدك علماً عزيزي

تحياتي، من مصر

شكرا جزيلا لك أختي

تحياتي لك ولأهل مصر

أضف تعليق

(مطلوب)

(مطلوب)

:wink: :-| :-x :) 8-O :( :roll: :-P :oops: :-o :lol: :idea: :-D :evil: :cry: 8) :arrow: :-? :?: :!: