6
$\begingroup$

I would like to change the edge color of a graph when double clicking on it, the following code works:

DynamicModule[{col = Green, col2 = Blue, col3 = Green},
    Dynamic@Graph[{1, 2, 3} , {
        EventHandler[1 \[UndirectedEdge] 2,
        {"MouseDown" :> If[CurrentValue[
            "MouseClickCount"] == 2, (col = col /. {Red -> Green, Green -> Red})]}
 ],

EventHandler[3 \[UndirectedEdge] 2,
    {"MouseDown" :> If[CurrentValue[
        "MouseClickCount"] == 2, (col2 = col2 /. {Red -> Blue, Blue -> Red})]}
 ],

EventHandler[1 \[UndirectedEdge] 3,
    {"MouseDown" :> If[CurrentValue[
        "MouseClickCount"] == 2, (col3 = col3 /. {Red -> Green, Green -> Red})]}
 ] }, 
EdgeStyle -> {1 \[UndirectedEdge] 2 -> col, 
              3 \[UndirectedEdge] 2 -> col2,
              1 \[UndirectedEdge] 3 -> col3}

   ](* Graph *)
]

But when I changed it like this, it doesn't work at all. Would somebody please help and point out what is wrong?

DynamicModule[{kk = {3 \[UndirectedEdge] 2, 1 \[UndirectedEdge] 2, 
                     3 \[UndirectedEdge] 1 },
               col = Green, col2 = Blue, col3 = Green}, 
               Dynamic@Graph[{1, 2, 3}, 
               Table[EventHandler[i, {"MouseDown" :> 
                   If[CurrentValue["MouseClickCount"] == 2,
                           (col = col /. {Red -> Green, Green -> Red})]}
   ], {i, kk}] ]
]

@Pickett Thanks so much for your detail instructions, I learned a lot from you. And I followed your method, and take a step further, intending to write some codes to delete the unwanted edges from a RandomGraph by double clicking on them. By inspecting the FullForm[dynGraph], the EventHandlers seems attached to each edge correctly, but again it doesn't work. How can I get around not setting the value of edglst in the first place?

DynamicModule[{rg, edglst, eventhlers }, 
 rg = RandomGraph[{6, 12}];
 vlst = VertexList[rg];
 edglst = EdgeList[rg];
 vcood = VertexCoordinates /. AbsoluteOptions[rg, VertexCoordinates];
 eventhlers = 
  MapThread[
   EventHandler[#, {"MouseDown" :> 
       If[CurrentValue["MouseClickCount"] == 2, (
         pos = First@First@Position[edglst, #];
         edglst = Drop[edglst, {pos}];
         eventhlers = Drop[eventhlers, {pos}])]}] & , {  edglst } ];
 dynGraph = 
  Dynamic@Evaluate@
    Graph[vlst, eventhlers, VertexCoordinates -> vcood, 
     EdgeStyle -> {Thick}];
 Evaluate@eventhlers;
 dynGraph
  ]
$\endgroup$
0

2 Answers 2

6
$\begingroup$

Tell me if that's what you are looking for.

DynamicModule[{kk = {3 <-> 2, 1 <-> 2, 3 <-> 1}, col},
 col[1] = Green; col[2] = Red; col[3] = Blue;
 Dynamic@Graph[{1, 2, 3}, 
   EventHandler[#, {"MouseDown" :> 
     If[CurrentValue["MouseClickCount"] == 2, 
       (col[#2] = col[#2] /. {Red -> Green, Green -> Red,
        Blue -> Yellow, Yellow -> Blue})]}] & @@@ Thread[{kk, Range@Length@kk}], 
   EdgeStyle -> (Rule[#, #2] & @@@ Thread@{kk, col@# & /@ Range@Length@kk})]]

enter image description here


Regarding your second edit, you were almost there. I believe that you confused quite a lot of things but here you go.

DynamicModule[{rg, vlst, edglst, eventhlers, pos}, 
  rg = RandomGraph[{6, 12}];
  vlst = VertexList[rg]; edglst = EdgeList[rg];
  vcood = VertexCoordinates /. AbsoluteOptions[rg, VertexCoordinates];
  eventhlers = MapThread[EventHandler[#, {"MouseDown" :> 
       If[CurrentValue["MouseClickCount"] == 2, (pos = Flatten@Position[edglst, #];
         edglst = Drop[edglst, pos];
         eventhlers = Drop[eventhlers, pos])]}] &, {edglst}];
  Dynamic@Graph[vlst, eventhlers, VertexCoordinates -> vcood, 
    EdgeStyle -> {Thick}, VertexLabels -> "Name", ImagePadding -> 10]]

enter image description here

$\endgroup$
5
  • $\begingroup$ +1, I like that you pass around color indices instead of symbols. You should consider upvoting questions you answer, especially when they were posted by new users. Since you put some time into this, you probably thought it was a good question. $\endgroup$
    – C. E.
    Jun 21, 2014 at 23:06
  • $\begingroup$ @Pickett Thanks :) I liked the idea indeed, but I believe that this question has been answered somewhere before.. :) I didn't "learn" how to do that by myself :D But you provided a nice & complete answer, you have my +1 :) $\endgroup$
    – Öskå
    Jun 21, 2014 at 23:28
  • 1
    $\begingroup$ @Öskå Thank you so much! $\endgroup$
    – Putterboy
    Jun 22, 2014 at 17:24
  • $\begingroup$ @Öskå Would you please give some explanations on what col[] is, MMA responses as it is not a symbol when I checked with DownValues, OwnValues and Attributes, definitely it is not a function, right? $\endgroup$
    – Putterboy
    Jun 23, 2014 at 15:19
  • $\begingroup$ @user16069 Try col["hello"] = 10 ?col col["hello"]. It's basically an array without the array construction. $\endgroup$
    – Öskå
    Jun 23, 2014 at 15:27
5
$\begingroup$

TL;DR In order to understand why what you did doesn't work you have to read up on how Mathematica stores information internally (DownValues and OwnValues), how and when Mathematica evaluates expressions (including how attributes affects that). There's a code halfway through that will do what you want.


You asked "would somebody please help and point what is wrong?" which is a very good question, I think, because it gets down to dynamic functionality of the kind that is one of the most difficult Mathematica concepts to learn. I will explain this concept, but first of all you have some obvious mistakes in your code, let's start there.

Table[EventHandler[i, {"MouseDown" :> 
                   If[CurrentValue["MouseClickCount"] == 2,
                           (col = col /. {Red -> Green, Green -> Red})]}
   ]

The table that this code creates uses col for all three colors. That's a big mistake. And also your code doesn't include EdgeStyle, so whatever changes you make to col won't be reflected in the edge style of the graph. Fixing these mistakes while still doing it the way you intended, by constructing a table of EventHandlers, requires jumping through hoops...

First of all we have to recall how the values of variables are stored in Mathematica.

testVal = 5;
var = testVal;
OwnValues[var]
(* Out: {HoldPattern[var] :> 5} *)

versus

var = undefinedVar;
OwnValues[var]
(* Out: {HoldPattern[var] :> undefinedVar} *)

OwnValues tells us what Mathematica will replace a symbol with, i.e. what that symbol's value is. As you can see, if we attempt to save a symbol in another symbol when it already has a value there is no link to the original symbol. For that reason it won't work to write

MapThread[EventHandler[#, {
    "MouseDown" :> 
     If[CurrentValue["MouseClickCount"] == 
       2, (#2 = #2 /. {Red -> Green, Green -> Red})]
    }] &, {graphs, colors}
 ]

If colors = {col1,col2,col3} where col1, col2 and col3 already have values (this is what you tried to do with your Table. I'm using MapThread instead of Table, but they can do the same things). Then if we look at the result it would just be a bunch of rules like Green = Green /. {Red -> ...}. We need to store {col1, col2, col3} in colors in such a way that it stores the symbols, not the values. I.e. like the second example above. That is exactly what will happen if we don't define col1 etc. before we create the table of EventHandlers. With this preliminary discussion I will post my code now. In order to understand it you need to pay close attention to when I defined variables, so you know which OwnValues refer to symbols and which refer to values.

DynamicModule[{
  graphs = {3 \[UndirectedEdge] 2, 1 \[UndirectedEdge] 2, 3 \[UndirectedEdge] 1},
  col1, col2, col3, colors,
  eventHandlers, edgeStyles, defaultColors, dynGraph
  },
 colors = {col1, col2, col3};
 defaultColors = {Green, Blue, Green};

 edgeStyles = MapThread[# -> #2 &, {graphs, colors}];

 eventHandlers = MapThread[
   EventHandler[#, {
      "MouseDown" :> 
       If[CurrentValue["MouseClickCount"] == 2, #2 /. {Red -> Green, Green -> Red}; (#2 = #2 /. {Red -> Green, Green -> Red}); #2]
      }] &,
   {graphs, colors}
   ];
 dynGraph = Dynamic@Evaluate@Graph[
     {1, 2, 3}, eventHandlers, EdgeStyle -> edgeStyles
     ];

 Evaluate@colors = defaultColors;

 dynGraph
 ]

There are number of "teaching opportunities" here. Since we just said basically that you can't work with defined variables, you can't pass around col1, col2 and col3 if they already have values because you will just be passing around their values instead of those symbols, it's interesting to take look at where I define those values. I don't until the very end with this line:

Evaluate@colors = defaultColors;

Set (=) has the attribute HoldFirst.

Attributes[Set]
(* Out: {HoldFirst, Protected, SequenceHold} *)

Which means that the left hand side of = won't be evaluated. But since I don't want to assign the list of colors to colors I use Evaluate to force evaluation. Evaluating means replacing the symbols with their values. The right hand side isn't held so it is automatically evaluated, so what that line actually says is

{col1, col2, col3} = {Green, Blue, Green}

You make a mistake in your code that is related to this. You write

col = col /. {Red -> Green, Green -> Red}

but since col has a value and only the left hand side is held, you actually get

col = Red

through replacement of col with Green and then by application of the rules. This is another example of how your code doesn't work because you defined col too early.

Another thing you can see is that I stored the graph in a variable dynGraph. That means that the stored definition of dynGraph includes all the color symbols on the right hand side. This is another trick I had to use because of the problem with defining symbols too early.

$\endgroup$
2
  • $\begingroup$ Thanks so much for your detail instructions, I learned a lot from you. And I followed your method, and take a step further, intending to write some codes to delete the unwanted edges from a RandomGraph by double clicking on them. The EventHandlers seems attached to each edge by inspecting the FullForm[dynGraph], but again it doesn't work. $\endgroup$
    – Putterboy
    Jun 22, 2014 at 2:08
  • $\begingroup$ @user16069 Things like these are complicated no doubt. I don't have the time or energy to help you more than this, but either my or Öskås strategy could surely be generalized to make it work. Mine is basically just about understanding how Mathematica works and then using that. So if you keep at it and really think about when and where evaluation takes place maybe you'll find a way. $\endgroup$
    – C. E.
    Jun 22, 2014 at 3:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.