JWDStructure

دروس VBA AutoCAD 

تطبيقات برمجة أوتوكاد - التطبيق الثالث - الاستفادة من الأحداث

تم نشر هذا التطبيق في موقع ملتقى المهندسين العرب بعنوان (تمرين 3: Vba وأوتوكاد - أحداث أوتوكاد)، وللتفاعل معه يمكنكم المشاركة على الرابط التالي:

http://www.arab-eng.org/vb/t141414.html

المستوى

  • متوسط

مقدمة

في هذا التطبيق سنتحدث عن كيفية الاستفادة من بعض الأحداث (Events) التي يطلقها أوتوكاد عند القيام بأمر ما، وذلك عن طريق استعراض برنامج عملي.

المهارات المطلوبة لفهم الموضوع

  • معرفة بسيطة باستخدام برنامج أوتوكاد.
  • معرفة بالأوامر الأساسية للغة البرمجة Visual Basic.

ما هو الحدث (Event)؟

الحدث هو عبارة عن برنامج جزئي (Sub) يتم تسميته بشكل محدد، وتقوم لغة VB بتنفيذ هذا البرنامج عند حصول طارئ ما.

مثلاً: عند النقر على زر بواسطة الفأرة، يقوم هذا الزر بإصدار الحدث Click فإن كان هذا الزر اسمه Command1 تقوم VB بالبحث عن البرنامج الجزئي المسمى Command1_Click فتقوم بتنفيذه (في حال وجوده).

كما هو موضح في المثال فإن اسم البرنامج الجزئي الذي يدل على الحدث يكون مؤلفاً من قسمين يفصل بينهما الرمز (_): الأول وهو اسم الكائن الذي يتبع له الحدث، والثاني هو اسم الحدث.

بالنسبة لاسم الحدث فإن لكل كائن أحداثه الخاصة ويمكن معرفتها إما من ملف التعليمات الخاص بهذا الكائن أو كما سيتم توضيحه في المثال العملي أدناه.

بعض الأحداث الخاصة بالكائن Document والتي سنحتاجها في هذا التمرين

الحدث BeginCommand: يحدث هذا الحدث بعد البدء بأمر جديد مثل Line أو Circle أو أي أمر آخر يتم تنفيذه من سطر أوامر أوتوكاد ويأخذ برنامجه الجزئي الشكل التالي:

Private Sub AcadDocument_BeginCommand(ByVal CommandName As String)

End Sub

نلاحظ أن هذا البرنامج اسمه AcadDocument_BeginCommand وهو مكون من قسمين: الأول وهو AcadDocument وهو يدل على اللوحة التي نرسم عليها والقسم الآخر وهو BeginCommand وهو اسم الحدث، كما أن البرنامج له بارامتر واحد اسمه CommandName وقيمته هي اسم الأمر الجاري تنفيذه في أوتوكاد، فإذا كتبنا الأمر L أو Line في أوتوكاد فإن هذا الحدث سيتم تفعيله وسيحمل المتحول CommandName القيمة "LINE"

الحدث EndCommand: مثل السابق تماماً والفرق أنه يتم تفعيله بعد الانتهاء من تنفيذ الأمر بشكل كامل، ويأخذ برنامجه الجزئي الشكل التالي:

Private Sub AcadDocument_EndCommand(ByVal CommandName As String)

End Sub

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

Private Sub AcadDocument_ObjectAdded(ByVal Object As Object)

End Sub

نلاحظ أن هذا البرنامج له بارامتر واحد اسمه Object وهو يشير إلى الكائن الذي تمت إضافته، فإذا قمنا بإضافة خط (Line) فإن هذا المتحول سوف يؤشر إلى هذا الخط الجديد.

والآن إلى التمرين

أستخدم غالباً في لوحات الأوتوكاد التي أرسمها عدة طبقات (وهذا أمر طبيعي!) وكذلك أستخدم عدة أنماط للأبعاد (DimStyles)

