The latter will be easier to debug, because you will have smaller functions to check, test, etc.
If not a GUI, perhaps tell them to edit an excel spreadsheet? To have them choose files, you can use NativeFileDialogue.jl.
Hmm, I thought I would be ok regardless of where they run the file from based on the documentation of @__dir__
: âExpand to a string with the absolute path to the directory of the file containing the macrocall.â
Yeah, most of the data files I am reading are already .xlsx
. I wasnât sure that .xlsx
was a good medium for reading scalar inputs though. NativeFileDialog.jl seems really useful.
In any case, I can test out some different file types and helper packages (maybe __init__
too). I just wanted to make sure I wasnât overlooking some obvious workflow. It sounds like I should still be using a package (and thus a module) but that it would be better to put function calls inside process
rather than include
statements, even if that means I need to define containers for everything.
Give the name of the input file as argument? julia script.jl arg1 arg2
(Getting Started ¡ The Julia Language)
I think OPâs audience is on Windows and probably doesnât know how to use the command line.
What if your users had to create this type of script file?
user_custom_script.jl_script
:
#!JLScript -iq --startup-file=no --project=@GlobalScripting_ASME
using ASME_Materials
#raw"": Allows paths with "\" without escaping (ex: "S:\\Material..."):
data_root = raw"S:\Material Properties"
options = (
input_file = joinpath(data_root, "Section II-D Tables.xlsx"),
output_dir = joinpath(data_root, raw"Excel Material Data\AIP Q&T Steels"),
)
result = ASME_Materials.RunTheScript(options)
display(result) #Because: Why not?
:DONE
âŚand execute it from with a right-click in Windows Explorer?
Example code
â see: GitHub - ma-laforge/JuliaScripting.jl
You could provide (copy) the basic âScripting interfaceâ from JuliaScripting.jl
:
(copying mostly JuliaScripting/src/install.jl
)
- Creates a sample âsharedâ Julia environment (
GlobalScripting_ASME
).- (
GlobalScripting_ASME
environment is used by sample scriptsample_scripts/sample1.jl_script
.)
- (
- Copies a powershell script (
launch_julia_script.ps1
) to the active Julia directory.- This might require Windows admin privileges.
- Adds RMB âExecute with Juliaâ action for
.jl_script
files in Windows Explorer.- Also requires Windows admin privileges.
Thatâs really cool, but admin privileges might be an issue.
If admin privileges are an issue:
-
You wonât need them to write the
.ps1 file
⌠since it gets copied to your Julia install directory (which presumably was installed without admin privileges either) -
You would only need them to execute/install the
.inf
file to register the RMB action item in Windows Explorer.
But since the .inf
file is relatively simple/easy to decode, you can probably convince someone in IT to install it for you:
[version]
signature="$CHICAGO$"
[DefaultInstall]
AddReg = Explore.AddReg
[Strings]
ACTION_TEXT = "Execute with Julia v1.7.2"
ACTION_CMD = "powershell -WindowStyle hidden -ExecutionPolicy Bypass -Command "C:\APPS\Julia-1.7.2\bin\launch_julia_script.ps1" '%1'"
[Explore.AddReg]
HKCR,SystemFileAssociations\\.jl_script\\shell\\JLScript-v1.7.2,,,%ACTION_TEXT%
HKCR,SystemFileAssociations\\.jl_script\\shell\\JLScript-v1.7.2\\command,,,%ACTION_CMD%
(It gets installed by double-clicking on this file if it has a .inf
extension!)
RE:
Thatâs because you shouldnât run include()
from within a function (much in the same way you shouldnât run import
/using
from within a function).
Doing so appears to run include()
in the calling scope⌠and so you start getting strange behaviours. I am almost certain running include()
from within a function is bad practice, and I would personally avoid it. include()
isnât exactly the same as #include
in C/C++.
A small tweak to your methodology
What you should instead be doing is something more like:
include("ReadTables.jl")
include("BuildTables.jl")
include("WriteTables.jl")
include("PlotTables.jl")
function process(options)
read_input_tables(options) # defined in ReadTables.jl
build_more_tables(options) # defined in BuildTables.jl
write_some_tables(options) # defined in WriteTables.jl
plot_important_tables(options) # defined in PlotTables.jl
return results #written somewhere in the module's global namespace, I suppose
end
This coding shift requires encapsulating much of the code you wrote in the global namespace (of *Tables.jl
files) inside these read_input_tables()
/build_more_tables()
/⌠functions. You can leave the code in their respective files - just wrap them inside one-or-more function call(s).
If you want to keep the data around inside the global namespace: just remember to declare/tag the variable as global:
function readtable(...) #This low-level function is fine as it is. No need to change it.
function read_input_tables(options) #New encapsulating function
#...
global tableY = readtable(options.inputfilepath, "Table Y-1")
#...
end
No scripting inside modules.
One of the consequences of what @mkitti mentioned:
is that you shouldnât write your *Tables.jl
files as conventional scripts with code running in the global namespace (ie: âglobalâ = not âinsideâ functions).
That only works for executing files in the REPL. As soon as you wrap files into modules, your non-static, âexecution-modeâ code should all be written inside one (or-more) functions.
I donât think this is explicitly stated anywhere (Iâm sort of sad to make this realization just now)⌠but it is a natural consequence of what @mkitti just said.
Only âstaticâ code that can effectively be pre-compiled is allowed to exist outside functions.
A more Julia-friendly solution
I have a few more relatively important suggestions for improving your code, but this particular issue seems to be your dominant problem - so Iâll refrain from giving you more confusing tips.
The JuilaScripting.jl sample I provided earlier shows a more Julian way to write your module (I modelled it closely after your ASME_Materials.jl
module to help you better understand the Matlab->Julia transition)
â take another look!: GitHub - ma-laforge/JuliaScripting.jl
I also tried to add useful comments to the global namespace & __init__()
functions so you can get a better idea of what should go where.
Thank you so much! I am happy to hear all your suggestions. Give me a few days to try to update my package with the suggestions already provided (maybe longer to figure out the jl_script
, .inf
, .ps1
stuff ). Then I will reach back out to you privately.