بایگانی نویسنده

افزایش سرعت جستجو در مقادیر هنگفتى از اطلاعات متنى

دی ۱۹م, ۱۳۸۷

هدف از این سرى مقالات ارائه و بررسى روش هایى است که براى افزایش سرعت جستجو در مقادیر هنگفتى از اطلاعات متنى استفاده مى شود. راههاى بیان شده در این مقاله همگى توسط راقم سطور یا نهادهاى دیگرى آزمایش شده و بسیار عالى پاسخ داده اند.

فهرستى از مطالب ارائه شده در این مقالات

  • بررسى کلى صورت مساله
  • روش Indexing ساده
  • روش Indexing و Compacting پیشرفته
  • بررسى Indexing Service مربوط به بانکهاى اطلاعاتى و آشنایى با Indexing Service در SQL Server و ویندوز ۲۰۰۰

بررسى کلى صورت مساله
امروزه سیستم هاى اطلاعاتى جزء لاینفک مراکز تحقیقاتى است. یک مرکز تحقیقاتى از یک سیستم اطلاعاتى تواقعاتى را دارد؛ همچون افزایش ضریب امنیت، افزایش سرعت عمل، کاهش زمان تحقیق، نائل شدن به بیشترن اطلاعات ممکن، موضوع بندى بودن اطلاعات و …
یک مرکز تحقیقاتى باید به امنیت سیستم تحقیقاتى خود اطمینان داشته باشد؛ یک مرکز تحقیقاتى باید اطمینان داشته باشد که اطلاعات او امن هستند و امکان از بین رفتن اطلاعاتش به هر طریق از جمله نفوذ ویروس، اشکالات سیستم، ایزوله نبودن سیستم و … از بین نمى رود. یک مرکز تحقیقاتى باید مطمئن باشد که از بیشترین پناسیل اطلاعاتى خود استفاده مى کند و مسائلى از این قبیل
به علاوه یک مرکز تحقیقاتى نیاز دارد که یک سیستم اطلاعاتى بتواند زمان تحقیق را کاهش دهد و در کمترین زمان به بیشترن اطلاعت ممکن برسد و همچنین داده ها در یک ساختار درختى یا چیزى شبیه به آن دسته بندى شده باشند تا دسترسى از کل به جزء امکان پذیر باشد و در مدت زمان کمى به نتایج دلخواه خود برسد. اما شاید افزایش سرعت عمل یکى از مهمترین آرزوهاى یک مرکز تحقیقاتى است. البته در اینجا نباید مسئله امنیت و نائل شدن به بیشترین اطلاعات ممکن را فراموش کنیم. آن مرکز علاوه بر این که تمایل دارد به بیشترین اطلاعت ممکن برسد، همچنین مایل نیست که اطلاعات او دچار اختلال شود.
بنابراین یک مرکز تحقیقاتى علاقه دارد که در مورد مسئله سرعت به نتایج زیر برسد:

  • افزایش سرعت
  • افزایش ضریب کارایى یا راندمان تحقیق
  • افزایش امنیت
  • کاهش مدت زمان جستجو
  • استفاده از کمترین امکانات سخت افزارى و نرم افزارى جهت کاهش هزینه‌ها

راقم سطورسعى دارد در این سلسه مقالات راجع به نیل به این اهداف بحث کرده و مساله را به طور عمیقتر بشکافد.

روش Indexing
این روش شاید یکى از سهل الوصول ترین روشها است که مى توانید در سیستم هاى کوچک و حتى متوسط به راحتى پاسخگوى نیازها باشد.
شاید، براى شروع بهتر باشد با کلمه Indexing بیشتر آشنا شویم:
دیکشنرى انگلیسى به انگلیسى Babylon کلمه Indexing را این گونه توضیح داده است:

n. act of creating an index; act of labelling and arranging data in an index table for easy access(Computers)

عمل درست کردن فهرست یا شاخص؛ عمل نام گذارى و سازمان دهى کردن اطلاعات در یک جدول شاخص براى دسترسى آسانتر (رایانه)

دیکشنرى کامپیوتر مایکروسافت نیز در توضیح لغت indexed search این گونه مى نویسد:

n. A search for an item of data that uses an index to reduce the amount of time required.

جستجو به دنبال فقره‌اى از اطلاعات. در این شیوه جستجو از یک فهرست یا شاخص براى کاستن مقدار زمان مورد نیاز استفاده مى شود.

حال که فهمیدیم Indexing یعنى چه، مى توانیم به بررسى روش مذکور بپردازیم. فرض کنید سازمان شما بخواهد در ۳،۰۰۰،۰۰۰ صفحه اطلاعات متنى جستجو کند. در آن صورت شما ممکن است در ابتدا تصمیم بگیرید از Query استفاده کنید. ولى مطئمنا پس از انجام اولین تست از ایده خود منصرف خواهید شد؛ زیرا متوجه می‌شوید که زمان مورد نیاز براى جستجودر ۳،۰۰۰،۰۰۰ صفحه اطلاعات سازمان شما بسیار بسیار بیشتر از تصور شما بوده است.
حالا اجازه بدهید ببینیم Indexing براى این مساله چه راه حلى ارائه مى دهد؟
کافى است به سادگى جدولى از لغات غیرتکرارى متون بسازید و در آن جستجو کنید. اجازه دهید ببینیم چگونه مى شود چنین کارى کرد؟ کاراکتر ” ” یا Space مهمترین جداکننده لغات است. مى توان به سادگى یک Parser یا تجزیه کننده متن نوشت که این لیست غیرتکرارى را درست کرده و در یک Table بریزد. فعلا به عنوان یک تجربه نوع Database استفاده شده مهم نیست ولى من Microsoft Access را پیشنهاد مى کنم چون فعلا میتواند نیازهاى ما را به طور کامل برطرف کند.
شیوه کار بسیار ساده است. Parser شما باید لغات را بر حسب کاراکتر ” ” یا Space از هم جدا کرده و در صورتى که این لغت قبلا در بانک مربوطه ریخته نشده بود آن را به بانک اضافه کند. ساختار Table مذکور نیز باید چیزى شبیه به این باشد:

نام فیلد نوع اندازه توضیحات
Word String 15 کلمه
Positions Memo 0 موقعیتها – تشریح خواهد شد
جدول ۱ – ساختار بانک KeyWords

البته تجزیه کردن متون با شیوه مذکور خالى از اشکال نیست؛ یکى از مهمترین اشکالات این روش این است که به عنوان مثال بین لغت “انفورماتیک” و “انفورماتیک،” تفاوت قائل میشود و دو رکورد مجزا براى آنها ایجاد مى کند. ولى فعلا همین Parser ساده مشکل ما را حل میکند. براى یک پروژه واقعى شاید لازم باشد یک Parser قوى تر بنویسید و آن را از جهات سرعت بهینه سازى کنید.
همان طور که دیدید Tableما علاوه بر فیلد Word داراى فیلد دیگریبود به نام Positions. این فیلد مسئولیت کلیدى را در شیوه جستجوى ما بازى مى کند. هر بار که شما یک کلیدواژه را به بانک اضافه میکند، باید آدرس آن کلیدواژه را نیز به طور دقیق در فیلد Positions وارد کنید. این مساله که چگونه آدرس مذکور را درج کنید، بسته به شکل طراحى Database شماست. ولى اجازه دهید فرض کنیم شما متون را در قالب زیر و با ساختار Master-Detailى نگهدارى مى کنید:

نام فیلد نوع اندازه توضیحات
+ID AutoInc 0 ID
BookName String 30 نام کتاب
جدول ۲ – ساختار بانک Master
نام فیلد نوع اندازه توضیحات
+ID AutoInc 0 ID
Code Integer 0 Used for creating Relation
PageNum Integer 0 شماره صفحه
Text Integer 0 متن صفحه مربوطه
جدول ۳ – ساختار بانک Detail

طبق این ساختار شما نام کتابهاى خود را در جدول Master و متن کتابها را در جدول Detail نگهدارى مى کنید. به علاوه بین جدول Master و Detail یک ارتباط یک به چند موجود است. این ارتباط از طریف فیلد Code برقرار مى شود.
خوب … با توجه به ساختار مذکور شما مى توانید آدرس کلمات را با استفاده از فرمت زیر در بانک KeyWords وارد کنید:

<Master.ID>,<Detail.PageNum>;<Master.ID>,<Detail.PageNum>;<Master.ID>,<Detail.PageNum>;….

براى این که مساله بیشترى روشن تر شود به الگوریتم کلى کار نگاهى بیاندازید:

  • کلمه به کلمه در متن جلو بروید.
  • هر کمله را پس از این که مطمئن شدید قبلا در بانک KeyWords وارد نشده، در فیلد Word همین بانک وارد کنید.
  • آدرس این کلمه را نیز به شکلى که گفته شده در فیلد Positions وارد کنید. تنها لازم است هر بار یک آدرس را وارد کنید و براى وارد کردن آدرس بعدى از یک کاراکتر “;” استفاده کرده و آدرس بعدى را اضافه کنید.
  • از زندگى لذت ببرید و آماده باشید که به تشریح چگونگى جستجو در متون بپردازیم….

دیگر راه درازى در پیش نداریم. به هدف رسیده ایم. فرض کنید در برنامه نهایى کاربر درخواست جستجو در لغت “انفورماتیک” را صادر کرد. برنامه شما چه باید بکند؟ خیلى ساده است. به جایى این که در متون جستجو کنید در بانک KeyWords جستجو کنید. جستجو در این بانک به سرعت باد انجام مى گردد زیرا حجم اطلاعات آن پایین است (قبلا گفتیم که این بانک حاوى لغات تمامى متون ولى به صورت غیرتکرارى است).
برنامه شما باید در صورتى که رکور “انفورماتیک” در بانک KeyWords نبود یک پیغام خطا به کاربر نشان دهد و در غیر این صورت آدرس هاى مذکور را از حالت کد شده در بیاورد و به کاربر بگوید که در کتاب “فلان” و در صفحه شماره “فلان” لغت مورد نظر شما موجود است.
اگر از این روش استفاده کنید مطمئن باشید که از سرعت فوق العاده آن شگفت زده مى شوید. در واقع ما یک بار خودمان تمامى جستجوهاى ممکن را انجام دادیم و حالا در جستجوهاى خودمان مورد نیاز کاربر را مى یابیم
اما یک مساله مهم دیگر هنوز باقیست. و آن جستجوى شرطى است. اگر بخواهید به برنامه خود قابلیت جستجوى AND – OR یا NOT را بدهید، چه؟
انجام دادن چنین کارى ساده نیست ولى به پیچیدگى خود روش Indexing هم نیست. فرض کنید رشته ورودى کاربر شما اینگونه باشد: “انفورماتیک AND ایران AND معظلات”.
حالا برنامه شما باید رکوردهاى “انفورماتیک”، “ایران” و “معظلات” را در بانک KeyWords فیلتر کند و مواردى که این سه لغت در یک صفحه واقع شده اند را به کاربر گزارش دهد.
شاید یک مساله دیگر هم ذهن شما را به خود جلب کرده است و آن مساله حجم است. شاید از خود بپرسید: “خوب، این روش خوبى است ولى احتیاج به حجم خیلى زیادى دارد!”. در پاسخ شما باید بگویم که نه این چنین نیست. حجم مورد نیاز شما بسیار اندک است. زیرا که بانک شما حاولى کل لغات متون شما نیست بلکه حاوى لغات غیرتکرارى متون است.
به علاوه اگر خیلى اصرار دارید که از حجم کمترى استفاده کنید میتوانید آدرس ها را کد کنید.
یک روش کدکردن اعداد وجود دارد که خیلى خیلى کاراست. تا عدد ۶۵،۵۳۶ را میتوان فقط با دو بایت ذخیره کرد.
انجام این کار خیلى خیلى ساده است.
بگذارید با مثال این مساله را به مطرح کنم.

