Message Boards Message Boards

Graph Editor 2020 ver. powered by the Object Oriented Programming

Posted 8 years ago

GraphEditor[] is a tool for Graph editing. You can create a new and a complex graph. This new version resolved the issue on the program part using Event function.

GraphEditor shows that Mathematica event handling system is very useful and strong with OOP.

Object-Oriented-Programming(OOP) in Mathematica is a pathway to make an easy handling method for large number of targets that.

This GraphEditor program can

  • make named vertex and arrows connecting with other vertices
  • move objects around the board
  • delete vertices and arrows
  • save and load the graph
  • convert to a Mathematica graph.

You can view a sample movie here.

Source code is here.

(* board *)
Module[{boardGraphics,
  boardRatio = 1.4,
  boardImageScale = 800,
  boardDisable = False,
  myStyle = {Medium, FontFamily -> "Helvetica"}},

 (* graphics-box-class *)
 box[nam_] := Module[{
    textInputMode = False,
    pos,
    boxSize,
    title = Null,
    arrowLine = {},(* arrow leader *)
    arrowPointList = {},
    targetArrowList = {}},(* arrows graphics *)

   (* graphics-arrow-class *)

   arrowImage[nam[pointName_]] ^:= Module[{
      delta,
      bsize,
      tpos,
      arrowDrawback = 0},
     EventHandler[(
       delta = Abs[pos - (tpos = (boxPosGet[pointName]))];
       bsize = 
        0.5*boxSizeGet[pointName]/(boardRatio*boardImageScale);
       If[delta != {0, 0},
        arrowDrawback = 
         Norm[delta]*If[Apply[ArcTan, delta] > Apply[ArcTan, bsize],
           Abs[bsize[[2]]/delta[[2]]],
           Abs[bsize[[1]]/delta[[1]]]]];
       ;
       {Arrowheads[Medium], 
        Arrow[{pos, boxPosGet[pointName]}, {0, arrowDrawback}]}),
      {"MouseClicked" :> 
        If[CurrentValue["CommandKey"], 
         boxArrowPointDelete[nam[pointName]]]
       }, PassEventsUp -> False]
     ];(* end of arrow class *)

   boxArrowPointList[nam[pointName_]] ^:= 
    AppendTo[arrowPointList, pointName];
   boxArrowPointDelete[nam[pointName_]] ^:= (
     arrowPointList = 
      Delete[arrowPointList, 
       Position[arrowPointList, pointName][[1]]]
     );
   boxPosSet[nam[x_]] ^:= (pos = x);
   boxPosGet[nam] ^:= pos;
   boxTitleSet[nam[x_]] ^:= title = x;
   boxTitleGet[nam] ^:= title;
   boxAPListSet[nam[x_]] ^:= arrowPointList = x;
   boxAPListGet[nam] ^:= arrowPointList;
   boxSizeSet[nam[x_]] ^:= boxSize = x; 
   boxSizeGet[nam] ^:= boxSize; 

   boxBoth[nam] ^:= 
    Which[
     textInputMode == True,
     (* graphics of text-input-
     mode *)
     {arrowLine, targetArrowList,
      Text[
       EventHandler[
        InputField[Dynamic[title], String, FieldSize -> Small
         , BaseStyle -> myStyle],
        {"MouseEntered" :> None,
         "DownArrowKeyDown" :> Paste["\n"],
         "ReturnKeyDown" :> (boardDisable = False; 
           textInputMode = False; 
           boxSizeSet[
            nam[ImageDimensions@
              Rasterize@
               Text@Framed[title, FrameMargins -> 5, 
                 BaseStyle -> myStyle]]])
         }, PassEventsUp -> False], pos]},

     textInputMode == False,(* graphics-
     mode *)
     {Dynamic[{arrowLine, targetArrowList}],
      EventHandler[
       Text[Framed[title, FrameMargins -> 5, BaseStyle -> myStyle], 
        Dynamic[pos], Background -> LightGray],
       {"MouseDragged" :> Switch[CurrentValue["OptionKey"],

          True, (arrowLine = 
            Line[{boxPosGet[nam], MousePosition["Graphics"]}]),
          False, (pos = MousePosition["Graphics"])
          ],(* end of if-current value *)

        "MouseClicked" :> 
         Which[probe[$x[{nam, boardDisable}]]; (* 
          OOP probe program hook line *)

          CurrentValue["CommandKey"], removeInst[nam],
          CurrentValue["ShiftKey"], textInputMode = True; 
          boardDisable = True],
        "MouseMoved" :> (pointName = nam),(* 
        get the name of another instance *)
        "MouseUp" :> Which[
          CurrentValue["OptionKey"],
          (arrowLine = {};
           boxArrowPointList[nam[pointName]];(* 
           append point to the list *)
           (* 
           overide arrow instances *)

           targetArrowList := 
            Map[arrowImage[nam[#]] &, arrowPointList]),
          True, arrowLine = {}(* safety eraser *)
          ],(* 
        end of if-shift-key *)
        PassEventsUp -> False}, 
       PassEventsUp -> False](* end of event-handler *)
      }(* 
     end of graphics *)
     ];(* end of text-input-mode *)

   buildUpArrowList[nam] ^:= 
    targetArrowList := Map[arrowImage[nam[#]] &, arrowPointList]
   ]; (* end of box-class *)


 (* function to make box-instance with unique name *)

 makeInst[newPos_] := (
   newName = Unique[];
   box[newName];(* make new box instance *)

   boxPosSet[newName[newPos]];(* pop-up the instance on the board *)

     boxSizeSet[
    newName[ImageDimensions@
      Rasterize@Text@Framed[Null, BaseStyle -> myStyle]]];
   AppendTo[namList, newName];
   targetList := Map[boxBoth, namList](* makeup graphics *)
   );

 (* function to remove box-instance *)

 removeInst[
   boxName_] :=
  (namList = 
    Delete[namList, Position[namList, boxName][[1]]];
   targetList = Map[boxBoth, namList]);

 boardGraphics = 
  Graphics[Dynamic[targetList], 
   PlotRange -> {{0, boardRatio}, {0, 1.}}, 
   ImageSize -> {boardRatio, 1}*boardImageScale];

 delegate := (
   Which[boardDisable == True, boardGraphics,
    boardDisable == False, EventHandler[boardGraphics,
     "MouseClicked" :> Which[
       CurrentValue["OptionKey"] == True, 
       CreateDialog[
        Pane[Column[{Button["Save", 
            Put[Map[
              Map[#, namList] &, {List, boxAPListGet, boxTitleGet, 
               boxPosGet, boxSizeGet}], "graph"]; DialogReturn[]], 
           Button["Load", buildUp[Get["graph"]]; DialogReturn[]]}
          ], ImageMargins -> 5], Modal -> True, 
        WindowTitle -> "objects"]; boardDisable == False,
       True, makeInst[MousePosition["Graphics"]]
       ], PassEventsUp -> False](* 
    end of EventHandler *)
    ]);(* end of delegate graphics *)

 (* build up graphics from file *)

 buildUp[{namListX_, boxAPList_, boxTitle_, boxPos_, boxSize_}] := (
   Apply[Clear, namList = Flatten@namListX];
   targetList = {};
   Map[box, namList];
   MapThread[boxPosSet[#1[#2]] &, {namList, boxPos}];
   MapThread[boxSizeSet[#1[#2]] &, {namList, boxSize}];
   MapThread[boxTitleSet[#1[#2]] &, {namList, boxTitle}];
   MapThread[boxAPListSet[#1[#2]] &, {namList, boxAPList}];
   Map[buildUpArrowList, namList]; 
   Flatten@ReleaseHold[
     MapThread[f[#1, #2] &, {namList, boxAPList}] /. 
      f[nam_, p_] -> Hold@Map[boxArrowPointList[nam, #] &, p]];
   targetList = Map[boxBoth, namList];
   boardDisable = False
   );
 ](* end of Module *)

To start the program,

targetList = {}; namList = {};
SetDirectory[NotebookDirectory[]];
Framed@Deploy@Dynamic@delegate

then, you can see the blank board.

Usage is as follows.

Board area: Click makes text-box, and with Option-key Click shows menu for data save to a file and load data from a file.

Text-box: Drag moves text-box around with Shift-key Click move into the title input mode, and Return-key move out to the graphics mode with Command-key Click deletes text-box with Option-key Drag the mouse into another text-box make connection with an arrow

Arrow: with Command-key Click deletes arrow

Following is a sample code for saved data converter to the Mathematica Graph data.

g = Get["graph"][[1 ;; 3]];     (*  read the file *)

f[t_] := Map[Rule[t[[1]], #] &, t[[2]]];     (* define rule function *)

gl1 = Flatten@
   Map[f, Transpose[{Flatten[g[[1]]], 
      g[[2]]}]];     (*  construct vertex list of graph  *)

gl1 = gl1 /. 
   Rule[x_, y_] /; x == y -> Nothing;     (*  remove self loop *)

gl2 = Map[Rule[#[[1, 1]], #[[2]]] &, 
   Transpose[{g[[1]], 
     g[[3]]}]];(*  construct label list of graph  *)
gg = 
 Graph[gl1, VertexLabels -> gl2, ImagePadding -> 50, 
  GraphLayout -> "SpringEmbedding"]

Unfortunately, the number of vertex labels may be limited, you may chose the labels, and can get a graph converted from the GraphEditor file.

9 Replies

Hirokazu ... congratulations on your Featured Contributor Badge ... your excellent graph builder shows an amazing grasp of using Dynamic ... it would be great to add Szabolcs' suggested enhancements ... I'm investigating how to include a conditional probability table at each node ... btw I noticed a surplus command near the end of the delegate Which ... although it has no effect ... boardDisable == False thanks, Stuart

POSTED BY: Stuart Nettleton

Hello Stuart,

Thanks for your comment for a code line near the end of program. May be you are right and the concerned line may be a garbage. As mentioned previously, I think the code is too complex to re-analyze, and should be re-writing some day, however ...

Regards.

enter image description here -- you have earned Featured Contributor Badge enter image description here

Your exceptional post has been selected for our editorial column Staff Picks http://wolfr.am/StaffPicks and Your Profile is now distinguished by a Featured Contributor Badge and is displayed on the Featured Contributor Board. Thank you!

POSTED BY: EDITORIAL BOARD

Hello Stuart.

Thanks for interesting to my cord. Honestly speaking, the code is complex caused mix of Object Oriented, Event driven, Dynamic, and Graphics.... So, I have tryed to make it to neat, however, have been shelved until now. The responce encouraged me to try again. I'll do my best.

Great Hirokazu ... here are my files (need to remove txt from graph.txt as upload didn't allow just graph) ... perhaps another EventHandler is needed to set the Import & Export Filenames ... look forward to your additional thoughts!

POSTED BY: Stuart Nettleton

Hirokazu ... in the attached files ... a graph (& vertices, edges, vertexLabels and the output in your graph file) can be imported directly into the notebook ... and graphs can be saved and loaded by filename ... hope this helps

POSTED BY: Stuart Nettleton

Very nice! This is a cool application.

Regards

POSTED BY: Neil Singer

This is very nice. It would be great to modify it to create a graph within a notebook instead of saving it to disk. Another useful modification is to avoid using Null as VertexLabels. For some reason, Mathematica does not accept this.

With these modifications, it could become a truly practical replacement for the old (and no longer supported) GraphEdit[]

POSTED BY: Szabolcs Horvát

Thanks, Szabolcs. This project was started to show the power of Mathematica OOP and this program is a sample of OOP and EventHandler combinations. At the moment I'm intending to make a bridge between Mathematica and OmniGraffle rather than replace the function GraphEdit of Mathematica, however, you and anyone can modify this program freely.

Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract