الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

حفظ إعدادت البرامج Storage Of Application Preferences

بدأه Wajdy Essam في 14 سبتمبر 2009 · 6 رد · 1,954 مشاهدة · في JavaSE
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكــم ورحمـة الله وبركاتــه ،،

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

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

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

بعد ذلك قامت صن في جافا 1.4 بتقديم الكلاس Preference الذي يحل كل المشاكل تماما واستخدامه أسهل بكثير أيضا من الProperties .

في هذه المقالة سوف نتناول هذه الAPI الخاصه بحفظ اعدادات البرامج (سواء الProperties و الPreferences ) ، وفي الأخير سوف نطرح مثال عملي متكامل حول هذا الموضوع ..

Property Maps

الProperty هو map لتخزين مجموعه من المفاتيح وما يقابلها من القيم Key/Value Pairs . وجافا تطبق هذا المفهوم في الكلاس Properties وفي هذا الكلاس يجب أن تكون المفاتيح والقيم كلها من النوع String والذي يميزه عن استخدام الملفات العادية هو سهولة التخزين واسترجاع البيانات بسهوله ، اضافة الى أنك يمكن أن تقدم قيم افتراضية في حال لم تكن هناك قيم في الملف .

مثال :

Properties settings = new Properties();
settings.put("title","This is simple example");
settings.put("color","Color.BLACK");

وعند إراده تخزين هذه البيانات سوف نستخدم الدالة store والتي تستقبل FileStream حتى تكتب فيه البيانات والمعامل الأخر هو نص يُكتب في أول سطر في الملف ، كما المثال :

FileOutputStream out = new FileOutputStream("mySettings.properties");
settings.store(out , "This is My Settings");

ومن المُفضل تخزين هذا الملف في مجلد المستخدم user home ، ويتم الإستعلام عن هذا المسار من خلال الدالة getProperty في الكلاس System :

String userDir = System.getProperty("user.home");

وعند إراده القرائه من الملف ، سوف نستخدم الدالة load ويمرر لها FileStream يتم القرائه منه ، كما يلي :

FileInputStream in = new FileInputStream("mySettings.properties");
settings.load(in);

لكن قد يحدث أن يتم التغيير في هذا الملف أو حتى حذفه ، لذلك يمكن أن نضع قيمه افتراضيه بحيث اذا لم يجد القيم في الملف يستخدم الإفتراضيه ، ويمكن ان نضع هذه القيم الإفتراضيه بطريقتين :

الأولى وهي من خلال استدعاء الدالة getProprty (في الكلاس Properties وليس System ) وتمرير المفتاح الذي نريد قيمته ويكون المعامل الأخر هو القيمه الإفتراضيه اذا لم يجد ذلك المفتاح :

String title = settings.getProperty("title","new title");

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

