/

17 tháng 3, 2013

Bài 12: Dẫn xuất từ AcDbObject

Giới thiệu

Tôi sẽ bắt đầu với những đối tượng mới và còn vấn đề thực thể mới sẽ được trình bày trong bài kế tiếp. Đối tượng mới có thể được sử dụng với nhiều mục đích khác nhau và chúng thực sự rất mạnh mẽ. Một khi ứng dụng tạo ra và quản lý một đối tượng mới, cấu trúc càng phức tạp bao nhiêu thì ứng dụng lại càng thông minh và lưu trữ dữ liệu hiệu qủa bấy nhiêu.
Bắt đầu bằng một ví dụ đơn giản, giả sử bạn sẽ xây dựng một ứng dụng ObjectARX sẽ triển khai một vài thanh ngang, chúng có thuộc tính chiều dài và mang một kiểu mẫu shape nhất định. Có thể có nhiều hơn một thanh ngang mang cùng một kiểu mẫu và sẽ thật tốt nếu bạn có thể cung cấp một ví dụ của thông tin shape và chia sẻ nó giữa tất cả các thanh ngang sử dụng kiểu mẫu này.
Đầu tiên là lặp lại thông tin trên từng thanh ngang một không quan trọng nếu có sự trùng lặp thông tin. Nó vẫn làm việc nhưng sẽ tạo thêm vấn đề dựa vào vấn đề đầu tiên cái mà  là không gian không cần thiết để lưu trữ các thông tin trùng lặp. Giả sử bạn cần cập nhật thông tin shape và bạn muốn áp dụng lên tất cả các thanh ngang sử dụng mẫu shape này. Nếu thông tin được lặp lại trên từng thành ngang, bạn sẽ cần mở mỗi thanh ngang và cập nhật thông tin cho nó. Tuy nhiên, nếu thông tin shape được cất giữ tại cùng một vị trí và thanh ngang kia phản chiểu thông thi này, bạn sẽ chỉ cần cập nhật thông tin tại một nơi duy nhất, và mọi thanh ngang sử dụng cùng mẫu shape sẽ được cập nhật ngay khi bạn cập nhật thông tin.
AutoCAD sử dụng kỹ thuật này trên nhiều tính năng như lớp, text style, nhóm group, ... Bạn sẽ sử dụng đối tượng mới để lưu trữ thông tin này và chia sẻ nó, thông qua mã ObjectId, giữa tất cả các "máy trạm" của đối tượng này.

Bắt đầu như thế nào?

Như đã thảo luận trong bài trước, bạn cần phải dẫn xuất từ AcDbObject để có thể xây dựng một đối tượng riêng. Việc này có thể thực hiện một cách đơn giản thông qua ARXWizard hoặc có thể tự làm bằng cách tạo ra lớp mới bằng tay.
Sau khi tạo được lớp mới, bạn cần xây dựng một vài phương thức như tạo, lưu trữ và thu nhận phiên bản của những đối tượng này từ khoang chứa tương ứng. Tốt thôi, nhưng bạn nên cất giữ đối tượng mới ở đâu? AutoCAD cung cấp khoang chứa cho mục đích chung gọi là NOD (từ điển đối tượng được đặt tên - Named Object Dictionary). NOD có nhiệm vụ cất giữ bất kể đối tượng nào được dẫn xuất từ AcDbObject. Nó sử dụng từ điển giống như cấu trúc lưu trữ khi bạn đặt một mã độc nhất (trong cùng một cấp độ) và một phiên bản đối tượng thông qua con trỏ và mã ObjectId. Có một vài khoang chứa đối tượng kiểu mới như Extension Dictionary sẽ được nhắc tới trong một bài khác.
Khoang chứa NOD có thể và nên được sắp xếp theo thư mục để cho từ điển có được tổ chức nhất có thể. Mấu chốt đầu tiên là tên ứng dụng để tránh xung đột với các ứng dụng ObjectARX của bên thứ ba mà sử dụng NOD cùng lúc với dựng dụng của bạn. Mức thứ hai là nên chứa tất cả các nhóm thương mại của đối tượng. Nó thực sự dựa vào việc có bao nhiêu và số lượng kiểu đối tượng mới mà bạn có. NOD không cập bạn lưu trữ các lớp khác nhau tại cùng một cấp bậc, nhưng tôi thực sự khuyên bạn tránh việc đó trừ trường hợp bạn cần lưu trữ các đối tượng chung như trong nhóm ưu tiên (Preferences group) của đối tượng.

Bạn không cần phải mở và đóng NOD thường xuyên và đi sâu tìm kiếm vị trí đối tượng dẫn xuất mỗi lúc cần truy xuất đến nó. Bạn có thể xây dựng một vài kiểu bộ đệm của đối tượng sử dụng nhiều nhất thông qua mã ObjectId và quản lý bộ đệm này để cập nhận đến từng bản vẽ đang được mở. Nhớ rằng NOD là một phần của đối tượng AcDbDatabase và nó là một per document. Vì vậy, bạn cần phải quan tâm đến việc xây dựng và điền vào từ điển cho mỗi bản vẽ mới được mở ra.

Tiếp tục với đối tượng mới như thế nào?

Như đã đề cập trước đó, nơi cất giữ đối tượng kiểu mới là NOD - một AcDbDictionary. NOD quan tâm đến các đối tượng con bởi vì nó là một khoang chứa. Vì vậy, khi đối tượng AcDbDatabase được đưa ra để ghi lại dữ liệu của nó bởi AutoCAD, nó cũng chuyển thông báo này tới đối tượng con và NOD là một trong số đó. Một khi NOD nhận được thông báo, nó kiểm tra trong cấu trúc và gọi hàm dwgOutFileds() cho từng đối tượng lưu giữ trong đó. Quá trình tương tự xảy ra khi bạn mở bản vẽ và hàm dwgInFileds() được gọi bởi AcDbDatabase trên NOD và trên đối tượng con. Nhờ có nó bạn sẽ cần ghi đè lên phương thức lấp đầy DWG để có thể tiếp tục sử dụng các đối tượng mới giữa quá trình đóng và mở DWG. Bản chất của hàm ghi đè trong lớp đối tượng kiểu mới là:
virtual Acad::ErrorStatus dwgInFields(AcDbDwgFiler* filer);
virtual Acad::ErrorStatus dwgOutFields(AcDbDwgFiler* filer) const;
virtual Acad::ErrorStatus dxfInFields(AcDbDxfFiler* filer);
virtual Acad::ErrorStatus dxfOutFields(AcDbDxfFiler* filer) const;
Nếu không định hỗ trợ giao diện DXF trong đối tượng riêng, bạn có thể bỏ qua chúng.

Quản lý trạng thái đối tượng

Trong bài 5, chúng ta đã nói về trạng thái đối tượng khi nó được mở. Bên trong lớp đối tượng riêng, bạn cần phải chú ý để gọi phương thức xác nhận phù hợp để chắc chắn rằng mọi sự kiện và quá trình thích hợp được kích hoạt khi trang thái đối tượng thay đổi. Việc này rất quan trọng!
Những hàm thay đổi trạng thái dữ liệu của đối tượng phải gọi hàm assertWriteEnabled() đầu tiên và sau đó áp dụng các thay đổi cần thiết. Những hàm chỉ đọc thông tin từ đối tượng và không ảnh hưởng đến trạng thái phải gọi hàm assertReadEnabled() và tôi cũng khuyên bạn làm tất cả dưới dạng hàm hằng số. Việc đó nhằm tránh tai nạn có thể làm thay đổi trạng thái đối tượng khi nó được mở để Đọc, sẽ gây ra thông báo lỗi xác nhận. Nếu quên gọi phương thức xác nhận chính xác, những việc lạ thường có thể xảy ra giống như gọi UNDO và đối tượng vẫn không thay đổi ...

Làm thế nào để tạo ra đối tượng kiểu mới

Để bổ sung một đối tượng mới, bạn cần thực hiện các bước sau đây:
  1. Dẫn xuất từ lớp cơ sởAcDbObject;
  2. Bổ sung dữ liệu;
  3. Bổ sung hàm truy xuất (Đọc/Ghi) với phương thức xác nhận phù hợp;
  4. Bổ sung phương thức lấp đầy duy trì và đọc dữ liệu;
Tôi sẽ trình bày một ví dụ đơn giản sau:
// -------------------------------------------
// Class declaration
// -------------------------------------------
 

class MyClass : public AcDbObject {

public:
ACRX_DECLARE_MEMBERS(MyClass);

MyClass() {};
virtual ~MyClass() {};

Acad::ErrorStatus getVal (int& val) const;
Acad::ErrorStatus setVal (int val);

Acad::ErrorStatus getString (CString& str) const;
Acad::ErrorStatus setString (LPCTSTR str);

virtual Acad::ErrorStatus dwgInFields(AcDbDwgFiler*);
virtual Acad::ErrorStatus dwgOutFields(AcDbDwgFiler*) const;

private:
int m_Val;
CString m_Str;
};
// -------------------------------------------
// Class Definition
// -------------------------------------------
ACRX_DXF_DEFINE_MEMBERS(MyClass,
AcDbObject, AcDb::kDHL_CURRENT,
AcDb::kMReleaseCurrent, 0, MYCLASS, MYSAMP);
// -------------------------------------------Acad::ErrorStatus MyClass::getVal (int& val) const {
assertReadEnabled();val = m_Val;
return Acad::eOk;
}// -------------------------------------------Acad::ErrorStatus MyClass::setVal (int val) {
assertWriteEnabled();m_Val = val;
return Acad::eOk;
}// -------------------------------------------Acad::ErrorStatus MyClass::getString (CString& str) const {
assertReadEnabled();str.Format(_T("%s"),m_Str);
return Acad::eOk;

}// -------------------------------------------Acad::ErrorStatus MyClass::setString (LPCTSTR str) {
assertWriteEnabled();m_Str.Format(_T("%s"),str);
return Acad::eOk;
}// -------------------------------------------Acad::ErrorStatus MyClass::dwgInFields(AcDbDwgFiler* pFiler) {
assertWriteEnabled();
AcDbObject::dwgInFields(pFiler);
Adesk::Int16 _val = 0;
pFiler->readInt16(&_val);
m_Val = _val;
TCHAR* _temp = NULL;
pFiler->readString(&_temp);
m_Str.Format(_T("%s"),_temp);
acutDelString(_temp);

