2
$\begingroup$

Following this post, I want to use code to generate an input cell with new code. However, unlike the linked post, I want my code to dynamically determine the new code to be printed in the input cell.

The use of Defer is making this difficult. For example, the following works:

makeImportCell[filename_] := 
   CellPrint[ExpressionCell[
       Defer[data = Import[filename]], "Input"]]

makeImportCell["myData.txt"]

Output (as an input cell):

data = Import["myData.txt"]

But if I want the function to manipulate the string filename, it breaks:

makeImportCell[filename_] := Module[{file},
  file = filename <> ".txt"; 
  CellPrint[ExpressionCell[Defer[data = Import[file]], "Input"]]]

makeImportCell["myData"]

Output (as an input cell):

data = Import[file$45844993]

I'd also like to use If statements based on some other variables to alter the code that gets generated, but if I put any Ifs into Defer, they'll be left unevaluated. Maybe the best solution would be to have the function construct a large string containing exactly the code I want to print, and then print this string as input code rather than a string. But I can't figure out how to do this.


I just realized I could use

makeImportCell[filename_] := 
   CellPrint[ExpressionCell[
       Defer[data = Import[filename <> ".txt"]], "Input"]]

to accomplish the same thing the second function is trying to do. But this doesn't generalize. I want to be able to manipulate the code (and strings within the code) that is to be generated, inside the body of Module, and then CellPrint exactly the code I want.


EDIT

J.M.'s answer prompted me to ask a more general version of this question, and the answer given there is particularly simple and elegant. It solves the problem of converting any arbitrary string of code into an input cell.

$\endgroup$
1
  • 3
    $\begingroup$ Use With, e.g. With[{file = filename <> ".txt"}, CellPrint[...]] $\endgroup$
    – Carl Woll
    Jul 20, 2022 at 20:57

2 Answers 2

6
$\begingroup$

You should just use With instead of Module, as Carl Woll suggests in the comments:

makeImportCell[filename_] := With[{file = filename <> ".txt"},
  CellPrint[ExpressionCell[Defer[data = Import[file]], "Input"]]]

However, be aware that usage of CellPrint for producing "Input" cells is discouraged:

I'm not a huge fan of this because of the combination of CellPrint and the "Input" style. This creates Input cells which have GeneratedCell->True and CellAutoOverwrite->True (this is documented behavior of CellPrint). If you start using these cells for real, then you may have unpleasant side effects...like your Input cells disappearing when you evaluate inputs above them. If you really want Input cells, I'd recommend a NotebookWrite based solution. The equivalent formulation would be NotebookWrite[EvaluationNotebook[],Cell[BoxData[ToString[#, InputForm]], "Input"]] &. – John Fultz (Jul 9, 2017 at 5:19)

To generate "Input" cells that behave exactly like regular user-created cells, use NotebookWrite instead:

makeImportCell[filename_] := With[{file = filename <> ".txt"},
  NotebookWrite[EvaluationNotebook[], 
   Cell[BoxData@MakeBoxes[data = Import[file]], "Input"]]]

Further reading:

$\endgroup$
7
  • 2
    $\begingroup$ If the GeneratedCell and CellAutoOverwrite options are the problem, you can also just manually set those options: CellPrint[ExpressionCell[expr, "Input", GeneratedCell -> False, CellAutoOverwrite -> False]] $\endgroup$ Jul 21, 2022 at 6:33
  • 1
    $\begingroup$ @SjoerdSmit I tried it. My experiments showed that it doesn't allow to reproduce the behavior we get with NotebookWrite. I don't know what, but there seems to be something besides these options that affects this. See the last words in this comment. Also, I think John Fultz has serious reasons to recommend NotebookWrite instead of CellPrint. $\endgroup$ Jul 21, 2022 at 8:36
  • $\begingroup$ I also want the generated cell to be an initialization cell, which I can do with CellPrint[..., InitializationCell -> True], but not NotebookWrite. However, I'm not finding any overwrite issues with CellPrint. Maybe the default behavior was updated? $\endgroup$
    – WillG
    Jul 21, 2022 at 19:41
  • $\begingroup$ E.g., execute the following code multiple times. I get a new generated cell each time, and nothing is overwritten. CellPrint[ Cell[BoxData@"Module[{x}, x = 1]", "Input", InitializationCell -> True]]. $\endgroup$
    – WillG
    Jul 21, 2022 at 19:42
  • 1
    $\begingroup$ @WillG Just tried, yes: with such simple setup it works as expected, but not with more complicated. Try the setup from the linked question: CellPrint/@{Cell["1\n2","Input",InitializationCell->True],Cell["I'll disappear after evaluation of the previous cell!","Text"],Cell["3","Input",InitializationCell->True]};. $\endgroup$ Jul 22, 2022 at 1:19
4
$\begingroup$

This required a bit more trickery than I would like:

makeImportCell[filename_] := Module[{file},
    file = filename <> ".txt";
    CellPrint[Cell[BoxData[RowBox[{"data", "=", 
                                   RowBox[{"Import", "[",
                                           "\"" <> file <> "\"", "]"}]}]], 
                   "Input"]]]

and then evaluating makeImportCell["fooba"] does print a cell with data = Import["fooba.txt"].

$\endgroup$
5
  • $\begingroup$ Thanks, it seems like I can use a similar method to construct arbitrary output code. However, I'm not familiar with the BoxData and RowBox structure that is apparently used to encode notebook expressions. Is there a handy way to convert an already-typed notebook expression, e.g., data=Import["filename.txt"], into the explicit BoxData/RowBox form you used here? $\endgroup$
    – WillG
    Jul 20, 2022 at 21:24
  • 1
    $\begingroup$ Something like MakeBoxes[data = Import["filename.txt"], StandardForm] would be useful for that purpose. $\endgroup$ Jul 20, 2022 at 21:30
  • $\begingroup$ I might make a new post about this, but... Suppose I have some string str which is a valid piece of code that evaluates upon ToExpression@str. I can simply copy/paste the contents of this string into a new cell, and they are automatically formatted correctly. Therefore, it seems this copy/pasting action should be easy to automate with code. Is there a way to use CellPrint so that str appears in a new cell as correctly formatted code? $\endgroup$
    – WillG
    Jul 21, 2022 at 5:33
  • 1
    $\begingroup$ I like the method outlined above with MakeBoxes, but it still requires manually fiddling around with each particular expression. Seems like there should be a simpler way to automate the string -> input code conversion that works for arbitrary strings of (valid) code. $\endgroup$
    – WillG
    Jul 21, 2022 at 5:40
  • $\begingroup$ Made a new post: mathematica.stackexchange.com/questions/271091/… $\endgroup$
    – WillG
    Jul 21, 2022 at 5:51

Your Answer

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

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