# Is there a way to require confirmation for execution of certain cells?

Often I have Notebooks where I generate several images and export them into files. Now when I want to change one image, I'd like to just re-evaluate the complete notebook, however I generally do not want all images to be exported again, but only selected ones. For this, it would be useful if I could make some cells confirmation-only.

Ideally it would highlight the cell in question, but that's optional (I can just look at which cell is the first unevaluated). It should however make sure that the cell is visible (because Mathematica dialogs unfortunately tend to completely block the underlying notebook). It should ask me if I want to execute the cell, and if I say no, just skip it and continue at the next cell.

Is this possible, and if yes, how?

Bonus points if it doesn't trigger when doing Shift-Enter from within the cell in question (because in that case, it's obvious that I want it executed)

-
 Interesting idea. This is sort of reminiscent of what Mathematica does when it checks an older notebook for compatibility with a newer version. But what should be the trigger for recognizing a cell that has to throw a dialog? Perhaps the occurrence of any Plot* or Graphics* command in the cell? Or would it be OK to wrap the desired cell in a specially written function (which would make the cell harder to read)? – Jens Mar 7 '12 at 17:18 In fact, I would like to have such a functionality for cells that do Export (because I don't always want to overwrite exported graphics). Currently I manually make such cells non-evaluatable when not needed. – Jens Mar 7 '12 at 17:32 Indeed, cells containing Export are also the cells I would want that for. But I'd have no problems with a solution where I'd have to tag the cells accordingly. – celtschk Mar 7 '12 at 17:58

## 3 Answers

Perhaps there are better ways, but one I am aware of is by using CellEvaluationFunction option for a given cell. Here is code to generate some example cell with the behavior similar to what you presumably desire:

CellPrint[
Cell[BoxData[RowBox[{"100", "!"}]], "Input",
CellEvaluationFunction ->
(Module[{res = ChoiceDialog["Evaluate this cell?"]},
If[res, ToExpression[#]]] &)
]
]


A very informative account on CellEvaluationFunction, and the one I originally learned about it from, is this answer by @WReach.

EDIT

Here is a more elaborate version, which would also highlight the cell in question:

makeCell[] :=
With[{tag = ToString[Unique[]] <> DateString[]},
CellPrint[
Cell[BoxData[RowBox[{"100", "!"}]], "Input", CellTags -> tag,
CellEvaluationFunction ->
(
Module[{result = Null, thiscell, oldbackgr},
NotebookLocate[tag];
thiscell = NotebookSelection[];
oldbackgr =  Background /. Options[thiscell] /. Background -> None;
SetOptions[thiscell, Background -> LightGreen];
If[ChoiceDialog["Evaluate this cell?"], result = ToExpression[#]];
SetOptions[thiscell, Background -> oldbackgr];
SelectionMove[EvaluationNotebook[], After, Cell];
result
] &)
]]]


You can execute makeCell[] to see what this does.

-
Thanks. I cannot test until tomorrow, but it looks as if this would do what I want (except that I'd prefer to change existing cells accordingly, but I guess I could figure out that part). BTW, if I understand the code correctly, I think it doesn't restore the background color when not executing the cell. – celtschk Mar 7 '12 at 18:15
@celtschk Good catch. I noticed that and was just about to fix it when I got your comment. Modifying existing cells is just as easy: add a unique tag, plus an option CellEValuationFunction->... with the above function (with correct tag) manually (by pressing CTRL+SHIFT+E on Windows), and editing the cell's content, then pressing the same combination to get back to display the cell normally. I would however automate this, by writing a program which goes through each cell and asks you, whether or not you want to "patch" it with this code. – Leonid Shifrin Mar 7 '12 at 18:21
When trying this code, I noticed a side effect which is not relevant for my intended use case, but for other uses could be problematic: When choosing to not evaluate the cell, the output cell containing a previous evaluation result is removed. So if it is a long calculation, and you protect it to avoid unnecessary recalculation on further opening the notebook (because the output is only printed, but not used in the notebook later on), you'll not like the existing result going away. – celtschk Mar 8 '12 at 11:50
@celtschk That's a good point, and I am pretty sure it can be addressed. I will keep this in mind, to look into this whenever I get more time. Thanks for the accept. – Leonid Shifrin Mar 8 '12 at 12:56
Once you have the CellEvaluationFunction do what you want I'd suggest to create a stylesheet with a style, e.g. "ConfirmEvaluation" which inherits from "Input" but has this CellEvaluationFunction set. Then it's just a matter of switching the cell style to make a cell confirm on evaluation. If you give that entry a menu position lower than 10 you can even use a keyboard shortcut to switch between "Input" and "ConfirmEvaluation" ... – Albert Retey Jun 2 '12 at 18:44
show 1 more comment

For similar purposes using Button can be useful (in a non-sequential way):

images = ExampleData["TestImage"][[1 ;; 8]];
choice = ExampleData /@ images;
mods = ImageEffect[#, {"MotionBlur", 15}] & /@ choice;
buttons =
Button[Grid[{{"Export: " <> #[[1]]}, {Tooltip[#[[2]],
HoldForm[Export @@ #]]}}], Export @@ #] & /@
MapIndexed[{images[[#2[[1]], 2]] <> "_mod.jpg", #} &, mods];
Grid[Partition[buttons, 4]]


For lots of different images you can do a nice Grid of these buttons. You can especially add useful information to the button label, in this example with a Tooltipshowing the to-be-executed code. It can take some care to preserve content across kernel sessions - With is your friend here (see How to inject an evaluated expression into a held expression? ).

-
 That's definitely an interesting alternative. – celtschk Mar 8 '12 at 12:01 Yes, this is great idea! Instead of exporting the graphic, just produce a button which can be saved with the notebook and will export the graphic in one press – Szabolcs Jun 6 '12 at 8:25 @Szabolcs yes you do get rather nifty notebooks with a minimum amount of fuss. Also handy for presentation purposes. I guess there are many more of these potential gadgets out there we just have not got the hang of yet. – Yves Klett Jun 6 '12 at 8:50

I expanded the answer by @Leonid to restore the previous result if you cancel the evaluation and packaged the functionality into a cell style in my stylesheet for convenience. The cell expression for the stylesheet is pasted below.

This works in the cases I use often, but it has not been extensively tested. For example, it will not restore output in cases where multiple cells are generated during an evaluation of a single input cell. Also, only the last input of multiple input cells is displayed.

Cell[
StyleData["ExportCell", StyleDefinitions -> StyleData["Input"]],
CellEvaluationFunction->(
Module[{result = Null, thiscell, oldbackgr, tag, oldresult,evalq, nb},

(* find currently evaluating cell and change background *)
nb=EvaluationNotebook[];
SelectionMove[nb, All, EvaluationCell];
thiscell = NotebookSelection[];
oldbackgr = Replace[Background, Append[Options[thiscell], Background -> None]];
SetOptions[thiscell, {Background -> LightGreen, CellTags -> (tag = StringJoin["Export", ToString[Unique[]]])}];
NotebookLocate[tag];

(* ask user whether to evaluate cell *)
evalq = ChoiceDialog["Evaluate this cell?"];
SetOptions[thiscell, Background -> oldbackgr];

(* evaluate new result or reprint old result, as necessary *)
If[evalq,
result = ToExpression[#],

SelectionMove[nb,Next, Cell];
oldresult=NotebookRead[nb];

(* basic check for case when there is no existing output, will miss cases with other outputs, e.g., Print commands *)
If[oldresult[[2]]=="Output",
NotebookWrite[nb,oldresult];,
SelectionMove[nb,Previous,Cell];
];

];
SelectionMove[nb,After,Cell];

result
]& ),

CellFrameLabels->{{None, "Export"}, {None, None}},
MenuSortingValue->1500
]


EDIT:

Alternative check for output cell as suggested by @celtschk in comments is below. It's not an elegant implementation but it seems to work. However, this version may cause the input notebook to "blink" once as it autoscrolls rapidly back and forth in some cases where no output cell exists. Also, the input cursor location will not necessarily end up in the usual place since the front end normally moves it before evaluating any cells (actually, this is true of the previous version as well).

If[evalq,
result = ToExpression[#],

(* check if output cell exists *)
SelectionMove[nb,All,CellGroup, AutoScroll->False];
SelectionMove[nb,Before,CellGroup, AutoScroll->False];
SelectionMove[nb,Next,Cell, AutoScroll->False];

(* if input cell is first cell in a cell group *)
If[tag===(ReplaceAll[CellTags, Options[NotebookSelection[nb],CellTags]]),
SelectionMove[nb,All,CellGroup, AutoScroll->False];
SelectionMove[nb,After,CellGroup, AutoScroll->False];
SelectionMove[nb,Previous,Cell, AutoScroll->False];
oldresult = NotebookRead[nb];
(* if last cell in group is output cell *)
If[Part[oldresult, 2] == "Output",
NotebookWrite[nb, oldresult]
],

NotebookLocate[tag]
];
];

-
 Curiously, the AutoScroll option of NotebookLocate opens closed cell groups to show the target cell, while the same option for SelectionMove does not. – dws Jun 1 '12 at 23:16 Looks nice, thank you. Unfortunately I won't be able to check it till Monday. However I take your word that it works and vote it up anyways. BTW, maybe it helps in identifying the output cell that input and output cell (and additional cells in between generated by Print or Message) are combined into a surrounding cell. However that surrounding cell does not yet exist if there has not (yet) been generated an output, and the input cell might be part of another cell (like the ones generated by titles and sections). So maybe checking whether such an "evaluation supercell" exists and taking ... – celtschk Jun 2 '12 at 8:25 ... the last cell in that supercell is a solution. Of course there would still have to be check whether that cell is an output cell (for example, the kernel might have been killed during evaluation, so that no output cell was generated). – celtschk Jun 2 '12 at 8:27