لنفرض أنه لدي نمط أبعاد (DimStyle) اسمه Dim أستخدمه لرسم الأبعاد.

ولدي نمط أبعاد آخر أستخدمه لكتابة أسماء الأعصاب اسمه RibName كما هو موضح في الشكل (1).

الشكل (3)
الشكل (1): توضيح أنماط الأبعاد والطبقات

عندما أكتب الأبعاد أضعها في طبقة ولتكن Dim وأستخدم النمط Dim، وعندما أكتب أسماء الأعصاب أضعها في طبقة أخرى ولتكن RibName وأستخدم النمط RibName.

المشكلة:

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

2- بعد رسم خط البعد الخاص باسم الأعصاب أقوم باختياره ثم تغيير الخاصية Text override إلى الاسم الذي أريده، وهذا يأخذ وقتاً إضافياً أيضاً.

المطلوب لحل المشكلة:

أريد برنامجاً يقوم بتغيير نمط البعد تلقائياً عندما أقوم بإضافة خط بعد بحسب الطبقة التي أقوم برسم البعد عليها، فإن كنت أرسم على الطبقة RibName أريد من البرنامج أن يختار نمط البعد RibName وإلا أن يختار نمط البعد Dim، ثم بعد الانتهاء من رسم خط البعد أن يسألني عن اسم الأعصاب إن كنت قد رسمت البُعد في الطبقة التي اسمها RibName، فيقوم تلقائياً بتغيير الخاصية Text override له إلى القيمة الجديدة.

فكرة الحل

سأقوم بشرح الحل نظرياً ثم برمجياً، وسنلاحظ كيف أن جهداً برمجياً صغيراً يمكنه أن يجعل حياتنا أسهل، وأرجو أن أوفق في ذلك.

عندما نبدأ أمراً جديداً وليكن Line يحدث الحدث BeginCommand للكائن ActiveDocument وهو الكائن الذي يشير إلى لوحة الرسم التي نرسم عليها، ويقوم أوتوكاد بتمرير اسم الأمر إلى البرنامج الجزئي الذي يقوم بمعالجة هذا الحدث، ويتم تخزين اسم الأمر في المتحول CommandName كما تم توضيحه أعلاه.

ففي هذه الحالة (أي عند طلب أمر إضافة خط) ستكون قيمة المتحول CommandName تساوي LINE.

أما عند إضافة خد بعد (من النوع Linear) (وهو الذي نريد استخدامه في مشروعنا) ستكون القيمة هي DIMLINEAR.

لذلك سنختبر قيمة هذا المتحول فإن كانت تساوي DIMLINEAR نقوم باختبار اسم الطبقة الحالية، فإن كانت RibName سنقوم باختيار نمط البعد RibName وإلا سنختار نمط البعد Dim أياً كانت الطبقة.

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

لذلك سنختبر نوع هذا الكائن فإن كان بُعداً مرسوماً في الطبقة RibName سنقوم بتخزينه في متحول خاص بنا وليكن اسمه MyDim (لنتذكره لاحقاً من أجل تغيير الخاصية Text override) وإلا سنتجاهله.

بعد هذا الحدث يقوم أوتوكاد بتنشيط الحدث EndCommand والذي يدل على الانتهاء بشكل كامل من آخر أمر فعال، وسنقوم عندها باختبار قيمة المتحول الذي قمنا بتخزينه، فإن كان لا يحوي قيمة فلا بأس، أما إن كان يحوي قيمة فهذا يعني أنه خط بعد في الطبقة RibName (طبعاً نحن قررنا ذلك في الخطوة السابقة) وسنسأل المستخدم عن الاسم الذي يريده ثم نقوم بتخزين هذا الاسم ضمن الخاصية Text override للكائن المضاف.

هذا باختصار والآن إلى البرنامج.

الحل برمجياً

افتح لوحة جديدة في أوتوكاد وأضف إليها الطبقة Dim والطبقة RibName، ثم أضف إليها نمطي بعد (Dim Style) أحدهما اسمه Dim والآخر RibName.

ملاحظة: تقيد بحالات الأحرف كما كتبتها (أحرف صغيرة أو كبيرة) واجعل تمايزاً بين الطبقتين باللون وبين نمطي البعد بالشكل حتى تظهر نتائج البرنامج بشكل أوضح، أو يمكنك تحميل الملف المرفق أدناه للتجريب عليه.

قم بتشغيل VBA بالضغط على ALT+F11 في لوحة المفاتيح أو من القوائم Tools – Macro – Visual Basic Editor.

بعد ظهور نافذة VBA انقر نقراً مزدوجاً على ThisDrawing فتظهر صفحة البرمجة الخاصة باللوحة الحالية كما هو موضح في الشكل (2).

الشكل (2): بيئة عمل VBA
الشكل (2): بيئة عمل VBA

سنقوم أولاً بتعريف المتحول MyDim وهو من النوع AcadDimRotated وهو الذي سيشير إلى البعد المضاف كما يلي:

10 Dim MyDim As AcadDimRotated

ملاحظة: إن الأرقام الموجودة في بداية الأسطر هي أرقام للأسطر، وهو أسلوب قديم متبع في اللغات القديمة، وقد استخدمته هنا فقط كمرجع لتوضيح عملية إضافة سطر ما بين سطرين كما سيتبين لاحقاً، وليس من الضروري كتابة هذه الأرقام في VBA، كما أنه لا يوجد تسلسل معين يجب الالتزام به لأن VBA تنفذ الأسطر حسب ترتيب كتابتها وليس حسب ترتيب أرقامها.

من قائمة الكائنات (راجع الشكل) اختر الكائن AcadDocument، ثم من قائمة الأحداث اختر الحدث BeginCommand فيقوم VBA بإضافة البرنامج الجزئي الذي تمت الإشارة إليه أعلاه، أي ستتم إضافة الأسطر التالية (بدون أرقام الأسطر):

20 Private Sub AcadDocument_BeginCommand(ByVal CommandName As String)

100 End Sub

أضف بين السطرين السابقين الأسطر التالية:

30 If CommandName = "DIMLINEAR" Then
40     If ThisDrawing.ActiveLayer.Name = "RibName" Then
50         ThisDrawing.ActiveDimStyle = DimStyles("RibName")
60     Else
70         ThisDrawing.ActiveDimStyle = DimStyles("Dim")
80     End If
90 End If

في السطر 30 نختبر قيمة المتحول CommandName فإن كانت DIMLINEAR فهي الحالة التي سنعالجها ضمن حلقة If وإلا فإن الأمر لا يهمنا وسينتقل عندها البرنامج إلى السطر 90.

في حال كان الأمر هو DIMLINEAR أي أننا سنقوم بإضافة بُعد، لذلك سنختبر في السطر 40 اسم الطبقة الحالية فإن كان اسمها RibName سنقوم في السطر 50 بتغيير النمط الحالي للأبعاد إلى RibName فإن لم يكن اسمها كذلك (Else) سنقوم في السطر 70 بتفعيل نمط البعد Dim.

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

ملاحظة: بالنسبة للسطر رقم 10 أعلاه لم نستخدمه حتى هذه اللحظة وهو خاص بالمشكلة الثانية.

الآن أضف البرنامج الخاص بالحدث ObjectAdded بنفس الطريقة التي استخدمناها لإضافة الحدث السابق فيقوم VBA بإضافة الأسطر التالية (بدون أرقام للأسطر):

110 Private Sub AcadDocument_ObjectAdded(ByVal Object As Object)

150 End Sub

أضف الأسطر التالية بين السطرين السابقين:

120 If TypeOf Object Is AcadDimRotated And ThisDrawing.ActiveLayer.Name _
        = "RibName" Then
130     Set MyDim = Object
140 End If

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