Properties defaultSettings = new Properties();
defaultSettings.put("title",new title");
defaultSettings.put("color","Color.BLACK);
//....
Properties settings = new Properties( defaultSettings );

المثال التالي لواجهه Swing تحفظ حجم الJFrame والإسم بعد أغلاق البرنامج ، ويعود الحجم عند فتحك للتطبيق مره أخرى :

// use Java Properties File to store Application size and Title

import java.awt.EventQueue;

import java.awt.event.WindowListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import java.util.Properties;

import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class PropTest {
	public static void main (String[] args ) {
		EventQueue.invokeLater ( new Runnable() { 
			public void run () { 
				PropFrame frame = new PropFrame();
				frame.setVisible(true);
			}
		});
	}
}

class PropFrame extends JFrame {
	public PropFrame () {
		// get size,position,title from properties file
		String userDir = System.getProperty("user.home");
		File propertiesDir = new File ( userDir, ".myApp" );
		if ( ! propertiesDir.exists() )
			propertiesDir.mkdir();
		propertiesFile = new File( propertiesDir , "here.test");

		Properties defaultSettings = new Properties();
		defaultSettings.put("left","0");
		defaultSettings.put("top","0");
		defaultSettings.put("width", "" + DEFAULT_WIDTH );
		defaultSettings.put("height","" + DEFAULT_HEIGHT );
		defaultSettings.put("title", "" );

		settings = new Properties( defaultSettings );

		if ( propertiesFile.exists() )  {
			try {
				FileInputStream in = new FileInputStream ( propertiesFile );
				settings.load( in );
			}
			catch (IOException ex ) {
				ex.printStackTrace();
			}
		}

		// set size, position
		setBounds ( Integer.parseInt( settings.getProperty("left")) ,
					Integer.parseInt( settings.getProperty("top"))  ,
					Integer.parseInt( settings.getProperty("width")),
					Integer.parseInt( settings.getProperty("height"))
		);


		// if no title , get title from user
		String title = settings.getProperty("title");
		if ( title.equals("") )
			title = JOptionPane.showInputDialog("please supply frame title");

		if ( title == null )
			title = "";

		setTitle(title );

		addWindowListener ( new WindowAdapter() {
			public void windowClosing ( WindowEvent event ) {
				settings.put("left", "" +  getX() );
				settings.put("top", "" + getY() );
				settings.put("width", "" + getWidth());
				settings.put("height","" + getHeight());
				settings.put("title",getTitle());

				try {
					FileOutputStream out = new FileOutputStream( propertiesFile );
					settings.store( out , "Program Settings");
				}
				catch ( IOException ex ) {
					ex.printStackTrace();
				}

				System.exit(0);
			}
		});

	}

	private Properties settings;
	private File propertiesFile;

	private static final int DEFAULT_WIDTH = 300;
	private static final int DEFAULT_HEIGHT= 200;
}

The Preferences API

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

الكلاس Preferences أتي لحل هذه المشاكل ، فهو يقوم بحفظ الإعدادات في مكان ما على حسب البيئة التي يعمل عليها البرنامج ، فاذا عمل البرنامج تحت نظام ويندوز فسوف يخزن تلك الإعدادات في مسجل النظام Registry ، أما اذا عمل تحت لينوكس سوف يخزنها في مكان في نظام الملفات . ويمكن حفظ هذه الإعدادت للمستخدم الحالى User ، أما لأي مستخدم في النظام System . كما يبين المثال :

Preferences root = Preferences.userRoot(); // for user
Preferences root = Preferences.systemRoot(); // for system

بعد ذلك يمكنك وضع الإعدادت في المكان الذي تريد ويفضل أن تكون كما في تقسيم الباكج ، المثال التالي يضعها في com/mycompany/myapp

Preferences node = root.node("/com/mycompany/myapp");

اذا أردت أن تضع المسار كما في باكج الكلاس الحالي يمكنك أن تستخدم (فقط غير obj بthis ) :

Preferences node = Preferences.userNodeForPackage(obj.getClass());
Preferences node = Preferences.systemNodeForPackage(obj.getClass());

لقرائه الإعدادت ، سوف تستخدم أحدى الدوال التالية ، وكلها تستقبل المفتاح والقيمه الإفتراضيه وترجع القيمه القيمه المناسبة (الإفتراضية اذا لم توجد قيمه للمفتاح والا القيمه الموجودة ) :

• String get(String key, String defval)
• int getInt(String key, int defval)
• long getLong(String key, long defval)
• float getFloat(String key, float defval)
• double getDouble(String key, double defval)
• boolean getBoolean(String key, boolean defval)
• byte[] getByteArray(String key, byte[] defval)

وللكتابة سوف تستخدم :

• void put(String key, String value)
• void putInt(String key, int value)
• void putLong(String key, long value)
• void putFloat(String key, float value)
• void putDouble(String key, double value)
• void putBoolean(String key, boolean value)
• void putByteArray(String key, byte[] value)

ويمكن اذا أردت أن تنقل البرنامج من بيئة لأخرى أن تقوم بعمل export للإعدادت في ملف xml ، وهناك قم بعمل import للإعدادات ، وذلك من خلال الدوال التالية:

void exportSubtree(OutputStream out)
void exportNode(OutputStream out)
void importPreferences(InputStream in)

Text-To-Speech Application :

قمت بعمل مثال بسيط ، لبرنامج لقرائه النصوص باستخدام المكتبة TTS ، أيضا استخدمت واجهات للبرنامج تسمى Substance ، وفي حال قام المستخدم بتغيير الواجهه أو تغيير صوت القارئ ومن ثم خرج من البرنامج فسوف تعود الإعدادات مره أخرى بعد فتح التطبيق .. أيضا اذا تمت الكتابة في JTextArea وتم الخروج من البرنامج فسوف ترجع ما كان موجود مرة أخرى عند فتح التطبيق ..

الكود حمله من هنا (بدون المكتبات) :

Text-To-Speech-Example

Prefer.PNG

لترجمه وتشغيل التطبيق ، حمل المكتبة Substance وضع الملف substance.jar في مسار البرنامج ، أيضا حمل المكتبة TTS وضع مجلد lib بالكامل في مسار البرنامج ، وأفضل أن تقوم بعمل ملف الباتش make.bat هذا للتسهيل وقم بتشغيله :

cls
@echo off
javac -cp .;lib\freetts.jar TextToSpeech.java
if not errorlevel 1 java -cp .;lib\freetts.jar;substance.jar TextToSpeech
pause

أما من يريد الكود + المكتبة للتجربة فقط فيمكنه تحميل هذا الملف ( 10 ميغا ):

Code + Library

وقم بتشغيل ملف الmake.bat ..

أي سؤال أو ملاحظة ، يرحب به ..

والسلام عليكم ورحمه الله وبركاته ..

تم تعديل هذه المشاركة بواسطة Wajdy Essam في 14 سبتمبر 2009 في 04:02 — السبب: المنتدى لا يقبل المرفقات !!!

1

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#2

شكرا على المقالة، ودائما أتساءل كيف تستطيع أن تكتب مقالات طويلة كهذه، قرائتها فقط متعبة، فما بالك بكتابتها :lol:

بالنسبة لPreferences فعندي حساسية منها وأفضل استعمال Properties

أولا لأني لا أعرف مكان التخزين، وفي حالة ما إذا كان عندي برنامج ووفرت معه uninstaller، ففي حالة الProperties فموقع الملف معروف وبالتالي سهل إزالته، أما بالنسبة لPreferences فالمشكل هو أن المكان غير معروف ويعتمد على النظام، وإذا ما تم تثبيت البرنامج على نفس الجهاز فاحتمال أنه يشتغل بالمعطيات الخاصة بالبرنامج القديم

ثانيا، في حالة عندي برنامج يكون عنده مثلا ملف تكون فيه الdefault configuration، في حالة Properties ممكن أخزنهم في ملف config.properties وأضع الملف في الjar، لكن مع الpreferences فكيف العمل؟؟

بالنسبة لText to speach فأنا تعقدت منه من زمان لأن ولا واحد يدعم اللغة العربية، وأغلبهم يقتصر على اللغة الإنجليزية فقط

نقطة أخيرة، انت عارف أننا سننضم حفل عزاء لمنتدانا العزيز، وعلاء الله يهديه لا يريد أن يتكفل بالمصاريف، ما رأيك لو تتكفل بها أنت :lol:

تم تعديل هذه المشاركة بواسطة herch في 14 سبتمبر 2009 في 04:16

#3

حياك الله أخي عصام :) ..

الPreferences تتميز بالabstraction وكل ما تريده تستطيع عمله بسهوله وبدون أن تعرف طبيعة الback-end ، يعني اذا أردت أن تحذف الإعدات الحالية فقط استدعى الدالة clear وهي ستتولى كل شيء ..

مثلا أراد المستخدم أن ينقل اعداداته الحالية لنظام أخر يمكنك توفير خاصيه export/import وسيتم ذلك بكل سهوله .

بالنسبة لtts فهو جا على بالي كده على الطاير :) .. وكلامك صحيح لا يوجد أي TTS عربي ،، تقريبا لدى صخر واحد ولكنه غير مجاني ..

مصاريف ؟؟!!

:salam:

سلام عليكم :lol:

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#4

:resentful: يعني المسألة فيها clear و import/export. يعني أنا غلطان... :tantrum:

في الحقيقة كنت جربت منذ كم شهر طريقة حفظ خصائص النافذة في ملف properties ثم تطبيقها عند إعادة تشغيل البرنامج، وفكرت وقتها أني عبقري زماني لأني عملت هذا الشيء، وأن كبريات الشركات ستتسابق لتوظيفي :haha: ، لكن لا شيء من ذلك حصل، ومازلنا منتظرين الفرج -_-

متى ستضع مشروع تخرجك الخاص بالفيروسات على ما أتذكر؟؟

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

راودني نفس الشعور بالضبط :lol: ... ان شاء الله تفرج لنا ولكم أجمعين ...

بالنسبة للon-demand scanner فهو غير مفيد فلا يخلعك الأسم :) ،، ومنذ ذلك الوقت لم أقم بزياده ولو سطر واحد ، على العموم بعد العيد ان شاء الله سأقوم بالتطوير سواء في الجزء النظري والعملي أيضا .. وبعدها سأنزلهم واتمنى أن أنتهي قبل نهايه السنه الحالية ..

دمت بخير أخ عصام ،،

تم تعديل هذه المشاركة بواسطة Wajdy Essam في 14 سبتمبر 2009 في 04:56

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#6

ماشاء الله عنك يا وجدي ,, فعلا انت شخص مبدع

بارك الله فيك على المقالة

#7

رائع يا وجدي استمر

يا عصام حرام عليك سيب العالم في حالها

كلنا هنا مفلسون

ما رأيك أن تتكلف بالمصاريف أنت :)

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

والله يا عصام نفس الشعور كان عندي

على كل ربنا يفرجها بفرجه علينا وعليكم

تحياتي

حزمة المحرك الإصدارة 0.8

أي أحد يجد أني ظلمته فليراسلني

وبإذن الله لو كان له حق سيأخذه

728x90.png

مواضيع مشابهة