Say, I have three opened notebooks Untitled-1, Untitled-2, Untitled-3 in first instance of Mathematica and three notebooks Untitled-1, Untitled-2, Untitled-3 in second instance of Mathematica. How do I distinguish which notebook belong to which instance? Is it possible to have notebooks of one instance to have different window border color then notebooks of another instance? Preferably that it would be done automatically - i.e. each new instance of Mathematica would choose different color of window.
-
2$\begingroup$ Because usually when one notebook of Mathematica crashes then all other notebooks of the same instance crash too. But notebooks of another instance are unaffected. That is why I always use several instances of Mathematica, but then I am confused, which notebook belong to one instance and which to the other. $\endgroup$– azerbajdzanCommented Sep 10, 2021 at 12:11
-
1$\begingroup$ When you open a new notebook, save it with a name that indicates the instance used. $\endgroup$– Bob HanlonCommented Sep 10, 2021 at 14:09
-
2$\begingroup$ @Szabolcs Is it this? superuser.com/questions/378995/… $\endgroup$– internetCommented May 14 at 10:39
-
2$\begingroup$ @internet Yes! I've no idea how that ended up on SuperUser. Probably migrated from SO as "not programming related", one of the many reasons why we couldn't wait to create our own site ... $\endgroup$– SzabolcsCommented May 14 at 11:01
-
1$\begingroup$ @rhermans One reason I use multiple instances is often I have a notebook running with a calculation taking a long time. That is unusable until the calculation is finished. I open a new notebook with a different kernel and I can continue working with mathematica while the first notebook works away. $\endgroup$– Bill WattsCommented Jun 15 at 20:23
3 Answers
Based on comments by @Szabolcs, @internet and answer by @BrettChampion.
Inserting the following code into the file $UserBaseDirectory <> "\\Kernel\\init.m"
:
SetOptions[$FrontEndSession,
DockedCells ->
Cell[BoxData[ToBoxes["", StandardForm]], "DockedCells",
ShowStringCharacters -> False, CellMargins -> {{0, 0}, {0, -15}},
Background ->
ColorData[97, Length[SystemProcesses["Mathematica.exe"]]]]]
The width of the cell can be controlled by CellMargins -> {{0, 0}, {0, -15}}
.
Still it would be better if the background color of the frame could be changed instead of adding DockedCells
. So I still do not consider my question as answered.
There may be some lag of several seconds before the cell appears. I guess because of Length[SystemProcesses["Mathematica.exe"]]
which counts number of instances of Mathematica and it seems to be a little bit slow.
-
$\begingroup$ The solution you want involves communication between frontends,
PersistentSymbol
may satisfy this need. Besides, you can useFirst@First@$FrontEnd
to distinguish between different frontends. $\endgroup$– wioiwCommented May 14 at 16:27 -
$\begingroup$
SystemProcesses
do the job without need of communication between frontends. $\endgroup$ Commented May 14 at 17:25 -
$\begingroup$
SystemProcesses["Mathematica.exe"]
is a bit slow, could useOnce[ColorData[97, UnixTime[]]]
. $\endgroup$– SilviaCommented May 17 at 12:45 -
$\begingroup$ @Silvia This does not guarantee that the colors would be distinguishable enough. $\endgroup$ Commented May 17 at 12:58
-
1$\begingroup$ fyi, instead of
$UserBaseDirectory <> "Kernel\\init.m"
it should be$UserBaseDirectory <> "\\Kernel\\init.m"
notice the extra `\` before the kernel. Otherwise it will generate wrong folder name. $\endgroup$– NasserCommented May 17 at 18:16
Your post contains two separate, unrelated questions:
- How can I distinguish opened windows by their parent process ID?
- How can I change the titlebar/border color of a window?
The following mini package WindowColor
solves both problems. However, unfortunately, it solves the 2nd problem only on Windows 11. It looks like you are using Windows 10, which – as far as I know* – does not support per-window settings of colors (you can only change the color of all windows). On Win10, you are therefore stuck only with changing the light/dark theme of a window, so you could only distinguish between two** instances.
Here is an example of using the package. First, obtain a list of all Mathematica windows:
Needs["WindowColor`"];
windows = ListMathematicaWindows[]
(* <|18800 -> {134172, 265248, 857768}, 3588 -> {200468}, 6844 -> {71604}|> *)
Each window is identified with its window handle (hwnd), and they are grouped by their corresponding process ID. As you can see, I have five Mathematica windows, coming from three different Mathematica.exe
processes.
Now let's have some fun with the colors! There are four things you can set:
- border color (Win11 only)
- caption color (Win11 only)
- text color (Win11 only)
- light/dark mode
window = First@First@windows;
(* These work only on Win11 *)
WindowBorderColor[window, Red];
WindowCaptionColor[window, Darker@Green];
WindowTextColor[window, White];
window = Last@First@windows;
(* This works on Win10 and Win11 *)
WindowDarkMode[window];
Coloring windows by their parent process is now a trivial matter, for example:
colorlist = ColorData[97, "ColorList"];
MapThread[WindowCaptionColor[#1, #2] &, {Values@windows, Take[colorlist, Length[windows]]}];
MapThread[WindowBorderColor[#1, #2] &, {Values@windows, Take[colorlist, Length[windows]]}];
WindowTextColor[Flatten@Values@windows, White];
WindowColor
package
This package relies heavily on NetLink
and calling Windows DLLs (dwmapi.dll
, user32.dll
) directly.
BeginPackage["WindowColor`"];
WindowBorderColor::usage="WindowBorderColor[hwnd, color] changes the color of the window border.";
WindowCaptionColor::usage="WindowCaptionColor[hwnd, color] changes the color of the titlebar.";
WindowTextColor::usage="WindowTextColor[hwnd, color] changes the color of the text in the titlebar.";
WindowLightMode::usage="WindowLightMode[hwnd] turns off the dark mode for the window.";
WindowDarkMode::usage="WindowDarkMode[hwnd] turns on the dark mode for the window.";
ListMathematicaWindows::usage="ListMathematicaWindows[] returns an association of all visible windows, created by Mathematica.exe, grouped by their parent process ID.";
Begin["`Private`"];
Needs["NETLink`"];
LoadNETType["System.Diagnostics.Process"];
DwmSetWindowAttribute=DefineDLLFunction["DwmSetWindowAttribute","dwmapi.dll","Int16",{"HWND","DWORD","byte[]","DWORD"}];
EnumWindowsCallbackDelegate = DefineNETDelegate["EnumWindowsCallback", "BOOL", {"HWND", "LPARAM"}];
EnumWindows=DefineDLLFunction["EnumWindows","user32.dll","BOOL",{EnumWindowsCallbackDelegate,"LPARAM"}];
GetWindowThreadProcessId=DefineDLLFunction["GetWindowThreadProcessId","user32.dll","DWORD",{"HWND","out int"}];
IsWindowVisible=DefineDLLFunction["IsWindowVisible","user32.dll","BOOL",{"HWND"}];
(* DWMWINDOWATTRIBUTE enums *)
DWMWAUSEIMMERSIVEDARKMODE=20;
DWMWABORDERCOLOR=34;
DWMWACAPTIONCOLOR=35;
DWMWATEXTCOLOR=36;
(* Auxiliary function for converting numeric hwnd to pointers *)
EnsureHwnd[hwnd_Integer]:=NETNew["System.IntPtr", hwnd];
EnsureHwnd[hwnd_]:=hwnd;
SetAttributes[{WindowBorderColor,WindowCaptionColor,WindowTextColor,WindowDarkMode,WindowLightMode},Listable];
WindowColor[hwnd_,attr_,color_]:=DwmSetWindowAttribute[EnsureHwnd@hwnd,attr,255*Append[List@@RGBColor[color],0],4];
WindowBorderColor[hwnd_,color_]:=WindowColor[hwnd,DWMWABORDERCOLOR,color];
WindowCaptionColor[hwnd_,color_]:=WindowColor[hwnd,DWMWACAPTIONCOLOR,color];
WindowTextColor[hwnd_,color_]:=WindowColor[hwnd,DWMWATEXTCOLOR,color];
WindowDarkMode[hwnd_]:=DwmSetWindowAttribute[EnsureHwnd@hwnd,DWMWAUSEIMMERSIVEDARKMODE,{1},4]
WindowLightMode[hwnd_]:=DwmSetWindowAttribute[EnsureHwnd@hwnd,DWMWAUSEIMMERSIVEDARKMODE,{0},4]
ListMathematicaWindows[]:=Module[{hwnds={},pIDs={},pIDsMathematica={},wins={},enumWindowsCallbackFunction,enumWindowsCallbackDelegate},
(* Get window handles (hwnds) of all visible windows *)
enumWindowsCallbackFunction[hwnd_,lparam_]:=(If[IsWindowVisible[hwnd]===1,AppendTo[hwnds,hwnd];KeepNETObject[hwnd]];1);
enumWindowsCallbackDelegate=NETNewDelegate[EnumWindowsCallbackDelegate,enumWindowsCallbackFunction];
EnumWindows[enumWindowsCallbackDelegate,0];
(* Get corresponding process IDs of all visible windows *)
(GetWindowThreadProcessId[#,pID];AppendTo[pIDs,pID])&/@hwnds;
(* Get process IDs of all Mathematica.exe *)
pIDsMathematica=Sort@Table[p@Id,{p,Process`GetProcessesByName["Mathematica"]}];
(* Select windows created by Mathematica.exe *)
wins = Select[Transpose[{hwnds,pIDs}],MemberQ[pIDsMathematica,Last@#]&];
(* Dereference pointers *)
wins = MapAt[#@ToInt32[]&,wins,{All,1}];
(* Group by process ID *)
GroupBy[wins,Last->First]
]
End[];
EndPackage[];
* I may be wrong, but please refer to StackOverflow for resolving this. Also, note that applications on Win10 certainly can have customly colored windows, but this is because they use their own functions for drawing windows, not the native Windows ones.
** Obviously, there is another simple solution of using WindowTitle
and prepending some distinguishable geometric symbols.
Idea
As explained as my comment under @azerbajdzan's post.
Initialization of global scope:
LocalSymbol@"frontEndSessions" = {};
Initialization of FrontEndSession
scope:
AppendTo[LocalSymbol@"frontEndSessions", First@First@$FrontEnd];
SetOptions[$FrontEndSession, DockedCells ->
Cell[
BoxData[ToBoxes["", StandardForm]],
"DockedCells",
ShowStringCharacters -> False,
CellMargins -> {{0, 0}, {0, -15}},
Background -> ColorData[97, Length@LocalSymbol@"frontEndSessions"]
]
];
$Epilog := (LocalSymbol@"frontEndSessions" //= DeleteCases[#, First@First@$FrontEnd]&;);
Shortage
$Epilog
won't be called when Quit
or Exit
are not called, such as the situation when you close the last notebook of one FrontEndSession
. That means LocalSymbol@"frontEndSessions"
may contain dead FrontEndSession
.
Performance
3~4 times faster than @azerbajdzan's code, but nearly same if the time of launching Mathematica.exe is taken into account.
-
$\begingroup$ Maybe
NotebookEventActions
is a better choice, but needs more detailed design. $\endgroup$– wioiwCommented Jun 15 at 15:13