return pFiler->filerStatus();
}// -------------------------------------------Acad::ErrorStatus MyClass::dwgOutFields(AcDbDwgFiler* pFiler) const {
assertReadEnabled();
AcDbObject::dwgOutFields(pFiler);
pFiler->writeInt16(m_Val);
pFiler->writeString(static_cast<const TCHAR*>(m_Str));

return pFiler->filerStatus();
}// -------------------------------------------// -------------------------------------------
// Entry Point
// -------------------------------------------
AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* appId) {
switch (msg) {
case AcRx::kInitAppMsg:
acrxDynamicLinker->unlockApplication(appId);
acrxDynamicLinker->registerAppMDIAware(appId);
MyClass::rxInit();
acrxBuildClassHierarchy();
break;

case AcRx::kUnloadAppMsg:
deleteAcRxClass(MyClass::desc());
break;
}

return AcRx::kRetOK;
}

Làm cách nào để tạo và cất giữ đối tượng mới

Khoang chứa NOD dựa trên lớp AcDbDictionary có vài phương thức đọc, ghi và xóa đầu mục. Ứng dụng của bạn cần phải quan tâm đến đầu mục của NOD, chịu trách nhiệm tạo phiên bản của lớp mới và cất giữ chúng bên trong NOD. Mỗi một đối tượng được lưu giữ,  phải có một khóa định nghĩa hoặc một mã được sinh ra sử dụng  kí hiệu sao (*) trong tên của nó.
void createMyObjects() {
AcDbDictionary *pNamedobj = NULL;
acdbHostApplicationServices()->workingDatabase()->
getNamedObjectsDictionary(pNamedobj, AcDb::kForWrite);
AcDbDictionary *pDict = NULL;
if (pNamedobj->getAt(_T("MYDICT"),(AcDbObject*&) pDict,
AcDb::kForWrite) == Acad::eKeyNotFound) {
pDict = new AcDbDictionary;
AcDbObjectId DictId;
pNamedobj->setAt(_T("MYDICT"), pDict, DictId);
}
pNamedobj->close();
if (pDict) {
MyClass *pObj1 = new MyClass();pObj1->setVal(1);
pObj1->setString(_T("String1"));

MyClass *pObj2 = new MyClass();
pObj2->setVal(2);
pObj2->setString(_T("String2"));

AcDbObjectId rId1, rId2;
pDict->setAt(_T("*M"),pObj1, rId1);
pDict->setAt(_T("*M"),pObj2, rId2);

pObj1->close();
pObj2->close();

pDict->close();

}
}

Làm cách nào để xác thực đối tượng đã được cất giữ trong NOD hay chưa?

Bạn cần phải lặp qua từng đầu mục trong NOD để tìm từ điển của mình và sau đó tiền hành phép lặp với từng đầu mục của nó. Quá trình đó như sau:


void listMyObjects() {
AcDbDictionary *pNamedobj = NULL;
acdbHostApplicationServices()->workingDatabase()
->getNamedObjectsDictionary(pNamedobj, AcDb::kForRead);
AcDbDictionary *pDict = NULL;
pNamedobj->getAt(_T("MYDICT"), (AcDbObject*&)pDict,AcDb::kForRead);
pNamedobj->close();
if (pDict == NULL) {
acutPrintf(_T("\nThe dictionary MYDICT does not exist. Please create it first!"));
return;
}
AcDbDictionaryIterator* pDictIter= pDict->newIterator();
MyClass *pMyClass;
int _val;
CString _str;
for (; !pDictIter->done(); pDictIter->next()) {
pMyClass = NULL;
pDictIter->getObject((AcDbObject*&)pMyClass,AcDb::kForRead);
if (pMyClass != NULL) {
pMyClass->getVal(_val);
pMyClass->getString(_str);
pMyClass->close();
acutPrintf(_T("\nMyClass: val=%d, str=%s"),_val,_str);
}
}delete pDictIter;
pDict->close();
}
Tiếp tục đến bài tới, bạn sẽ học được cách tạo ra một thực thể mới!

Link nguồn: Arxdummies

Không có nhận xét nào:

Đăng nhận xét