عدد معادل فشرده شده
۲۵۶ #۲۵۶
۲۵۷ #۱#۱
۲۵۸ #۱#۲
۵۱۲ #۱#۲۵۶
۵۱۳ #۲#۱
۱۰۰۰ #۳#۲۳۲
جدول ۴ – نمونه اعداد کد شده

توجه: منظور از #۱ کد اسکى شماره ۱ است.
نمى دانم تا به حال متوجه منظور من شده اید یا نه. اگر متوجه نشدید به الگوریتم کارنگاهى بیاندازید

  • عدد مورد نیاز خود را تقسیم بر ۲۵۶ بکنید و خارج قسمت و باقیمانده را در جایى یادداشت کنید.
  • کاراکتر اسکى خارج قسمت و سپس کاراکتر اسکى باقیمانده کدشده عدد شما است

بالاترین ۹ بسته Installساز

دی ۵م, ۱۳۸۷

بالاترین ۹ بسته Installساز
اگر به دنبال ابزارى میگردید که برنامه خود را منتشر کرده و تحویل دهید بیش از این نگردید! این گزارش را بخوانید.

DeployMaster
اگر به دنبال یک ابزار مناسب براى توزیع انتشار برنامه ویندوزى خود یا فایلهاى کامپیوترى خودتان مى‌گردید و به علاوه سایر Installسازها شما را خسته کرده‌اند آنگاه JGsoft DeployMaster راه حلى است که شما لازم دارید.

Emma Setup
نسخه ۳ Emma Setup یک Installساز ۳۲ بیتی است. لازم نیست که یک برنامه‌نویس باشید تا بتوانید یک Install شخصى و حرفه‌اى بسازید. در حالى که خصوصیات حرفه‌اى خیلى زیادى دارد قیمت آن بسیار پایین و کار کردن با آن بسیار ساده است. این برنامه برای بسیارى از موارد بهترین انتخاب است.

gInstall
فقط تصور کنید که یک Installer قابل Skin گذارى چقدر مى‌تواند مفید باشد. مشتریان شما برنامه شما را از همان آغاز دوست خواهند داشت. اجازه دهید مشتریانتان با استفاده از شرکت یا برنامه شما خودشان را بشناسند. آیا دوست ندارید Setup برنامه شما دقیقا به شکل و تیپ برنامه خودتان باشد.

Install Shield
Install Shield قابلیت کنترل کامل بر روى Installer ویندوز را به شما می دهد و خصوصیات جدید و قدرتمند Installer ویندوز را در اختیار شما قرار می‌گذارد. برنامه نویسان بدون نوشتن حتی یک خط کد می توانند از تمامی قابلیتهای Installer ویندوز استفاده کنند.

Wise Solutions
Wise انقلاب بعدی در زمینه ساخت Installer برای ویندوز است که کاربران را کاملا در سیکل زمانی ساخت Setup قرار می دهد. شما قابلیتهایی از قبلی ساخت Installهای حرفه‌ای و قابل اطمینان را خواهید داشت.

Inno Setup
Inno Setup یک Setup ساز رایگان برای ویندوز است که تمامی نسخه های ۳۲بیتی ویندوز را پیشتیبانی می کند. اگرچه رایگان است ولی بسیار بسیار قدرتمند است و سورس آن به زبان دلفی نیز مهیا می باشد.

ICE (Install Creation Environment)
یک Installساز قوی که به شما اجازه می دهد Setup برنامه های خود را که با دلفی یا C++ Builder نوشته اید بسازید. احتیاج به یادگرفتن یک زبان scripting نیست. کار کردن با این نرم افزار بسیار ساده است و اصولا برای ساخت Install برای دلفی و C++ Builder ساخته شده است.

GP-Install
GP-Install یک Setup ساز است که خصوصیات “ساده بودن” و “قابل درک بودن” را با هم ترکیب کرده است. Setup ساخته شده بسیار فشرده است. و به شما اجازه می دهد که Setup خود را هر گونه که دوست دارید بیارایید. جالبتر از همه این که آن رایگان است.

Instyler
اگر به دنبال یک Installer ارزان قیمت و آسان میگردید تا برنامه های خود را انتشار دهید SmartSetup را آزمایش کنید. این برنامه قابلیت هایی همچون: مدیریت پروژه، انوع مختلف Setup، سازگاری با Registry/Ini، شکل قابل تغییر Setup، پشتیبانی از uninstallat، امکان upgrade، امکان حفاظت با استفاده از رمز، استفاده ار shortcutها و بسیاری خصوصیات استاندارد و حرفه ای دیگر.

مجموعه Tip شماره ۲ ایران دولوپرز دات کام

دی ۵م, ۱۳۸۷
مجموعه Tip شماره ۲ ایران دولوپرز دات کام
گردآورى: محمد باقر معممورى
تنظیم کردن رمز Paradox به طور اتوماتیک
{
The table component's ACTIVE property
must be set to FALSE.
Then, put this code on the form's create event:
}
Session.AddPassword('My secret password');
TableName.Active := True;