في السطر 120 نقوم باختبار نوع الكائن Object والطبقة التي تحويه، فإن كان الكائن من النوع AcadDimRotated وكانت طبقته هي RibName فإن هذا الكائن يهمنا لأننا نريد تعديل الخاصية TextOverride له إلى اسم العصب لذلك سنقوم بجعل المتحول MyDim يشير إليه (أي سنتذكره) في السطر 130، أما إن كان الكائن المضاف ليس من النوع AcadDimRotated أو أنه كان على طبقة غير الطبقة RibName حتى لو كان خط بعد فإنه لا يهمنا ولن نقوم بتذكره.

الآن أضف البرنامج الخاص بالحدث EndCommand كما تم شرحه أعلاه، أو يمكنك كتابته يدوياً:

160 Private Sub AcadDocument_EndCommand(ByVal CommandName As String)

230 End Sub

أضف بين السطرين السابقين:

170 If Not MyDim Is Nothing Then
180     Dim txt As String
190     txt = Trim(InputBox("Rib Name:"))
200     If txt <> "" Then MyDim.TextOverride = txt
210     Set MyDim = Nothing
220 End If


في السطر 170 نختبر المتحول MyDim فإن كان يشير إلى كائن ما فهذا يعني أنه يشير إلى كائن بعد تمت إضافته في الطبقة RibName لأنه لن يحمل قيمة إلا إذا تحقق هذا الشرط (راجع السطرين 120 و 130)

إن كان هذا المتحول يشير إلى كائن ما سنقوم بالمعالجة في السطر 180 وحتى 210 وإلا فإن البرنامج سينتهي دون فعل شيء وهذا ما نريده.

في السطر 180 نقوم بتعريف المتحول txt من النوع String وهو المتحول الذي سنقوم بتخزين اسم العصب فيه.

في السطر 190 نظهر رسالة تسأل المستخدم عن اسم العصب عن طريق الأمر InputBox وهو أحد أوامر VBA، وبالنسبة للأمر Trim فهو يقوم بحذف المسافات من بداية ونهاية القيمة التي سيقوم المستخدم بإدخالها إن وجدت هذه المسافات، وسيتم تخزين القيمة المعادة من المستخدم في المتحول txt.

في السطر 200 نقوم باختبار قيمة txt فإن كانت لا تساوي نصاً فارغاً فإننا سنقوم بتغيير الخاصة TextOverride للكائن MyDim إلى القيمة التي يحملها المتحول txt وإلا لن نقوم بتعديلها.

في السطر 210 نقوم بتفريغ قيمة المتحول MyDim لأننا لن نحتاج إلى تذكره بعد هذا.

السطر 220 هو نهاية حلقة If التي فتحناها في السطر 170.

الآن بعد انتهاء البرنامج قم بتجربته بالذهاب إلى أوتوكاد وإضافة خط بعد في الطبقة RibName وستلاحظ بعد الانتهاء من إضافته سيقوم أوتوكاد بسؤالك عن اسم العصب، أدخل R1 مثلاً وسيقوم أوتوكاد بتعديل قيمة البعد إلى هذه القيمة الجديدة.

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

نص البرنامج بالكامل

Dim MyDim As AcadDimRotated

Private Sub AcadDocument_BeginCommand(ByVal CommandName As String)
    If CommandName = "DIMLINEAR" Then
        If ThisDrawing.ActiveLayer.Name = "RibName" Then
            ThisDrawing.ActiveDimStyle = DimStyles("RibName")
        Else
            ThisDrawing.ActiveDimStyle = DimStyles("Dim")
        End If
    End If
End Sub

Private Sub AcadDocument_ObjectAdded(ByVal Object As Object)
    If TypeOf Object Is AcadDimRotated And ThisDrawing.ActiveLayer.Name _
        = "RibName" Then
        Set MyDim = Object
    End If
End Sub

Private Sub AcadDocument_EndCommand(ByVal CommandName As String)
    If Not MyDim Is Nothing Then
        Dim txt As String
        txt = Trim(InputBox("Rib Name:"))
        If txt <> "" Then MyDim.TextOverride = txt
        Set MyDim = Nothing
    End If
End Sub

تحميل