11
$\begingroup$

I'm developing a package that needs to exert significant control over a notebook while a user interacts with it. One specific thing that it needs to do, programmatically, is to select a specific subset of Input cells in order to do things to them -- set cell options, check their contents, etc. I can't figure out how to do this.

One solution that occurred to me is to apply a CellTag to those Input cells, so that I can select them with NotebookFind or NotebookLocate. The problem with this strategy is that whenever any of these Input cells are evaluated, the corresponding Output cells inherit the tag. So, my NotebookFind will select both the Input cells I want and these new Output cells I don't. I could instead have NotebookFind locate all Input cells, but as far as I can tell there's no way to say "Find all cells of type Input with tag XXX." NotebookFind won't accept multiple search criteria, and can't be limited to currently-selected cells.

Another potential solution is to create a new kind of cell that functions just like an Input cell, and make all the cells I want to find be of this cell style. Unfortunately, I haven't had any luck "subclassing" the Input cell style this way, or finding any information about how to do that. It seems that if I want a cell to behave like an Input cell, it has to be an Input cell.

Can anyone wiser than I suggest a way to designate, and later to programmatically select, a subset of Input cells?

$\endgroup$

2 Answers 2

11
$\begingroup$

There is a special cell attribute called CellID. I would suggest using this instead of cell tags to avoid the problem that you mentioned with cell tags being inherited.

Here's an example of how Select can be used to retrieve a cell with a specific cell ID:

nb = CreateWindow[];

cells = <|
   "name" -> RandomInteger[10^6],
   "status" -> RandomInteger[10^6]
   |>;
NotebookWrite[nb, Cell["John Doe", "Subsection", CellID -> cells["name"]]];
NotebookWrite[nb, Cell["Employed", "Text", CellID -> cells["status"]]];

getCell[id_] := SelectFirst[Cells[nb], CurrentValue[#, "CellID"] == id &]

setContent[id_, content_] := NotebookWrite[
  getCell[id],
  Cell[content, "Text", CellID -> id]
  ]

You should now have a notebook in front of you with a name and an employment status. Example usage of setContent:

setContent[cells["status"], "It's complicated"]
$\endgroup$
5
  • $\begingroup$ So I can assign the same CellID to more than one cell? And then select them all, by using Select rather than SelectFirst? $\endgroup$
    – ibeatty
    Aug 8, 2015 at 16:15
  • $\begingroup$ @ibeatty Actually I would probably give each cell a unique ID and group IDs together in a list, as a matter of sound practice, in case you need to address them individually later on. But giving them the same ID would work too. $\endgroup$
    – C. E.
    Aug 8, 2015 at 16:28
  • $\begingroup$ @Pickett It seems that the same in principle can be achieved with CellTags. What is the advantages of CellID as compared to CellTags? $\endgroup$ Aug 8, 2015 at 16:34
  • 1
    $\begingroup$ @AlexeyPopkov ibeatty mentioned in his question that output cells inherit cell tags, so you'll end up with cells that have the same ID even though they shouldn't. This is not the case with CellID. $\endgroup$
    – C. E.
    Aug 8, 2015 at 16:37
  • $\begingroup$ This solution is less than ideal, because CellID must be manipulated programmatically whereas CellTags and cell styles can be set in the front end... but it seems to be the only possibility. So, I'm accepting the answer. $\endgroup$
    – ibeatty
    Aug 25, 2015 at 1:31
1
$\begingroup$

You can get the cells that satisfy multiple criteria by using Cells. For example:

CellPrint @ ExpressionCell[x^2, "Input", CellTags->"tag"];
CellPrint @ ExpressionCell[x, "Input", CellTags->"tag"];
CellPrint @ ExpressionCell[x^2, "Output", CellTags->"tag"];

cells = Cells[CellTags->"tag", CellStyle->"Input"]

x^2

x

x^2

{CellObject[84705], CellObject[84737]}

will return a list of all cells that have the CellTags "tag" and are "Input" cells. You can use the output in functions like NotebookRead, CurrentValue, SetOptions, etc:

NotebookRead[cells]
CurrentValue[cells, CellChangeTimes]
SetOptions[#, CellLabel->"FOO"]& /@ cells

{Cell[BoxData[SuperscriptBox["x", "2"]], "Input", GeneratedCell -> True, CellAutoOverwrite -> True, CellChangeTimes -> {3.71269*10^9}, CellTags -> "tag"], Cell[BoxData["x"], "Input", GeneratedCell -> True, CellAutoOverwrite -> True, CellChangeTimes -> {3.71269*10^9}, CellTags -> "tag"]}

{{3.71269*10^9}, {3.71269*10^9}}

{Null, Null}

$\endgroup$

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.