فعال/غیرفعال کردن دکمه “START” ویندوز
//Enable:
EnableWindow(FindWindowEx(FindWindow
  ('Shell_TrayWnd', nil), 0,'Button',nil),TRUE);
//Disable:
EnableWindow(FindWindowEx(FindWindow
  ('Shell_TrayWnd', nil), 0,'Button',nil),FALSE);

آیا IDE در حال اجرا است؟
{
If you want to check whether IDE is runing or not
try this function: (Supose you want your new
shareware component to work only when IDE is
running, or something like that)
}
Function RunningInTheIDE: boolean;
Begin
  Result:=FindWindow('TAppBuilder', nil) > 0;
End;

nil), 0,'Button',nil),FALSE);

پیغام های Hint و Warning
{
It's a good idea to turn on hint and warning
messages specially when writing and compiling
your own code. Although some of these hints and
warnings can be ignored, most of them - if
nothing else - will remind you of things you
could do to improve your code.

- Go to "Project | Options"
- Change to the "Compiler" tab
- Make sure "Show hints" and "Show warnings"
check boxes are checked
}

آشکار/مخفی کردن TaskBar در ویندوز ۹۵
//To hide the task bar use
ShowWindow(FindWindow
   ('Shell_TrayWnd',nil), SW_HIDE);

//To show the task bar use
ShowWindow(FindWindow
   ('Shell_TrayWnd',nil), SW_SHOWNA);

مجموعه Tip شماره ۳ ایران دولوپرز دات کام

دی ۵م, ۱۳۸۷
مجموعه Tip شماره ۳ ایران دولوپرز دات کام
گردآورى: محمد باقر معممورى
مقایسه یک رشته با یک الگو
{
Sometimes we need to know if a string matches a
pattern, which is a string with wildcards
(for example ? and *).

Usage:
if IsLike('About Delphi', 'Abo?? Delp*') then
  ShowMessage('A match!');
}

uses SysUtils;

function IsLike(AString, Pattern: string): boolean;
var
  i, n, n1, n2: integer;
  p1, p2: pchar;
label
  match, nomatch;
begin
  AString := UpperCase(AString);
  Pattern := UpperCase(Pattern);
  n1 := Length(AString);
  n2 := Length(Pattern);
  if n1 < n2 then n := n1 else n := n2;
  p1 := pchar(AString);
  p2 := pchar(Pattern);
  for i := 1 to n do begin
    if p2^ = '*' then goto match;
    if (p2^ <> '?') and (p2^ <> p1^) then goto nomatch;
    inc(p1); inc(p2);
  end;
  if n1 > n2 then begin
nomatch:
    Result := False;
    exit;
  end else if n1 < n2 then begin
    for i := n1 + 1 to n2 do begin
      if not (p2^ in ['*','?']) then goto nomatch;
      inc(p2);
    end;
  end;
match:
  Result
end;	

پیدا کردن Handle یک چاپگر بر اساس نام آن
{
Here's how to get a Printer Handle
provided with the Printer name:
}

function PrinterHandleByName(PrinterName : string):THandle;
var
  PtrName : PChar;
begin
  Result:=0;
  PtrName := PChar(PrinterName);
  if not OpenPrinter(PtrName, Result, nil) then
    RaiseLastOSError
  else
  begin
    ClosePrinter(Result);
  end
end;

تبدیل String به Enum
{
Here's how to get the value of
an enumerated type constant
given its string representation.

Suppose enum:

TProgrammerType = (tpDelphi, tpVisualC, tpVB, tpJava);
}

uses TypInfo;

var
 s: string;
 programmer : TProgrammerType;
begin
 s:='tpDelphi'; // string
 programmer := TProgrammerType(GetEnumValue(
                TypeInfo(TProgrammerType),s));
end;

Export کردن اطلاعات به XML

دی ۵م, ۱۳۸۷

خیلی اوقات در برنامه نویسی اینترنت و همچنین در برنامه های بزرگ نیاز دارید که قابلیت Export کردن یک Dataset را به XML به کاربرانتان بدهید.
در این مقاله می توانید شیوه های مختلفی برای حل این مساله بیابید. البته شاید هم اکنون حس کنید که اصولا نیاز به همچنین مساله ای در پروژه های ایرانی محسوس نیست. نگارنده این مقاله تقریبا با شما هم عقیده است ولی قطعا روزی خواهد رسید که این نیاز به خوبی خود را نشان بدهد. به علاوه اگر شما قصد دارید برنامه نویسی اینترنت بکنید حتما باید اطلاعات خود را راجع به XML و دلفی گسترش دهید. همچنین اگر قصد دارید پروژه های شبکه بسیار بزرگ برای سازمان های بزرگ بنویسید مطمئن باشید XML می تواند در قسمتی از کارهایتان شما را یاری بدهد.
Export کردن یک Table به XML از طریق ADO
انجام دادن این کار از طریق ADO به راحتی استفاده از متد SaveToFile است.
به کد زیر نگاهی بیاندازید:

     

ADOTable1.SaveToFile('C:\test.xml', pfXML);

همچنین با استفاده از کد زیر می توانید فایل XML مربوطه را Load کنید:

     

ADOTable1.LoadFromFile('C:\test.xml');

