1. 程式人生 > >【mxGraph】原始碼學習:(6)mxGraphModel

【mxGraph】原始碼學習:(6)mxGraphModel

1. 概覽

mxGraphModel繼承自mxEventSource以實現graph model。graph model是負責儲存graph資料結構的包裝器。graph model充當事務包裝器,其中包含所有更改的事件通知,而cell包含用於更新實際資料結構的原子操作。

1.1 圖層

模型中的cell層次結構必須具有頂級根cell,其中包含圖層(通常是一個預設圖層),而這些圖層又包含圖層的頂級cell。這意味著每個cell都包含在一個圖層中。如果不需要圖層,則應將所有新cell新增到預設圖層。

圖層可用於隱藏和顯示cell組,或用於將cell組放置在顯示中的其他cell之上。要識別圖層,可以使用isLayer方法。如果給定cell的父級是模型的根,則返回true。

1.2 事件

有關詳細資訊,參閱事件部分。有一組新事件可用於跟蹤事件發生的變化。這些事件稱為初始beginUpdate的startEdit,為每個執行的更改執行,endEdit為endUpdate執行。執行的事件包含一個名為change的屬性,表示執行後的更改。

1.3 編碼模型

使用如下程式碼編碼graph model,這將建立一個包含所有模型資訊的XML節點:

var enc = new mxCodec();
var node = enc.encode(graph.getModel());

對於更改的編碼,需要graph model監聽器來編碼來自給定更改陣列的每個更改:

model.
addListener(mxEvent.CHANGE, function(sender, evt) { var changes = evt.getProperty('edit').changes; var nodes = []; var codec = new mxCodec(); for (var i = 0; i < changes.length; i++) { nodes.push(codec.encode(changes[i])); } // do something with the nodes });

對於解碼和執行更改,編解碼器需要一個查詢功能,允許它按如下方式解析單元ID:

var codec = new mxCodec();
codec.lookup = function(id) {
  return model.getCell(id);
}

對於每個編碼的變化(由節點表示),可以使用以下程式碼來執行解碼並建立變更物件:

var changes = [];
var change = codec.decode(node);
change.model = model;
change.execute();
changes.push(change);

然後可以使用模型如下排程更改:

var edit = new mxUndoableEdit(model, false);
edit.changes = changes;

edit.notify = function() {
  edit.source.fireEvent(new mxEventObject(mxEvent.CHANGE,
     'edit', edit, 'changes', edit.changes));
  edit.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,
     'edit', edit, 'changes', edit.changes));
}

model.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
model.fireEvent(new mxEventObject(mxEvent.CHANGE,
     'edit', edit, 'changes', changes));

2. 構造

mxGraphModel的建構函式如下:

/**
 * root - 表示根cell的mxCell
 */
function mxGraphModel(root) {
    this.currentEdit = this.createUndoableEdit();

    if (root != null) {
        this.setRoot(root);
    } else {
        this.clear();
    }
}

mxGraphModel與mxGraph一樣,都是採用原型鏈方式繼承自mxEventSource:

mxGraphModel.prototype = new mxEventSource();
mxGraphModel.prototype.constructor = mxGraphModel;

3. 原型屬性

下面列舉一些重要的原型屬性:

// 儲存根cell,而根cell又包含表示圖層作為子cell的cell。也就是說,圖中的實際元素應該存在於第三代cell中
mxGraphModel.prototype.root = null;

// 從id到cell的map
mxGraphModel.prototype.cells = null;

// 指定下一個被建立的id。初始值為0
mxGraphModel.prototype.nextId = 0;

// 儲存當前事務的更改。如果事務已關閉,則使用createUndoableEdit為此變數建立新物件
mxGraphModel.prototype.currentEdit = null;

// 計算巢狀事務的深度。每次呼叫beginUpdate都會增加這個數字,每次呼叫endUpdate都會減少它
// 當計數器達到0時,事務將關閉並觸發相應的事件。初始值為0
mxGraphModel.prototype.updateLevel = 0;

4. 原型方法

mxGraphModel中有許多原型方法和類,這裡選擇一些瞭解其聯絡及實現

4.1 建立根cell

建立根cell有兩種方式,第一種就是在建立mxGraphModel時指定一個cell作為根cell,如果沒有指定,則使用第二種方法,如下所示。

呼叫createRoot方法建立一個根cell,然後設定為當前model的根cell,並會觸發根cell更改的事件:

/**
 * 呼叫createRoot建立一個根cell
 */
mxGraphModel.prototype.clear = function () {
    this.setRoot(this.createRoot());
};

/**
 * 使用預設圖層(第一個cell)建立新的根cell
 */
mxGraphModel.prototype.createRoot = function () {
    var cell = new mxCell();
    cell.insert(new mxCell());
    return cell;
};

/**
 * 使用mxRootChange設定模型的root,並將更改新增到當前事務。
 * 這將重置模型中的所有資料結構,並且是清除現有模型的首選方法。返回新根。
 *
 * root - 根cell
 */
mxGraphModel.prototype.setRoot = function (root) {
    this.execute(new mxRootChange(this, root));
    return root;
};

/**
 * 如果需要,執行給定的編輯並觸發事件。編輯物件需要一個被呼叫的執行函式。編輯將新增到beginUpdate
 * 和endUpdate呼叫之間的currentEdit,以便在此執行是單個事務時觸發事件,即如果沒有呼叫
 * endUpdate之前沒有呼叫startUpdate呼叫。此實現在執行給定更改之前觸發execute事件。
 * 
 * change - 描述更改的物件。
 */
mxGraphModel.prototype.execute = function (change) {
    change.execute();
    this.beginUpdate();
    this.currentEdit.add(change);
    this.fireEvent(new mxEventObject(mxEvent.EXECUTE, 'change', change));
    // 新的全域性執行事件
    this.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change));
    this.endUpdate();
};

設定新的根cell時,是通過建立一個新的mxRootChange來實現的:

/**
 * 更改模型中的根的操作。
 * 通過建構函式指定模型中根的更改。
 */
function mxRootChange(model, root) {
    this.model = model;
    this.root = root;
    this.previous = root;
}

/**
 * 使用mxGraphModel.rootChanged執行根的更改
 */
mxRootChange.prototype.execute = function () {
    this.root = this.previous;
    this.previous = this.model.rootChanged(this.previous);
};

/**
 * 內部回撥以更改模型的根並更新內部資料結構,例如cells和nextId。返回先前的根。
 *
 * root - 根cell
 */
mxGraphModel.prototype.rootChanged = function (root) {
    var oldRoot = this.root;
    this.root = root;

    // 重置計數器和資料結構
    this.nextId = 0;
    this.cells = null;
    this.cellAdded(root);

    return oldRoot;
};

4.2 事務管理

mxGraphModel的事務管理簡單而強大,主要通過beginUpdate和endUpdate實現:

/**
 * 將updateLevel加1。事件通知排隊,直到updateLevel使用endUpdate減到0。
 */
mxGraphModel.prototype.beginUpdate = function () {
    this.updateLevel++;
    this.fireEvent(new mxEventObject(mxEvent.BEGIN_UPDATE));

    if (this.updateLevel === 1) {
        this.fireEvent(new mxEventObject(mxEvent.START_EDIT));
    }
};

/**
 * 將updateLevel減1,並在updateLevel減到0時觸發undo事件。此函式通過呼叫currentEdit
 * 上的notify方法間接觸發change事件,然後使用createUndoableEdit建立新的currentEdit。
 *
 * 每次編輯時只會觸發一次undo事件,而每當呼叫notify函式時,都會觸發change事件,即在編輯的
 * 撤消和重做時觸發。
 */
mxGraphModel.prototype.endUpdate = function () {
    this.updateLevel--;

    if (this.updateLevel === 0) {
        this.fireEvent(new mxEventObject(mxEvent.END_EDIT));
    }

    if (!this.endingUpdate) {
        this.endingUpdate = this.updateLevel === 0;
        this.fireEvent(new mxEventObject(mxEvent.END_UPDATE, 'edit', this.currentEdit));

        try {
            if (this.endingUpdate && !this.currentEdit.isEmpty()) {
                this.fireEvent(new mxEventObject(mxEvent.BEFORE_UNDO, 'edit', this.currentEdit));
                var tmp = this.currentEdit;
                this.currentEdit = this.createUndoableEdit();
                tmp.notify();
                this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', tmp));
            }
        } finally {
            this.endingUpdate = false;
        }
    }
};

/**
 * 建立一個新的mxUndoableEdit,它實現notify方法以通過mxUndoableEdit的源觸發change和notify事件。
 *
 * significant - 可選布林值,指定要建立的編輯是否重要。預設為true。
 */
mxGraphModel.prototype.createUndoableEdit = function (significant) {
    var edit = new mxUndoableEdit(this, (significant != null) ? significant : true);

    edit.notify = function () {
        // LATER: Remove changes property (deprecated)
        edit.source.fireEvent(new mxEventObject(mxEvent.CHANGE,
            'edit', edit, 'changes', edit.changes));
        edit.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,
            'edit', edit, 'changes', edit.changes));
    };

    return edit;
};

相關推薦

mxGraph原始碼學習6mxGraphModel

1. 概覽 mxGraphModel繼承自mxEventSource以實現graph model。graph model是負責儲存graph資料結構的包裝器。graph model充當事務包裝器,其中包含所有更改的事件通知,而cell包含用於更新實際資料結構的原

mxGraph原始碼學習5mxGraph

由於mxGraph原始檔有一萬多行,且涉及很多其它原始檔,所以重點在於瞭解mxGraph的作用、結構以及定義了哪些方法 1. 概覽 1.1 作用 mxGraph繼承自mxEventSource以實現基於Web的圖形元件的功能性方面。要啟用平移和連線,使用se

mxGraph原始碼學習7mxCell

1. 概覽 mxCell是graph model的元素。它們表示graph中的group、vertex和edge的狀態。 對於自定義屬性,建議使用XML節點作為cell的值。以下程式碼可用於建立具有XML節點的cell作為值: var doc = mxUtils

mxGraph原始碼學習2mxClient

1. mxClient.js檔案 mxClient.js是客戶端的引導機制,此檔案include了執行mxGraph所需的所有原始檔,並載入了其依賴的資原始檔,以及配置了客戶端的語言。 意思就是隻要在需要使用mxGraph的地方用<script>標籤

機器人學機器人開源專案KDL原始碼學習6笛卡爾空間軌跡規劃、圓弧過渡、姿態插值、梯形速度、pathlength

  本文的內容是對另一篇文章(連結)的補充,對Trajectory_example.cpp涉及到的原理作一些簡單的講解,主要內容是:   (1)機器人路徑規劃圓弧過渡的原理;   (2)機器人路徑規劃梯形波的原理;   (3)機器人末端姿態插值的方法(角-軸);   (4)KDL

機器人學機器人開源專案KDL原始碼學習8KDL的精髓

  首先說一下我的心得: 1. 我認為KDL的精髓是Spatial Vector,結合C++等面向物件的語言可以寫出較好的軟體。 2. 直接閱讀KDL程式碼不適合初學者學習機械臂動力學。 3. 要學習機械臂動力學的話應首先閱讀使用3維向量推導公式的文獻,也就是線速度和角速度獨立分析

機器人學機器人開源專案KDL原始碼學習4機械臂逆動力學的牛頓尤拉演算法

  機械臂的逆動力學問題可以認為是:已知機械臂各個連桿的關節的運動(關節位移、關節速度和關節加速度),求產生這個加速度響應所需要的力/力矩。KDL提供了兩個求解逆動力學的求解器,其中一個是牛頓尤拉法,這個方法是最簡單和高效的方法。    牛頓尤拉法演算法可以分為三個步驟: step1:

機器人學機器人開源專案KDL原始碼學習7examples中的CMakeList.txt檔案解讀

通過學習KDL開源專案的程式碼可以學習CMake構建程式的知識,現簡單介紹一下orocos_kinematics_dynamics-master\orocos_kinematics_dynamics-master\orocos_kdl\examples\CMakeList.txt檔案的指令。

機器人學機器人開源專案KDL原始碼學習5KDL如何求解幾何雅克比矩陣

這篇文章試圖說清楚兩件事:1. 幾何雅克比矩陣的本質;2. KDL如何求解機械臂的幾何雅克比矩陣。 一、幾何雅克比矩陣的本質 機械臂的關節空間的速度可以對映到執行器末端在操作空間的速度,這種對映可以通過一個矩陣來描述,就是幾何雅克比矩陣,瞭解雅克比矩陣需要了解這種對映關係的本質,這

機器人學機器人開源專案KDL原始碼學習3機器人操作空間路徑規劃(Path Planning)和軌跡規劃Trajectory Planning示例

很多同學會把路徑規劃(Path Planning)和軌跡規劃(Trajectory Planning)這兩個概念混淆,路徑規劃只是表示了機械臂末端在操作空間中的幾何資訊,比如從工作臺的一端(A點)沿直線移動到另一端(B點)。而軌跡規劃則加上了時間律,比如它要完成的任務是從A點開始到B點結束,中間

Nodejs學習筆記--- 簡介及安裝Node.js開發環境

ack 目錄 javascrip 難度 時間 網站開發 clas jetbrains 常用 目錄 學習資料 簡介 安裝Node.js npm簡介 開發工具 Sublime Node.js開發環境配置 擴展:安裝多版本管理器 學習資料   1.深入淺出Node.j

ADNI資料預處理6ADNI_slice_dataloader ||| show image

ADNI Series 1、【ADNI】資料預處理(1)SPM,CAT12 2、【ADNI】資料預處理(2)獲取 subject slices 3、【ADNI】資料預處理(3)CNNs 4、【ADNI】資料預處理(4)Get top k slices according to CNN

原創VBA學習筆記2--例項,VBA刪除表中的空行

Sub 巨集1迴圈內刪列() '資料不規範,有的空行是4,有的是6,有的是1就不好處理了     For i = 15 To 100 Step 2          Rows(i).Delete Shift:=xlUp &nb

原創python學習筆記10--《笨辦法學python》字串處理

  字串基本操作 (1) 字串+字串 (2)字串*數字 (3)字串+str(其他) # -*- coding:utf-8 -*- print ("test1") name1="alice" name2="bob" name_new=name1+name2 print

整理python學習筆記5-- pygame庫的函式和方法整理

PYGAME的方法和官方文件查詢 官方文件 http://www.pygame.org/docs/   模組 簡介 pygame.BufferProxy An array protocol view

原創pygame學習筆記4----一個打飛機遊戲

根據資料學習,程式碼是在資料的基礎上,重新打的,有部分修改。   轉載資料來源:-will https://www.cnblogs.com/wuzhanpeng/p/4261015.html http://eyehere.net/2011/python-pygame-n

原創pygame學習筆記3--triviagame答題遊戲

  一 容易出錯的地方 def _init_(self,name,score) 要記住, __init__ 前後都是兩個下劃線,而不只是1個下劃線   二 程式碼測試 顯示效果正常了,可以玩了 現在題目會迴圈玩 # -*- coding:utf

原創pygame學習筆記2----pie遊戲需優化

  測試程式碼情況 (1)做到了弧形可以按出來 (2)數字的顯示正確 (3)出的一些低階錯誤         temp:\\pygame2.txt 這樣的錯誤, temp\\pygame2.txt 導致 這樣的錯誤,

原創pygame學習筆記1----基本的線,矩形,圓形,弧形繪製

PYgame的內容 (1)這個module很有意思 (2)書本至少來源於《Python遊戲程式設計入門》 (3)官方權威說明:https://www.pygame.org/docs/   下面的嘗試把各種圖形在一個程式裡繪製 注意點: (1)特別注意,比如引

原創python學習筆記5--《笨辦法學python》,指令碼帶引數

一 指令碼檔案 (1) 簡單的說就是一段自己寫的,可執行的程式碼,否則會報錯 (2)簡單指令碼,直接 python  xxx1.py (3)帶引數指令碼,需要 python xxx2.py argv1 argv2 argv3         根據指令碼引數的數量,