3
$\begingroup$

I have a text file called file.txt that is being modified every few seconds by an external program (my Stenography machine's software).

myCode is a program which takes as input a List called data which is Imported from file.txt.

How can I execute myCode everytime this file is modified?

The following code does not work but looks like what I need.

file = NotebookDirectory[] <> "file.txt";
Dynamic[
    Refresh[
        fileSize := FileByteCount[file];
        data = Import[file, "Table"];
        myCode[data]
        ,TrackedSymbols :> {fileSize}
    ]
]
  • to help de-abstract the problem, here is are two example cases of myCode and file.txt

    myCode[data_] := Style[ListLinePlot[Flatten[data], PlotTheme -> "Marketing"], Magnification -> 2];
    Export[NotebookDirectory[]<>"file.txt", {1, 2, 3}]
    

This question has been asked 8 years ago for Mathematica 9.0. Sadly the answer provided then does not work in Mathematica 12.0

Note: I am not interested in repeatedly executing myCode every $n$ seconds. I only want to execute myCode IF file.txt is modified.


I'm afraid I oversimplified my question above. I wanted to try and present a minimal example. My true problem is below:

myCode is a program which takes the last element of data (a list of integers Imported from file.txt) and computes the ratio with the last element of another list (imported from another file which is sometimes modified (when this second file is modified it is modified at the same time $t$ as when file.txt is modified)). myCode then AppendsTo this ratio to a third list (which is then plotted). I only want it to do this operation if file.txt is modified.

$\endgroup$
4
  • 1
    $\begingroup$ Can you please clarify what the outcome of running the linked code is? How does it “not work”? $\endgroup$ Feb 10 at 7:01
  • 2
    $\begingroup$ Try this: mathematica.stackexchange.com/a/104118/5478 $\endgroup$
    – Kuba
    Feb 10 at 8:05
  • $\begingroup$ Thanks @Kuba. That looks like what I need. $\endgroup$ Feb 10 at 22:13
  • 2
    $\begingroup$ if you are happy with Kuba's suggestion there is nothing wrong with it. On the other hand, there is the concept of a "file system watcher" which will allow to call a function when a file changes without polling for changes. In this QA you will find an example of this using NETLink. If you are not on Windows, there is similar functionality for Java... $\endgroup$ Feb 11 at 0:02
1
$\begingroup$

Look at your code. You request that the code inside Request is executed every 5 seconds. AT the same time you specify TrackedSymbols :> {fileSize} what means, that the code should be executed when fileSizechanges. But this is not necessary because the code is executed every 5 seconds anyway.

Here is a code example that runs on version 12.1:

 myCode[d_] = ListLinePlot[d];
Dynamic[
 data = Import[file];
 myCode[data], UpdateInterval -> 2]
$\endgroup$
4
  • $\begingroup$ Thanks for your answer. I'm afraid I oversimplified my problem. I only want to execute myCode when file.txt is modified. I do not want to execute it every $n$ seconds. I hate asking complicated questions but I have added a short description to the bottom of my question. Your answer is nevertheless good because it explains how to Dynamically use Import. $\endgroup$ Feb 10 at 22:12
  • $\begingroup$ That is easily fixed. Check every 5 seconds if the file changed and only update it if so. To check if it changed, the method with ByteCount can be used, but the problem is when the file changed, but the length stays the same. An other method would be to calculate a hash. Can you make the change yourself? $\endgroup$ Feb 11 at 8:39
  • $\begingroup$ Yes I can make the change myself using a text editor. $\endgroup$ Feb 11 at 9:49
  • $\begingroup$ Thanks for your ideas. I used them in my answer below. $\endgroup$ Feb 11 at 18:38
1
$\begingroup$

update: A superior solution can be found here


Here is a simple solution using FinishDynamic inside a While loop that checks every $\Delta t$ seconds for changes and runs myCode once if a change is detected:

step 1

Execute:

Dynamic[x]

step 2

Run a While loop that calculates one FileHash of file.txt before and another FileHash after a small interval $\Delta t$. In each iteration: use an If statement to check if the hashes are equal or not. If they are not equal then myCode is executed. FinishDynamic pauses the While loop during each iteration until the output of myCode is rendered in the Dynamic cell above the cell containing the While loop.

Δt = 0.1;
file = NotebookDirectory[] <> "file.txt";
myCode[data_] := Style[ListLinePlot[Flatten[data], PlotTheme -> "Marketing"], Magnification -> 2];

While[True,
      FinishDynamic[];
      fileHashT = FileHash[file];
      Pause[Δt];
      fileHashTplusΔt = FileHash[file];
      
      If[ fileHashT != fileHashTplusΔt,
      
          data = Import[file, "Table"];
          x = myCode[data]
       ]   
]

enter image description here


note:

in this example, file.txt was created with:

Export[NotebookDirectory[]<>"file.txt",{1,2,3}]//SystemOpen
$\endgroup$
2
  • $\begingroup$ It seems like you should be able to do this with the sort of functionality in the answer you linked to, no? $\endgroup$ Feb 12 at 5:02
  • $\begingroup$ @CATrevillian You are right! A small modification to the linked answer makes it work with Mathematica 12.0. Thanks. $\endgroup$ Feb 12 at 20:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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