البته به جای pfXML می توانید از pfADTG می توانید استفاده کنید که در این صورت اطلاعات در یک فایل به صورت Binary نوشته می شود که به موضوع مبحث ما ربطی ندارد.
Export کردن یک Table به XML با استفاده از ClientDataSet
برای انجام این عمل با استفاده از ClientDataSet از مراحل زیر استفاده کنید:
۱- یک کمپوننت TDataSetProvider (این کمپوننت در تب DataAccess قرار دارد) بر روی فرم خود بگذارید و با استفاده از پراپرتی DataSet آن را به Table یا Query و یا به طور کلی به Dataset مورد نیاز خود متصل کنید.
۲- یک کمپوننت TClientDataSet (این کمپوننت هم در تب DataAccess قرار دارد) بر روی فرم خود بگذارید و مقدار ProviderName آن را برابر نام کمپوننت TDataSetProviderی قرار دهید که در مرحله ۲ ساختید.
۳- به سادگی از متد زیر استفاده کنید:

   

ClientDataSet1.Active := True;
   ClientDataSet1.SaveToFile('C:\1.XML', dfXML);

پارامتر دوم متد SaveToFile می توانید شامل مقادیر زیر باشد: dfBinary, dfXML, dfXMLUTF8.
بررسی راجع به dfBinary به مبحث مقاله ما مربوط نمی شود.
حتما متوجه شده اید که تفاوت dfXML و dfXMLUTF8 چیست؟ dfXMLUTF8 برای Export اطلاعات به XML با فرمت Unicode استفاده می شود.
البته راههای دیگری هم وجود دارد که در نسخه های قدیمی تر دلفی به کار می آیند.
به عنوان مثال به این مقاله نگاه کنید:

http://www.scalabium.com/faq/dct0079.htm

این مقاله یک Procedure ارائه کرده است که به خوبی عمل می نماید و در صورتی که شما از نسخه های قدیمی تر دلفی استفاده می کنید یا از نسخه Professional دلفی استفاده می کنید که ClientDataSet در آن موجود نیست می توانید از این مقاله استفاده کنید. (البته به احتمال زیاد در ایران اکثر برنامه نویسان دلفی از دلفی ۶ یا ۷ و نسخه Enterprise استفاده می کنند و دیگر از این نظری مشکلی نخواهند داشت)

موفق و موید و منصور باشید
معموری

استفاده از XML به عنوان ذخیره کننده اطلاعات

دی ۳م, ۱۳۸۷

XML می تواند دقیقا همچون یک Table برای شما عمل نماید. در واقع شما می توانید با استفاده از XML اطلاعات خودتان را در قالب یک جدول ذخیره کنید.
برای این کار می توانید از کمپوننت ClientDataSet استفاده کنید. (این کمپوننت را می توانید در تب DataAccess بیابید). در واقع این کمپوننت یک In-Memory Table است. یعنی اطلاعات خود را در حافظه نگهداری می کند و به همین علت نیز فوق العاده سریع است.
نگاهی به کد زیر بیاندارید و با Load کردن یک XML و درست کردن آن آشنا شوید:

     ClientDataSet1.FileName := ExtractFilePath(
Application.ExeName) +
 'test.xml';
     if FileExists(ClientDataSet1.FileName) then
        ClientDataSet1.Open
     else
     begin
          //AddFieldDef version
          with ClientDataSet1.FieldDefs do
          begin
               Clear;
               with AddFieldDef do
               begin
                    Name := 'ID';
                    DataType := ftAutoInc;
               end; //with AddFieldDef do
               with AddFieldDef do
               begin
                    Name := 'First Name';
                    DataType := ftString;
                    Size := 20;
               end; //with AddFieldDef do
               with AddFieldDef do
               begin
                    Name := 'Last Name';
                    DataType := ftString;
                    Size := 20;
               end; //with AddFieldDef do
          end; //with ClientDataSet1.FieldDefs
          ClientDataSet1.CreateDataSet;
     end; //else

همان طور که در این کد مشاهده می کنید در ابتدا سعی شده است که فایل test.xml را که در شاخه خود برنامه قرار دارد Load کنیم و در صورتی که این فایل پیدا نشد. آنگاه می توان به سادگی (همان طور که می بینید) این فایل را از ابتدا تعریف کرد. فیلدهای مورد نظر و مشخصات مورد نیاز خود را وارد کنیم و همچنین در صورتی که نیاز بود با استفاده از IndexDefs یک یا چند Index جدید ساخت.

به چند نکته راجع به این کد توجه کنید:
۱- با استفاده از متد FieldDefs.AddFieldDef مربوط به ClientDataSet می توانید یک فیلد جدید درست کنید.
۲- برای ساخت Index نیز می توانید از متد IndexDefs.AddIndexDef مربوط به ClientDataSet استفاده کرد.
۲- هر فیلد می توانید شامل انواع زیر باشد:

type TFieldType = (ftUnknown, ftString, ftSmallint, ftInteger, ftWord, ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary, ftCursor, ftFixedChar, ftWideString, ftLargeint, ftADT, ftArray, ftReference, ftDataSet, ftOraBlob, ftOraClob, ftVariant, ftInterface, ftIDispatch, ftGuid, ftTimeStamp, ftFMTBcd);

بنابراین شما به جای ftString و یا ftAutoInc می توانید از هر کدام از این ثابت ها استفاده کنید.
همان طور که مستحضرید به راحتی می توانید در XML حتی از BLOB هم استفاده کرده و در آن صدا یا تصویر ذخیره کنید.
۳- از متد CreateDataSet پس از ساخت فیلدهای مورد نظرتون استفاده کنید تا این تغییرات تثبیت بشود.
۴- یک ClientDataSet را می توانید به سادگی به یک کمپوننت DataSource وصل کنید. پس از آن می توانید از هر کمپوننت Data-Aware مثل DBGrid برای Browse یا ویرایش اطلاعات استفاده کنید.
۵- با استفاده از Filter و Filtered می توانید به سادگی اطلاعات رو فیلتر کنید و به علاوه به سادگی می توانید از Locate و FindKey و FindNearest برای جستجو در Tableتون استفاده کنید.
۶- در ClientDataSet هم می توانید از Lookupها و فیلدهای محاسباتی استفاده کنید. همچنین می توانید دو نوع فیلد دیگه به اسم InternalCalc و Aggregate رو هم استفاده کنید. راهنمای مربوط به InternalCalc و Aggregate دلفی رو در این جا آورده ام:

InternalCalc:
Retrieves values calculated at runtime by a client dataset and stored with its data (instead of being dynamically calculated in an OnCalcFields event handler). InternalCalc is only available if you are working with a client dataset. Values calculated for an InternalCalc field are stored and retrieved as part of the client dataset’s data.

Aggregate:
Retrieves a value summarizing the data in a set of records from a client dataset.

موفق و موید و منصور باشید
معموری

نوشتن همه کمپوننتهاى در یک فایل XML با استفاده از TXMLDocument

دی ۳م, ۱۳۸۷

با استفاده از این مقاله و کدهای ارائه شده در آن یاد خواهید گرفت که مشخصات کمپوننتها را در یک فایل XML بنویسید. این مقاله از جهاتی بسیار جالب است. شاید هیچ گاه نوشتن مشخصات و خود کمپوننت در یک فایل XML مورد نیاز شما نباشد. ولی با خواندن این مقاله طریقه استفاده از TXMLDocument را در عمل یاد می گیرید.

شیوه کلی کار تقریبا مشخص است:

for k:=0 to MyComponent.ComponentCount-1 do
begin
if not
(MyComponent.Components[k] is TControl) then
Continue;
// Here create node of the XML-document
end;

اما مشکل این جاست که باید در فایل XML ساختار پدر – فرزندی در حین ساخت گزینه ها رعایت شود. کد ارائه شده در این مقاله کاملا کار می کند و شما می توانید برای انجام این کار از آن استفاده کنید:

// Writing all controls of given component to TXMLDocument
according parent-child relations
Procedure Get_Component_as_XML
(AXMLDoc : TXMLDocument; AComponent : TComponent);
Var
k : Integer;
Ctrl : TControl;
List : TList;
XMLNode : IXMLNode;
GrCtrl : TGraphicControl;

Procedure Add_WinControlAttributes(AWCtrl :
TWinControl; AXMLNode : IXMLNode);
begin
with
AXMLNode do
begin
Attributes['Name'] := AWCtrl.Name;
Attributes['Owner'] := AWCtrl.Owner.Name;
Attributes['Left'] := IntToStr(AWCtrl.Left);
Attributes['Top'] := IntToStr(AWCtrl.Top);
Attributes['Width'] := IntToStr(AWCtrl.Width);
Attributes['Height'] := IntToStr(AWCtrl.Height);
// … add all needed properties
end;
end;

Procedure Add_GraphicsControlAttributes(AGCtrl : TGraphicControl;
AXMLNode : IXMLNode);
begin
with
AXMLNode do
begin
Attributes['Name'] := AGCtrl.Name;
Attributes['Owner'] := AGCtrl.Owner.Name;
Attributes['Left'] := AGCtrl.Left;
Attributes['Top'] := IntToStr(AGCtrl.Top);
Attributes['Width'] := IntToStr(AGCtrl.Width);
Attributes['Height'] := IntToStr(AGCtrl.Height);
// … add all needed properties
end;
end;

Procedure Add_XML_Node(AWCtrl : TWinControl; AXMLNode : IXMLNode);
Var
i : Integer;
WinC : TWinControl;
SubIXMLNode,
NewStock : IXMLNode;
SubCtrl : TControl;
SubGrCtrl : TGraphicControl;
begin
NewStock := AXMLNode.AddChild(AWCtrl.ClassName);
Add_WinControlAttributes(AWCtrl, NewStock);

if AWCtrl.ControlCount > 0 then
for
i:=0 to AWCtrl.ControlCount-1 do
begin
SubCtrl := TControl(AWCtrl.Controls[i]);
if List.IndexOf(SubCtrl) >= 0 then
Continue;

// take a special look at this list
List.Add(Pointer(SubCtrl));

if SubCtrl is TGraphicControl then
begin
SubGrCtrl := TGraphicControl(SubCtrl);
SubIXMLNode := NewStock.AddChild(SubGrCtrl.ClassName);
Add_GraphicsControlAttributes(SubGrCtrl, SubIXMLNode);
end

else if SubCtrl is TWinControl then
begin
WinC := TWinControl(SubCtrl);
Add_XML_Node(WinC, NewStock);
end;
end;
end;

begin
AXMLDoc.XML.Clear;
AXMLDoc.XML.Add(Format(‘<%s>’, [AComponent.Name]));
AXMLDoc.XML.Add(Format(‘</%s>’, [AComponent.Name]));

AXMLDoc.Active := True;

List := TList.Create; // special list to hold controls added to XML
try
for
k:=0 to AComponent.ComponentCount-1 do
begin
if not
(AComponent.Components[k] is TControl) then
Continue;
Ctrl := TControl(AComponent.Components[k]);
if List.IndexOf(Ctrl) >= 0 then
Continue;

// take a special look at this list
List.Add(Pointer(Ctrl));

if Ctrl is TGraphicControl then
begin
GrCtrl := TGraphicControl(Ctrl);
XMLNode := AXMLDoc.DocumentElement.AddChild(GrCtrl.ClassName);
Add_GraphicsControlAttributes(GrCtrl, XMLNode);
end

else if Ctrl is TWinControl then
// Recursive process for child controls
Add_XML_Node(TWinControl(Ctrl), AXMLDoc.DocumentElement);
end;
finally
List.Clear;
List.Free;
end;
end;


همان طور که گفتم شاید اصولا نیاز به انجام این کار آن چنان وجود نداشته باشید ولی با استفاده از این مقاله شما به خوبی یاد خواهید گرفت که از TXMLDocument استفاده کنید.
انشاء الله در آینده مقالات دیگری را برای آشنایی با کمپوننت فوق العاده جذاب و قوی TXMLDocument ارائه خواهم داد.

موفق و موید و منصور باشید
معموری

چرخش Bitmap در دلفی

دی ۳م, ۱۳۸۷

مرجع: http://www.delphi3000.com/articles/article_3592.asp

چرخش Bitmap یک Effect گرافیکی است که دلفی به طور پیش فرض پشتیبانی نمی کند. این مقاله به شما نشان می دهد که چگونه یک تصویر را تا ۹۰ درجه بچرخانید. این کد به شما اجازه می دهد که هر تصویری را ۰ – ۹۰ – ۱۸۰ یا ۲۷۰ درجه بچرخانید. با کمی کار می توان این کد را به گونه تغییر داد که برای هر درجه ای کار کند:

procedure RotateBitmap(var hBitmapDC : Longint; var lWidth : Longint;
var lHeight : Longint; lRadians : real);
var
I : Longint; // loop counter
J : Longint; // loop counter
hNewBitmapDC : Longint; // DC of the new bitmap
hNewBitmap : Longint; // handle to the new bitmap
lSine : extended; // sine used in rotation
lCosine : extended; // cosine used in rotation
X1 : Longint; // used in calculating new
// bitmap dimensions
X2 : Longint; // used in calculating new
// bitmap dimensions
X3 : Longint; // used in calculating new
// bitmap dimensions
Y1 : Longint; // used in calculating new
// bitmap dimensions
Y2 : Longint; // used in calculating new
// bitmap dimensions
Y3 : Longint; // used in calculating new
// bitmap dimensions
lMinX : Longint; // used in calculating new
// bitmap dimensions
lMaxX : Longint; // used in calculating new
// bitmap dimensions
lMinY : Longint; // used in calculating new
// bitmap dimensions
lMaxY : Longint; // used in calculating new
// bitmap dimensions
lNewWidth : Longint; // width of new bitmap
lNewHeight : Longint; // height of new bitmap
lSourceX : Longint; // x pixel coord we are blitting
// from the source image
lSourceY : Longint; // y pixel coord we are blitting
// from the source image

begin
// create a compatible DC from the one just brought
// into this function
hNewBitmapDC := CreateCompatibleDC(hBitmapDC);

// compute the sine/cosinse of the radians used to
// rotate this image
lSine := Sin(lRadians);
lCosine := Cos(lRadians);

// compute the size of the new bitmap being created
X1 := Round(-lHeight * lSine);
Y1 := Round(lHeight * lCosine);
X2 := Round(lWidth * lCosine – lHeight * lSine);
Y2 := Round(lHeight * lCosine + lWidth * lSine);
X3 := Round(lWidth * lCosine);
Y3 := Round(lWidth * lSine);

// figure out the max/min size of the new bitmap
lMinX := Min(0, Min(X1, Min(X2, X3)));
lMinY := Min(0, Min(Y1, Min(Y2, Y3)));
lMaxX := Max(X1, Max(X2, X3));
lMaxY := Max(Y1, Max(Y2, Y3));

// set the new bitmap width/height
lNewWidth := lMaxX – lMinX;
lNewHeight := lMaxY – lMinY;

// create a new bitmap based upon the new width/height of the
// rotated bitmap
hNewBitmap := CreateCompatibleBitmap(hBitmapDC, lNewWidth,
lNewHeight);

//attach the new bitmap to the new device context created
//above before constructing the rotated bitmap
SelectObject(hNewBitmapDC, hNewBitmap);

// loop through and translate each pixel to its new location.
// this is using a standard rotation algorithm
For I := 0 To lNewHeight do begin
For
J := 0 To lNewWidth do begin
lSourceX :=
Round((J + lMinX) * lCosine + (I + lMinY) * lSine);
lSourceY :=
Round((I + lMinY) * lCosine – (J + lMinX) * lSine);
If (lSourceX >= 0) And (lSourceX <= lWidth) And
(lSourceY >= 0) And (lSourceY <= lHeight) Then
BitBlt(hNewBitmapDC, J, I, 1, 1, hBitmapDC,
lSourceX, lSourceY, SRCCOPY);
end;
end;

// reset the new bitmap width and height
lWidth := lNewWidth;
lHeight := lNewHeight;

// return the DC to the new bitmap
hBitmapDC := hNewBitmapDC;

// destroy the bitmap created
DeleteObject(hNewBitmap);

End;

یک مثال از طریقه استفاده از این تابع را ببینید:

procedure TForm1.RotateTest(Sender: TObject);
var
lRadians : real;
DC : longint;
H, W : integer;
Degrees : integer;
begin
Degrees := 45;
lRadians := PI * Degrees / 180;
DC := Image1.Picture.Bitmap.Canvas.Handle;
H := Image1.Picture.Bitmap.Height;
W := Image1.Picture.Bitmap.Width;
RotateBitmap(DC, W, H, lRadians);
Image1.Width := W;
Image1.Height := H;
Image1.Picture.Bitmap.Width := W;
Image1.Picture.Bitmap.Height := H;
BitBlt(Image1.Picture.Bitmap.Canvas.Handle, 0, 0, W, H, DC, 0, 0, SRCCopy);
Image1.Refresh;
end;

موفق و موید و منصور باشید
معموری

چگونه یک HotKey یا میان بر سیستم درست کنیم؟

دی ۳م, ۱۳۸۷

چگونه یک HotKey یا میان بر سیستم درست کرده و آن را مدیریت کنیم؟ (که در تمامی برنامه ها کار کند)
مرجع: http://www.delphi3000.com/articles/article_3529.as p

{**********************

Copyright © by Jim McKeeth Licensed under LGPL
( http://www.gnu.org/licenses/licenses.html#LGPL )

Demo of creating a system wide hotkey or shortcut
This was written in Delphi 7,
but should work in most other versions
(but obviously not Kylix)
You need a form with
1) a THotKey named HotKey1
2) a TCheckBox named CheckBox1
To demo
1) Change the hotkey in the value
2) Check the box
3) Minimize the application
4) Press the hot key
5) Be impressed
———}
unit SystemHotKeyUnit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, StdCtrls, ComCtrls, Dialogs,
// Menus need to be added for calls in the code
Menus;

type
TForm1 = class(TForm)
HotKey1: THotKey;
CheckBox1: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
protected
// Handle the global hot key
messages when they are sent to the window
procedure HotyKeyMsg(var msg:TMessage); message WM_HOTKEY;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

var
myAtom: integer;

function ShiftState2Modifier(const Shift: TShiftState):Word;
begin
Result := 0;
if ssShift in Shift then
Result := Result or MOD_SHIFT;
if ssAlt in Shift then
Result := Result or MOD_ALT;
if ssCtrl in Shift then
Result := Result or MOD_CONTROL;
end;

function GetShortCutKey(ShortCut: TShortCut):Word;
var
shift: TShiftState;
begin
ShortCutToKey(ShortCut,Result,shift); // call in Menus!
end;

function GetShortCutModifier(ShortCut: TShortCut):Word;
var
key: Word;
shift: TShiftState;
begin
ShortCutToKey(ShortCut,key,shift); // call in Menus!
Result := ShiftState2Modifier(shift);
end;

function RegisterHotShortCut(const h:THandle; const Atom: integer;
const ShortCut: TShortCut):Boolean;
var
key : Word;
Shift: TShiftState;
begin
UnregisterHotKey(h,Atom); // call in Windows
ShortCutToKey(ShortCut,key,shift);
Result := RegisterHotKey(h,Atom,ShiftState2Modifier(Shift),key);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
// you need to type cast it as a pChar if you are using a string
myAtom := GlobalAddAtom(pchar(‘HotKeyDemo’));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
UnregisterHotKey(Handle,myAtom);
GlobalDeleteAtom(myAtom);
end;

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if
CheckBox1.Checked then
RegisterHotShortCut(Handle,myAtom,HotKey1.HotKey)
else
UnregisterHotKey(Handle,myAtom);
end;

procedure TForm1.HotyKeyMsg(var msg: TMessage);
begin
if
(msg.LParamLo=GetShortCutModifier(HotKey1.HotKey))
and (msg.LParamHi=GetShortCutKey(HotKey1.HotKey)) then
begin
Application.BringToFront;
Showmessage(‘Hey, now that is a system wide hot key!’)
end;
end;

end.


موفق و موید و منصور باشید
معموری

چگونه یک TRichEdit با پس زمینه Tile بسازیم؟

دی ۳م, ۱۳۸۷

به نظر می آید راهی برای Transparent کردن یک RichEdit عادی یا نقاشی کردن یک تصویر در پس زمینه آن به صورت Tile وجود ندارد.

اما در ویندوز ۲۰۰۰ یک راه عقبی وجود دارد که با کمک آن می شود این کار رو انجام داد. در این ویندوز شما می توانید کنترل خود را با استفاده از مقدار دهی ثابت WS_EX_LAYERED به استیل(Style)های توسعه یافته ویندوز و سپس فراخوانی تابع APIی به نام SetLayeredWindowAttributes به صورت Transparent در بیاورید.

مثال پایین یک کنترل TRichView با پراپرتی DrawStyle است. بسته به مقدار آن، کنترل پس زمینه Transparent خواهد داشت یا خودش را با alpha transparency نقاشی می کند.

constructor MyTransparentRichEdit.Create(AOwner: TComponent);
begin
inherited
Create(AOwner);
FDrawStyle := dsNormal;
end;

procedure MyTransparentRichEdit.CreateParams(var Params:
TCreateParams);
begin
inherited
CreateParams(Params);
if not (csDesigning in ComponentState) then
begin
Params.Style := Params.Style or WS_POPUP;
Params.ExStyle := Params.ExStyle + WS_EX_LAYERED;
end;
end;

procedure MyTransparentRichEdit.CreateWnd;
var
XPoint: TPoint;
begin
if not
(csDesigning in ComponentState) then
begin
XPoint := TWinControl(Owner).ClientToScreen(POINT(Left, Top));
Left := XPoint.X;
Top := XPoint.Y;
end;
inherited CreateWnd;
case FDrawStyle of
ds_Transparent:
SetLayeredWindowAttributes(Handle, ColorToRGB(Color), 255, LWA_COLORKEY);
ds_NotDistinctly:
SetLayeredWindowAttributes(Handle, 0, 150, LWA_ALPHA);
end;
end;

procedure MyTransparentRichEdit.SetDrawStyle(AValue: TDrawStyle);
begin
if
FDrawStyle <> AValue then
begin
FDrawStyle := AValue;
RecreateWnd;
end;
end;


موفق و موید و منصور باشید