Merge branch 'master' into fix_check_if_bin_exists_and_executable_paths
This commit is contained in:
53
README.md
53
README.md
@@ -39,6 +39,7 @@ CollectHelper|Yes|LMW|Add buttons to selected images module to manipulate the co
|
||||
copy_attach_detach_tags|Yes|LMW|Copy and paste tags from/to images
|
||||
cr2hdr|Yes|L|Process image created with Magic Lantern Dual ISO
|
||||
enfuseAdvanced|No|LMW|Merge multiple images into Dynamic Range Increase (DRI) or Depth From Focus (DFF) images
|
||||
exportLUT|Yes|LMW|Create a LUT from a style and export it
|
||||
ext_editor|No|LW|Export pictures to collection and edit them with up to nine user-defined external editors
|
||||
face_recognition|No|LM|Identify and tag images using facial recognition
|
||||
fujifilm_ratings|No|LM|Support importing Fujifilm ratings
|
||||
@@ -46,14 +47,16 @@ geoJSON_export|No|L|Create a geo JSON script with thumbnails for use in ...
|
||||
geoToolbox|No|LMW|A toolbox of geo functions
|
||||
gimp|No|LMW|Open an image in GIMP for editing and return the result
|
||||
gpx_export|No|LMW|Export a GPX track file from selected images GPS data
|
||||
HDRMerge|No|LMW|Combind the selected images into an HDR DNG and return the result
|
||||
HDRMerge|No|LMW|Combine the selected images into an HDR DNG and return the result
|
||||
hugin|No|LMW|Combine selected images into a panorama and return the result
|
||||
image_stack|No|LMW|Combine a stack of images to remove noise or transient objects
|
||||
image_time|Yes|LMW|Adjust the EXIF image time
|
||||
kml_export|No|L|Export photos with a KML file for usage in Google Earth
|
||||
LabelsToTags|Yes|LMW|Apply tags based on color labels and ratings
|
||||
OpenInExplorer|No|LMW|Open the selected images in the system file manager
|
||||
passport_guide|Yes|LMW|Add passport cropping guide to darkroom crop tool
|
||||
pdf_slideshow|No|LM|Export images to a PDF slideshow
|
||||
[photils](https://github.com/scheckmedia/photils-dt)|No|LM|Automatic tag suggestions for your images
|
||||
quicktag|Yes|LMW|Create shortcuts for quickly applying tags
|
||||
rate_group|Yes|LMW|Apply or remove a star rating from grouped images
|
||||
rename-tags|Yes|LMW|Change a tag name
|
||||
@@ -116,14 +119,24 @@ are met as well as providing an easy update path. Single scripts listed as stand
|
||||
|
||||
### snap packages
|
||||
|
||||
The snap version of darktable comes with lua included starting with version 2.4.3snap2. It is currently in the edge channel, but should reach the stable channel soon.
|
||||
The snap version of darktable comes with lua included starting with version 2.4.3snap2.
|
||||
|
||||
Ensure git is installed on your system. If it isn't, use the package manager to install it. Then open a terminal and:
|
||||
|
||||
cd ~/snap/darktable/current
|
||||
git clone https://github.com/darktable-org/lua-scripts.git lua
|
||||
|
||||
### flatpak and appimage packages
|
||||
### flatpak packages
|
||||
|
||||
Flatpak packages now use the internal lua interpreter.
|
||||
|
||||
|
||||
Ensure git is installed on your system. If it isn't, use the package manager to install it. Then open a terminal and:
|
||||
|
||||
cd ~/.var/app/org.darktable.Darktable/config/darktable
|
||||
git clone https://github.com/darktable-org/lua-scripts.git lua
|
||||
|
||||
### appimage packages
|
||||
|
||||
These packages run in their own environment and don't have access to a lua interpreter, therefore the scripts can't run. The packagers could enable the internal interpreter, or allow the package to link the interpreter from the operating system, or bundle a copy of lua with the package. If you use one of these packages and wish to use the lua scripts, please contact the package maintainer and suggest the above fixes.
|
||||
|
||||
@@ -138,8 +151,6 @@ Ensure git is installed on your system. If it isn't, use the package manager to
|
||||
|
||||
Ensure git is installed on your system. Git can be obtained from https://gitforwindows.org/, as well as other places. If you use the gitforwindows.org distribution, install the Git Bash Shell also as it will aid in debugging the scripts if necessary. Then open a command prompt and run:
|
||||
|
||||
Open a command prompt.
|
||||
|
||||
cd %LOCALAPPDATA%\darktable
|
||||
git clone https://github.com/darktable-org/lua-scripts.git lua
|
||||
|
||||
@@ -149,6 +160,24 @@ If you don't have %LOCALAPPDATA%\darktable you have to start dartable at least o
|
||||
|
||||
When darktable starts it looks for a file name `~/.config/darktable/luarc` (`%LOCALAPPDATA%\darktable\luarc` for windows) and reads it to see which scripts to include. The file is a plain text file with entries of the form `require "<directory>/<name>"` where directory is the directory containing the scripts, from the above list, and name is the name from the above list. To include GIMP the line would be `require "contrib/gimp"`.
|
||||
|
||||
The recommended way to enable and disable specific scripts is using the script manager module. To use script manager do the following:
|
||||
|
||||
### Linux or MacOS
|
||||
|
||||
echo 'require "tools/script_manager"' > ~/.config/darktable/luarc
|
||||
|
||||
### Windows
|
||||
|
||||
echo "require 'tools/script_manager'" > %LOCALAPPDATA%\darktable\luarc
|
||||
|
||||
### Snap
|
||||
|
||||
echo 'require "tools/script_manager"' > ~/snap/darktable/current/luarc
|
||||
|
||||
### Flatpak
|
||||
|
||||
echo require "tools/script_manager"' > ~/.var/app/org.darktable.Darktable/config/darktable/luarc
|
||||
|
||||
You can also create or add lines to the luarc file from the command line:
|
||||
|
||||
`echo 'require "contrib/gimp"' > ~/.config/darktable/luarc` to create the file with a gimp entry\
|
||||
@@ -172,6 +201,12 @@ To update the script repository, open a terminal or command prompt and do the fo
|
||||
cd ~/snap/darktable/current/lua
|
||||
git pull
|
||||
|
||||
|
||||
### Flatpak
|
||||
|
||||
cd ~/.var/app/org.darktable.Darktable/config/darktable/lua
|
||||
git pull
|
||||
|
||||
### Linux and MacOS
|
||||
|
||||
cd ~/.config/darktable/lua/
|
||||
@@ -202,13 +237,17 @@ Running darktable with Lua debugging enabled provides more information about wha
|
||||
|
||||
Open a terminal and start darktable with the command `snap run darktable -d lua`. This provides debugging information to give you insight into what is happening.
|
||||
|
||||
### Linux and MacOS
|
||||
### Linux
|
||||
|
||||
Open a terminal and start darktable with the command `darktable -d lua`. This provides debugging information to give you insight into what is happening.
|
||||
|
||||
### MacOS
|
||||
|
||||
Open a terminal and start darktable with the command `/Applications/darktable.app/Contents/MacOS/darktable -d lua`. This provides debugging information to give you insight into what is happening.
|
||||
|
||||
### Windows
|
||||
|
||||
Open the Git Bash Shell. Start darktable with the command `/c/Program\ Files/darktable/bin/darktable -d lua`. This provides debugging information to give you insight into what is happening.
|
||||
Open a command prompt. Start darktable with the command "C:\Program Files\darktable\bin\darktable" -d lua > log.txt. This provides debugging information to give you insight into what is happening.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -45,6 +45,17 @@ local function _(msgid)
|
||||
return gettext.dgettext("AutoGrouper", msgid)
|
||||
end
|
||||
|
||||
local Ag = {}
|
||||
Ag.module_installed = false
|
||||
Ag.event_registered = false
|
||||
|
||||
local GUI = {
|
||||
gap = {},
|
||||
selected = {},
|
||||
collection = {}
|
||||
}
|
||||
|
||||
|
||||
local function InRange(test, low, high) --tests if test value is within range of low and high (inclusive)
|
||||
if test >= low and test <= high then
|
||||
return true
|
||||
@@ -111,12 +122,26 @@ local function main(on_collection)
|
||||
end
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not Ag.module_installed then
|
||||
dt.register_lib(
|
||||
'AutoGroup_Lib', -- Module name
|
||||
_('auto group'), -- name
|
||||
true, -- expandable
|
||||
true, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 99}}, -- containers
|
||||
dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
GUI.gap,
|
||||
GUI.selected,
|
||||
GUI.collection
|
||||
}
|
||||
)
|
||||
Ag.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- GUI --
|
||||
GUI = {
|
||||
gap = {},
|
||||
selected = {},
|
||||
collection = {}
|
||||
}
|
||||
temp = dt.preferences.read(MOD, 'active_gap', 'integer')
|
||||
if not InRange(temp, 1, 86400) then temp = 3 end
|
||||
GUI.gap = dt.new_widget('slider'){
|
||||
@@ -143,16 +168,20 @@ GUI.collection = dt.new_widget("button"){
|
||||
tooltip =_('auto group the entire collection'),
|
||||
clicked_callback = function() main(true) end
|
||||
}
|
||||
dt.register_lib(
|
||||
'AutoGroup_Lib', -- Module name
|
||||
_('auto group'), -- name
|
||||
true, -- expandable
|
||||
true, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 99}}, -- containers
|
||||
dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
GUI.gap,
|
||||
GUI.selected,
|
||||
GUI.collection
|
||||
}
|
||||
)
|
||||
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not Ag.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
Ag.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -94,6 +94,10 @@ local GUI = { --GUI Elements Table
|
||||
}
|
||||
}
|
||||
|
||||
HDRM.module_installed = false
|
||||
HDRM.event_registered = false
|
||||
|
||||
|
||||
--Detect User Styles--
|
||||
local styles = dt.styles
|
||||
local styles_count = 1 -- 'none' = 1
|
||||
@@ -264,6 +268,23 @@ local function main()
|
||||
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not HDRM.module_installed then
|
||||
dt.register_lib( -- register HDRMerge module
|
||||
'HDRMerge_Lib', -- Module name
|
||||
_('HDRMerge'), -- name
|
||||
true, -- expandable
|
||||
true, -- resetable
|
||||
{[dt.gui.views.lighttable] = {'DT_UI_CONTAINER_PANEL_RIGHT_CENTER', 99}}, -- containers
|
||||
dt.new_widget('box'){
|
||||
orientation = 'vertical',
|
||||
GUI.stack
|
||||
}
|
||||
)
|
||||
HDRM.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- GUI Elements --
|
||||
local lbl_hdr = dt.new_widget('section_label'){
|
||||
label = _('HDRMerge options')
|
||||
@@ -411,14 +432,18 @@ else
|
||||
GUI.stack.active = 2
|
||||
end
|
||||
|
||||
dt.register_lib( -- register HDRMerge module
|
||||
'HDRMerge_Lib', -- Module name
|
||||
_('HDRMerge'), -- name
|
||||
true, -- expandable
|
||||
true, -- resetable
|
||||
{[dt.gui.views.lighttable] = {'DT_UI_CONTAINER_PANEL_RIGHT_CENTER', 99}}, -- containers
|
||||
dt.new_widget('box'){
|
||||
orientation = 'vertical',
|
||||
GUI.stack
|
||||
}
|
||||
)
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not HDRM.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
HDRM.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -55,6 +55,10 @@ du.check_min_api_version("3.0.0", "LabelsToTags")
|
||||
-- Lua 5.3 no longer has "unpack" but "table.unpack"
|
||||
unpack = unpack or table.unpack
|
||||
|
||||
local ltt = {}
|
||||
ltt.module_installed = false
|
||||
ltt.event_registered = false
|
||||
|
||||
local LIB_ID = "LabelsToTags"
|
||||
|
||||
-- Helper functions: BEGIN
|
||||
@@ -184,7 +188,7 @@ local function doTagging(selfC)
|
||||
job.valid = false
|
||||
end
|
||||
|
||||
local my_widget = darktable.new_widget("box") {
|
||||
ltt.my_widget = darktable.new_widget("box") {
|
||||
orientation = "vertical",
|
||||
mappingComboBox,
|
||||
darktable.new_widget("button") {
|
||||
@@ -217,6 +221,15 @@ darktable.register_tag_mapping = function(name, mapping)
|
||||
mappingComboBox.reset_callback(mappingComboBox)
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not ltt.module_installed then
|
||||
darktable.register_lib(LIB_ID,"labels to tags",true,true,{
|
||||
[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",20},
|
||||
},ltt.my_widget,nil,nil)
|
||||
ltt.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
darktable.register_tag_mapping("Example",
|
||||
{ ["+----*"] = { "Red", "Only red" },
|
||||
@@ -229,6 +242,19 @@ darktable.register_tag_mapping("Example",
|
||||
["*****R"] = { "Rejected" } })
|
||||
]]
|
||||
|
||||
darktable.register_lib(LIB_ID,"labels to tags",true,true,{
|
||||
[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",20},
|
||||
},my_widget,nil,nil)
|
||||
if darktable.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not ltt.event_registered then
|
||||
darktable.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
ltt.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -32,12 +32,18 @@ Install: (see here for more detail: https://github.com/darktable-org/lua-scripts
|
||||
|
||||
Select the photo(s) you wish to find in your operating system's file manager and press "show in file explorer" in the "selected images" section.
|
||||
|
||||
- Nautilus (Linux), Explorer (Windows), and Finder (macOS before Catalina) will open one window for each selected image at the file's location. The file name will be highlighted.
|
||||
- Nautilus (Linux), Explorer (Windows), and Finder (macOS prior to Mojave) will open one window for each selected image at the file's location. The file name will be highlighted.
|
||||
|
||||
- On macOS Catalina the Finder will open one window for each different directory. In these windows only the last one of the corresponding files will be highlighted (bug or feature?).
|
||||
- On macOS Mojave and Catalina the Finder will open one window for each different directory. In these windows only the last one of the corresponding files will be highlighted (bug or feature?).
|
||||
|
||||
- Dolphin (Linux) will open one window with tabs for the different directories. All the selected images' file names are highlighted in their respective directories.
|
||||
|
||||
As an alternative option you can choose to show the image file names as symbolic links in an arbitrary directory. Go to preferences|Lua options. This option is not available for Windows users as on Windows solely admins are allowed to create links.
|
||||
|
||||
- Pros: You do not clutter up your display with multiple windows. So there is no need to limit the number of selections.
|
||||
|
||||
- Cons: If you want to work with the files you are one step behind the original data.
|
||||
|
||||
----KNOWN ISSUES----
|
||||
]]
|
||||
|
||||
@@ -50,6 +56,7 @@ local gettext = dt.gettext
|
||||
--Check API version
|
||||
du.check_min_api_version("5.0.0", "OpenInExplorer")
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("OpenInExplorer",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
@@ -59,53 +66,150 @@ end
|
||||
local act_os = dt.configuration.running_os
|
||||
local PS = act_os == "windows" and "\\" or "/"
|
||||
|
||||
--Detect OS and quit if it is not supported--
|
||||
local proper_install = true
|
||||
--Detect OS and quit if it is not supported.
|
||||
if act_os ~= "macos" and act_os ~= "windows" and act_os ~= "linux" then
|
||||
proper_install = false
|
||||
dt.print_error(_('OpenInExplorer plug-in only supports Linux, macOS, and Windows at this time'))
|
||||
dt.print(_("OpenInExplorer plug-in only supports Linux, macOS, and Windows at this time"))
|
||||
dt.print_error("OpenInExplorer plug-in only supports Linux, macOS, and Windows at this time")
|
||||
return
|
||||
end
|
||||
|
||||
--Format strings for the commands to open the corresponding OS' file manager
|
||||
local fmng_cmd = {}
|
||||
fmng_cmd.linux = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowItems ass %d %s""]]
|
||||
fmng_cmd.macos = 'open -Rn %s'
|
||||
fmng_cmd.windows = 'explorer.exe /select, %s'
|
||||
local use_links, links_dir = false, ""
|
||||
if act_os ~= "windows" then
|
||||
use_links = dt.preferences.read("OpenInExplorer", "use_links", "bool")
|
||||
links_dir = dt.preferences.read("OpenInExplorer", "linked_image_files_dir", "string")
|
||||
end
|
||||
|
||||
--The working function that opens the file manager windows with the selected image file names highlighted.
|
||||
local function open_in_fmanager(op_sys, fmcmd)
|
||||
local images = dt.gui.selection()
|
||||
local curr_image, run_cmd, file_uris = '', '', ''
|
||||
if #images == 0 then
|
||||
dt.print(_('Please select an image'))
|
||||
elseif #images <= 15 then
|
||||
for _,image in pairs(images) do
|
||||
curr_image = image.path..PS..image.filename
|
||||
if op_sys == 'linux' then
|
||||
file_uris = file_uris .. df.sanitize_filename("file://" .. curr_image) .. " "
|
||||
dt.print_log("file_uris is " .. file_uris)
|
||||
else
|
||||
run_cmd = string.format(fmcmd, df.sanitize_filename(curr_image))
|
||||
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
||||
dsys.external_command(run_cmd)
|
||||
end
|
||||
end
|
||||
if op_sys == 'linux' then
|
||||
run_cmd = string.format(fmcmd, #images, file_uris)
|
||||
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
||||
dsys.external_command(run_cmd)
|
||||
end
|
||||
else
|
||||
dt.print(_('Please select fewer images (max 15)'))
|
||||
--Check if the directory exists that was chosen for the file links. Return boolean.
|
||||
local function check_if_links_dir_exists()
|
||||
local dir_exists = true
|
||||
if not links_dir then
|
||||
--Just for paranoic reasons. I tried, but I couldn't devise a setting for a nil value.
|
||||
dt.print(_("No links directory selected.\nPlease check the dt preferences (lua options)"))
|
||||
dt.print_error("OpenInExplorer: No links directory selected")
|
||||
dir_exists = false
|
||||
elseif not df.check_if_file_exists(links_dir) then
|
||||
dt.print(string.format(_("Links directory '%s' not found.\nPlease check the dt preferences (lua options)"), links_dir))
|
||||
dt.print_error(string.format("OpenInExplorer: Links directory '%s' not found", links_dir))
|
||||
dir_exists = false
|
||||
end
|
||||
return dir_exists
|
||||
end
|
||||
|
||||
--Format strings for the commands to open the corresponding OS's file manager.
|
||||
local open_dir = {}
|
||||
open_dir.windows = "explorer.exe /n, %s"
|
||||
open_dir.macos = "open %s"
|
||||
open_dir.linux = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowFolders ass 1 %s ""]]
|
||||
|
||||
local open_files = {}
|
||||
open_files.windows = "explorer.exe /select, %s"
|
||||
open_files.macos = "open -Rn %s"
|
||||
open_files.linux = [[busctl --user call org.freedesktop.FileManager1 /org/freedesktop/FileManager1 org.freedesktop.FileManager1 ShowItems ass %d %s ""]]
|
||||
|
||||
--Call the file mangager for each selected image on Linux.
|
||||
--There is one call to busctl containing a list of all the image file names.
|
||||
local function call_list_of_files(selected_images)
|
||||
local current_image, file_uris, run_cmd = "", "", ""
|
||||
for _, image in pairs(selected_images) do
|
||||
current_image = image.path..PS..image.filename
|
||||
file_uris = file_uris .. df.sanitize_filename("file://" .. current_image) .. " "
|
||||
dt.print_log("file_uris is " .. file_uris)
|
||||
end
|
||||
run_cmd = string.format(open_files.linux, #selected_images, file_uris)
|
||||
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
||||
dsys.external_command(run_cmd)
|
||||
end
|
||||
|
||||
--Call the file manager for each selected image on Windows and macOS.
|
||||
local function call_file_by_file(selected_images)
|
||||
local current_image, run_cmd = "", ""
|
||||
for _, image in pairs(selected_images) do
|
||||
current_image = image.path..PS..image.filename
|
||||
run_cmd = string.format(open_files[act_os], df.sanitize_filename(current_image))
|
||||
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
||||
dsys.external_command(run_cmd)
|
||||
end
|
||||
end
|
||||
|
||||
--Create a link for each selected image, and finally call the file manager.
|
||||
local function set_links(selected_images)
|
||||
local current_image, link_target, run_cmd, k = "", "", "", nil
|
||||
for k, image in pairs(selected_images) do
|
||||
current_image = image.path..PS..image.filename
|
||||
link_target = df.create_unique_filename(links_dir .. PS .. image.filename)
|
||||
run_cmd = string.format("ln -s %s %s", df.sanitize_filename(current_image), df.sanitize_filename(link_target))
|
||||
--[[
|
||||
In case Windows will allow normal users to create soft links:
|
||||
if act_os == "windows" then
|
||||
run_cmd = string.format("mklink %s %s", df.sanitize_filename(link_target), df.sanitize_filename(current_image))
|
||||
end
|
||||
]]
|
||||
if dsys.external_command(run_cmd) ~= 0 then
|
||||
dt.print(_("Failed to create links. Missing rights?"))
|
||||
dt.print_error("OpenInExplorer: Failed to create links")
|
||||
return
|
||||
end
|
||||
end
|
||||
--The URI format is necessary only for the Linux busctl command.
|
||||
--But it is accepted by the Windows Explorer and macOS's Finder all the same.
|
||||
run_cmd = string.format(open_dir[act_os], df.sanitize_filename("file://"..links_dir))
|
||||
dt.print_log("OpenInExplorer run_cmd = "..run_cmd)
|
||||
dsys.external_command(run_cmd)
|
||||
end
|
||||
|
||||
--The working function that starts the particular task.
|
||||
local function open_in_fmanager()
|
||||
local images = dt.gui.selection()
|
||||
if #images == 0 then
|
||||
dt.print(_("Please select an image"))
|
||||
else
|
||||
if use_links and not check_if_links_dir_exists() then
|
||||
return
|
||||
end
|
||||
if #images > 15 and not use_links then
|
||||
dt.print(_("Please select fewer images (max. 15)"))
|
||||
elseif use_links then
|
||||
set_links(images)
|
||||
else
|
||||
if act_os == "linux" then
|
||||
call_list_of_files(images)
|
||||
else
|
||||
call_file_by_file(images)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- GUI --
|
||||
if proper_install then
|
||||
dt.gui.libs.image.register_action(
|
||||
_("show in file explorer"),
|
||||
function() open_in_fmanager(act_os, fmng_cmd[act_os]) end,
|
||||
_("Opens the file manager at the selected image's location")
|
||||
dt.gui.libs.image.register_action(
|
||||
_("show in file explorer"),
|
||||
function() open_in_fmanager() end,
|
||||
_("Open the file manager at the selected image's location")
|
||||
)
|
||||
if act_os ~= "windows" then
|
||||
dt.preferences.register("OpenInExplorer", "linked_image_files_dir", -- name
|
||||
"directory", -- type
|
||||
_("OpenInExplorer: linked files directory"), -- label
|
||||
_("Directory to store the links to the file names. Requires restart to take effect"), -- tooltip
|
||||
"Links to image files", -- default
|
||||
dt.new_widget("file_chooser_button"){
|
||||
title = _("Select directory"),
|
||||
is_directory = true,
|
||||
}
|
||||
)
|
||||
dt.preferences.register("OpenInExplorer", "use_links", -- name
|
||||
"bool", -- type
|
||||
_("OpenInExplorer: use links"), -- label
|
||||
_("Use links instead of multiple windows. Requires restart to take effect"), -- tooltip
|
||||
false, -- default
|
||||
""
|
||||
)
|
||||
end
|
||||
|
||||
dt.register_event(
|
||||
"shortcut",
|
||||
function(event, shortcut) open_in_fmanager() end,
|
||||
"OpenInExplorer"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,10 +1,29 @@
|
||||
--[[
|
||||
Richardson-Lucy output sharpening for darktable using GMic
|
||||
|
||||
darktable is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
darktable is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
|
||||
--[[
|
||||
DESCRIPTION
|
||||
RL_out_sharp.lua - Richardson-Lucy output sharpening using GMic
|
||||
|
||||
This script provides a new target storage "RL output sharpen".
|
||||
Images exported will be sharpened using GMic (RL deblur algorithm)
|
||||
|
||||
REQUIRED SOFTWARE
|
||||
GMic command line interface (CLI) https://gmic.eu/download.shtml
|
||||
|
||||
USAGE
|
||||
* require this script from main lua file
|
||||
@@ -33,7 +52,6 @@
|
||||
|
||||
CHANGES
|
||||
* 20200308 - initial version
|
||||
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
@@ -156,7 +174,7 @@ sigma_slider = dt.new_widget("slider"){
|
||||
soft_max = 2.0,
|
||||
hard_min = 0.0,
|
||||
hard_max = 3.0,
|
||||
step = 0.1,
|
||||
step = 0.05,
|
||||
digits = 2,
|
||||
value = 1.0
|
||||
}
|
||||
|
||||
@@ -51,6 +51,11 @@ local function _(msgid)
|
||||
return gettext.dgettext("copy_attach_detach_tags", msgid)
|
||||
end
|
||||
|
||||
local cadt = {}
|
||||
cadt.module_installed = false
|
||||
cadt.event_registered = false
|
||||
cadt.widget_table = {}
|
||||
|
||||
|
||||
local image_tags = {}
|
||||
|
||||
@@ -156,6 +161,26 @@ local function replace_tags()
|
||||
dt.print(_('Tags replaced'))
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not cadt.module_installed then
|
||||
dt.register_lib("tagging_addon","Tagging addon",true,true,{
|
||||
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",500}
|
||||
},
|
||||
dt.new_widget("box") {
|
||||
-- orientation = "vertical",
|
||||
reset_callback = function()
|
||||
taglist_label.label = ""
|
||||
image_tags = {}
|
||||
end,
|
||||
table.unpack(cadt.widget_table),
|
||||
},
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
cadt.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- create modul Tagging addons
|
||||
taglist_label.reset_callback = mcopy_tags
|
||||
|
||||
@@ -190,31 +215,31 @@ local box2 = dt.new_widget("box"){
|
||||
local sep = dt.new_widget("separator"){}
|
||||
|
||||
-- pack elements into widget table for a nicer layout
|
||||
local widget_table = {}
|
||||
|
||||
widget_table[1] = box1
|
||||
widget_table[#widget_table+1] = box2
|
||||
cadt.widget_table[1] = box1
|
||||
cadt.widget_table[#cadt.widget_table+1] = box2
|
||||
|
||||
widget_table[#widget_table+1] = sep
|
||||
widget_table[#widget_table+1] = taglabel
|
||||
widget_table[#widget_table+1] = taglist_label
|
||||
cadt.widget_table[#cadt.widget_table+1] = sep
|
||||
cadt.widget_table[#cadt.widget_table+1] = taglabel
|
||||
cadt.widget_table[#cadt.widget_table+1] = taglist_label
|
||||
|
||||
|
||||
-- create modul
|
||||
dt.register_lib("tagging_addon","Tagging addon",true,true,{
|
||||
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER",500}
|
||||
},
|
||||
dt.new_widget("box") {
|
||||
-- orientation = "vertical",
|
||||
reset_callback = function()
|
||||
taglist_label.label = ""
|
||||
image_tags = {}
|
||||
end,
|
||||
table.unpack(widget_table),
|
||||
},
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not cadt.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
cadt.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- shortcut for copy
|
||||
|
||||
@@ -180,6 +180,11 @@ for _,i in pairs(dt.styles) do
|
||||
end
|
||||
|
||||
-- FUNCTION --
|
||||
|
||||
local function sanitize_decimals(cmd) -- make sure decimal separator is a '.'
|
||||
return string.gsub(cmd, '(%d),(%d)', "%1.%2")
|
||||
end
|
||||
|
||||
local function InRange(test, low, high) -- tests if test value is within range of low and high (inclusive)
|
||||
if test >= low and test <= high then
|
||||
return true
|
||||
@@ -240,7 +245,7 @@ end
|
||||
|
||||
local function ExeUpdate(prog_tbl) --updates executable paths and verifies them
|
||||
dt.preferences.write(mod, 'bin_exists', 'bool', true)
|
||||
for _,prog in pairs(prog_tbl) do
|
||||
for x,prog in pairs(prog_tbl) do
|
||||
dt.preferences.write('executable_paths', prog.name, 'string', GUI.exes[prog.name].value)
|
||||
prog.bin = df.check_if_bin_exists(prog.name)
|
||||
if not prog.bin then
|
||||
@@ -311,7 +316,7 @@ local function UpdateENFargs(image_table, prefix) --updates the Enfuse arguments
|
||||
local largest_id = 0
|
||||
local first_raw = {}
|
||||
for raw, temp_image in pairs(image_table) do
|
||||
_, source_name, source_id = GetFileName(raw.filename)
|
||||
local _, source_name, source_id = GetFileName(raw.filename)
|
||||
source_id = tonumber(source_id)
|
||||
if source_id < smallest_id then
|
||||
smallest_id = source_id
|
||||
@@ -435,6 +440,9 @@ local function main(storage, image_table, extra_data)
|
||||
local job = dt.gui.create_job('aligning images')
|
||||
image_table, images_to_remove, AIS.images_string = UpdateAISargs(image_table, images_to_remove)
|
||||
local run_cmd = BuildExecuteCmd(AIS)
|
||||
dt.print_log("AIS run command is " .. run_cmd)
|
||||
run_cmd = sanitize_decimals(run_cmd)
|
||||
dt.print_log("AIS decimaal sanitized command is " .. run_cmd)
|
||||
local resp = dsys.external_command(run_cmd)
|
||||
job.valid = false
|
||||
if resp ~= 0 then
|
||||
@@ -455,11 +463,14 @@ local function main(storage, image_table, extra_data)
|
||||
local image_num = 0
|
||||
local job = dt.gui.create_job('blending '..#variants..' image(s)', true) --create a GUI job bar to display enfuse progress
|
||||
UpdateActivePreference() --load current GUI values into active preference (only applies to elements without a clicked/changed callback)
|
||||
for _,prefix in pairs(variants) do --for each image to be created load in the preference values, build arguments string, output image, and run command then execute.
|
||||
for x,prefix in pairs(variants) do --for each image to be created load in the preference values, build arguments string, output image, and run command then execute.
|
||||
job.percent = image_num /(#variants)
|
||||
image_num = image_num+1
|
||||
ENF.images_string, final_image, source_raw = UpdateENFargs(image_table, prefix)
|
||||
local run_cmd = BuildExecuteCmd(ENF)
|
||||
dt.print_log("ENF run command is " .. run_cmd)
|
||||
run_cmd = sanitize_decimals(run_cmd)
|
||||
dt.print_log("ENF decimaal sanitized command is " .. run_cmd)
|
||||
local resp = dsys.external_command(run_cmd)
|
||||
if resp ~= 0 then
|
||||
remove_temp_files(images_to_remove)
|
||||
@@ -470,11 +481,16 @@ local function main(storage, image_table, extra_data)
|
||||
|
||||
--copy exif data from original file
|
||||
run_cmd = EXF.bin..' -TagsFromFile '..df.sanitize_filename(source_raw.path..os_path_seperator..source_raw.filename)..' -exif:all --subifd:all -overwrite_original '..df.sanitize_filename(final_image)
|
||||
-- replace comma decimal separator with period
|
||||
dt.print_log("EXF run command is " .. run_cmd)
|
||||
run_cmd = sanitize_decimals(run_cmd)
|
||||
dt.print_log("EXF decimaal sanitized command is " .. run_cmd)
|
||||
resp = dsys.external_command(run_cmd)
|
||||
|
||||
|
||||
if GUI.Target.auto_import.value then --import image into dt if specified
|
||||
local imported = dt.database.import(final_image)
|
||||
dt.print_log("image imported")
|
||||
if GUI.Target.apply_style.selected > 1 then --apply specified style to imported image
|
||||
local set_style = styles[GUI.Target.apply_style.selected - 1]
|
||||
dt.styles.apply(set_style , imported)
|
||||
@@ -939,7 +955,7 @@ GUI.Presets.current_preset = dt.new_widget('combobox'){
|
||||
end
|
||||
}
|
||||
GUI.Presets.load = dt.new_widget('button'){
|
||||
label = _('laod fusion preset'),
|
||||
label = _('load fusion preset'),
|
||||
tooltip = _('load current fusion parameters from selected preset'),
|
||||
clicked_callback = function() LoadFromPreference(GUI.Presets.current_preset.value) end
|
||||
}
|
||||
|
||||
174
contrib/exportLUT.lua
Normal file
174
contrib/exportLUT.lua
Normal file
@@ -0,0 +1,174 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
copyright (c) 2020 Noah Clarke
|
||||
|
||||
darktable is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
darktable is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
Add the following line to .config/darktable/luarc to enable this lightable module:
|
||||
require "contrib/exportLut"
|
||||
|
||||
Given a haldCLUT identity file this script generates haldCLUTS from all the user's
|
||||
styles and exports them to a location of their choosing.
|
||||
|
||||
Warning: during export if a naming collision occurs the older file is automatically
|
||||
overwritten silently.
|
||||
]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
local df = require("lib/dtutils.file")
|
||||
local ds = require("lib/dtutils.system")
|
||||
|
||||
local gettext = dt.gettext
|
||||
|
||||
gettext.bindtextdomain("exportLUT",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("exportLUT", msgid)
|
||||
end
|
||||
|
||||
du.check_min_api_version("5.0.0", "exportLUT")
|
||||
|
||||
local eL = {}
|
||||
eL.module_installed = false
|
||||
eL.event_registered = false
|
||||
eL.widgets = {}
|
||||
|
||||
-- Thanks Kevin Ertel for this bit
|
||||
local os_path_seperator = '/'
|
||||
if dt.configuration.running_os == 'windows' then os_path_seperator = '\\' end
|
||||
local mkdir_command = 'mkdir -p '
|
||||
if dt.configuration.running_os == 'windows' then mkdir_command = 'mkdir ' end
|
||||
|
||||
local file_chooser_button = dt.new_widget("file_chooser_button"){
|
||||
title = _("Identity_file_chooser"),
|
||||
value = "",
|
||||
is_directory = false
|
||||
}
|
||||
|
||||
local export_chooser_button = dt.new_widget("file_chooser_button"){
|
||||
title = _("Export_location_chooser"),
|
||||
value = "",
|
||||
is_directory = true
|
||||
}
|
||||
|
||||
local identity_label = dt.new_widget("label"){
|
||||
label = _("choose the identity haldclut file")
|
||||
}
|
||||
|
||||
local output_label = dt.new_widget("label"){
|
||||
label = _("choose the output location")
|
||||
}
|
||||
|
||||
local warning_label = dt.new_widget("label"){
|
||||
label = _("WARNING: files may be silently overwritten")
|
||||
}
|
||||
|
||||
local function end_job(job)
|
||||
job.valid = false
|
||||
end
|
||||
|
||||
local function output_path(style_name, job)
|
||||
local output_location = export_chooser_button.value .. os_path_seperator .. style_name .. ".png"
|
||||
output_location = string.gsub(output_location, "|", os_path_seperator)
|
||||
local output_dir = string.reverse(output_location)
|
||||
output_dir = string.gsub(output_dir, ".-" .. os_path_seperator, os_path_seperator, 1)
|
||||
output_dir = string.reverse(output_dir)
|
||||
if(output_dir ~= "") then
|
||||
df.mkdir(df.sanitize_filename(output_dir))
|
||||
end
|
||||
return output_location
|
||||
end
|
||||
|
||||
local function export_luts()
|
||||
local identity = dt.database.import(file_chooser_button.value)
|
||||
if(type(identity) ~= "userdata") then
|
||||
dt.print(_("Invalid identity lut file"))
|
||||
else
|
||||
local job = dt.gui.create_job(_('Exporting styles as haldCLUTs'), true, end_job)
|
||||
|
||||
local size = 1
|
||||
|
||||
for style_num, style in ipairs(dt.styles) do
|
||||
size = size + 1
|
||||
end
|
||||
|
||||
local count = 0
|
||||
for style_num, style in ipairs(dt.styles) do
|
||||
|
||||
identity:reset()
|
||||
dt.styles.apply(style, identity)
|
||||
local io_lut = dt.new_format("png")
|
||||
io_lut.bpp = 8
|
||||
|
||||
io_lut:write_image(identity, output_path(style.name, job))
|
||||
count = count + 1
|
||||
job.percent = count / size
|
||||
dt.print(_("Exported: ") .. output_path(style.name, job))
|
||||
end
|
||||
dt.print(_("Done exporting haldCLUTs"))
|
||||
job.valid = false
|
||||
identity:reset()
|
||||
end
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not eL.module_installed then
|
||||
dt.register_lib(
|
||||
_("export haldclut"),
|
||||
_("export haldclut"),
|
||||
true,
|
||||
false,
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}},
|
||||
dt.new_widget("box")
|
||||
{
|
||||
orientation = "vertical",
|
||||
table.unpack(eL.widgets),
|
||||
},
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
eL.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
local export_button = dt.new_widget("button"){
|
||||
label = _("export"),
|
||||
clicked_callback = export_luts
|
||||
}
|
||||
|
||||
table.insert(eL.widgets, identity_label)
|
||||
table.insert(eL.widgets, file_chooser_button)
|
||||
table.insert(eL.widgets, output_label)
|
||||
table.insert(eL.widgets, export_chooser_button)
|
||||
table.insert(eL.widgets, warning_label)
|
||||
table.insert(eL.widgets, export_button)
|
||||
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not eL.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
eL.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,64 +1,69 @@
|
||||
--[[
|
||||
ext_editor.lua - edit images with external editors
|
||||
|
||||
DESCRIPTION
|
||||
darktable is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
darktable is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
|
||||
--[[
|
||||
ext_editor.lua - edit images with external editors
|
||||
|
||||
This script provides helpers to edit image files with programs external to darktable.
|
||||
It adds:
|
||||
- a new target storage "collection". Image exported will be reimported to collection for
|
||||
further edit with external programs
|
||||
- a new lighttable module "external editors", to select a program from a list of up to
|
||||
- 9 external editors and run it on a selected image (adjust this limit by changing MAX_EDITORS)
|
||||
- a set of lua preferences in order to configure name and path of up to 9 external editors
|
||||
- a set of lua shortcuts in order to quick launch the external editors
|
||||
This script provides helpers to edit image files with programs external to darktable. It adds:
|
||||
- a new target storage "collection". Image exported will be reimported to collection for further edit with external programs
|
||||
- a new lighttable module "external editors", to select a program from a list of up to
|
||||
- 9 external editors and run it on a selected image (adjust this limit by changing MAX_EDITORS)
|
||||
- a set of lua preferences in order to configure name and path of up to 9 external editors
|
||||
- a set of lua shortcuts in order to quick launch the external editors
|
||||
|
||||
USAGE
|
||||
* require this script from main lua file
|
||||
|
||||
-- setup --
|
||||
* in "preferences/lua options" configure name and path/command of external programs
|
||||
* note that if a program name is left empty, that and all following entries will be ignored
|
||||
* in "preferences/shortcuts/lua" configure shortcuts for external programs (optional)
|
||||
* whenever programs preferences are changed, in lighttable/external editors, press "update list"
|
||||
-- setup --
|
||||
* in "preferences/lua options" configure name and path/command of external programs
|
||||
* note that if a program name is left empty, that and all following entries will be ignored
|
||||
* in "preferences/shortcuts/lua" configure shortcuts for external programs (optional)
|
||||
* whenever programs preferences are changed, in lighttable/external editors, press "update list"
|
||||
|
||||
-- use --
|
||||
* in the export dialog choose "collection" and select the format and bit depth for the
|
||||
exported image
|
||||
* press "export"
|
||||
* the exported image will be imported into collection and grouped with the original image
|
||||
-- use --
|
||||
* in the export dialog choose "collection" and select the format and bit depth for the
|
||||
exported image
|
||||
* press "export"
|
||||
* the exported image will be imported into collection and grouped with the original image
|
||||
|
||||
* select an image for editing with en external program, and:
|
||||
* in lighttable/external editors, select program and press "edit"
|
||||
* edit the image with the external editor, overwite the file, quit the external program
|
||||
* the selected image will be updated
|
||||
or
|
||||
* in lighttable/external editors, select program and press "edit a copy"
|
||||
* edit the image with the external editor, overwite the file, quit the external program
|
||||
* a copy of the selected image will be created and updated
|
||||
or
|
||||
* in lighttable select target storage "collection"
|
||||
* enter in darkroom
|
||||
* to create an export or a copy press CRTL+E
|
||||
* use the shortcut to edit the current image with the corresponding external editor
|
||||
* overwite the file, quit the external program
|
||||
* the darkroom view will be updated
|
||||
|
||||
* select an image for editing with en external program, and:
|
||||
* in lighttable/external editors, select program and press "edit"
|
||||
* edit the image with the external editor, overwite the file, quit the external program
|
||||
* the selected image will be updated
|
||||
or
|
||||
* in lighttable/external editors, select program and press "edit a copy"
|
||||
* edit the image with the external editor, overwite the file, quit the external program
|
||||
* a copy of the selected image will be created and updated
|
||||
or
|
||||
* in lighttable select target storage "collection"
|
||||
* enter in darkroom
|
||||
* to create an export or a copy press CRTL+E
|
||||
* use the shortcut to edit the current image with the corresponding external editor
|
||||
* overwite the file, quit the external program
|
||||
* the darkroom view will be updated
|
||||
|
||||
* warning: mouseover on lighttable/filmstrip will prevail on current image
|
||||
* this is the default DT behavior, not a bug of this script
|
||||
* warning: mouseover on lighttable/filmstrip will prevail on current image
|
||||
* this is the default DT behavior, not a bug of this script
|
||||
|
||||
CAVEATS
|
||||
* MAC compatibility not tested
|
||||
CAVEATS
|
||||
* MAC compatibility not tested
|
||||
|
||||
TODO
|
||||
* send multiple images to the same program, maybe
|
||||
|
||||
BUGS, COMMENTS, SUGGESTIONS
|
||||
BUGS, COMMENTS, SUGGESTIONS
|
||||
* send to Marco Carrarini, marco.carrarini@gmail.com
|
||||
|
||||
CHANGES
|
||||
* 20191224 - initial version
|
||||
* 20191227 - added button "update list", better error handling, fixed bug with groups/tags in "edit"
|
||||
|
||||
]]
|
||||
|
||||
|
||||
@@ -73,12 +78,18 @@ local MODULE_NAME = "ext_editor"
|
||||
|
||||
|
||||
-- check API version
|
||||
du.check_min_api_version("5.0.2", MODULE_NAME) -- darktable 3.0
|
||||
du.check_min_api_version("5.0.2", MODULE_NAME) -- darktable 3.x
|
||||
|
||||
|
||||
-- OS compatibility
|
||||
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
||||
|
||||
-- namespace
|
||||
local ee = {}
|
||||
ee.module_installed = false
|
||||
ee.event_registered = false
|
||||
ee.widgets = {}
|
||||
|
||||
|
||||
-- translation
|
||||
local gettext = dt.gettext
|
||||
@@ -98,6 +109,7 @@ local n_entries
|
||||
local allowed_file_types = {"JPG", "jpg", "JPEG", "jpeg", "TIF", "tif", "TIFF", "tiff", "EXR", "exr", "PNG", "png"}
|
||||
|
||||
|
||||
-- last used editor initialization
|
||||
if not dt.preferences.read(MODULE_NAME, "initialized", "bool") then
|
||||
dt.preferences.write(MODULE_NAME, "lastchoice", "integer", 0)
|
||||
dt.preferences.write(MODULE_NAME, "initialized", "bool", true)
|
||||
@@ -163,11 +175,7 @@ local function OpenWith(images, choice, copy)
|
||||
local bin = program_paths[choice]
|
||||
local friendly_name = program_names[choice]
|
||||
|
||||
-- check if external program executable exists, return if not
|
||||
if not df.check_if_bin_exists(bin) then
|
||||
dt.print(friendly_name.._(" not found"))
|
||||
return
|
||||
end
|
||||
if dt.configuration.running_os == "macos" then bin = "open -W -a "..bin end
|
||||
|
||||
-- image to be edited
|
||||
local image
|
||||
@@ -175,7 +183,7 @@ local function OpenWith(images, choice, copy)
|
||||
local name = image.path..PS..image.filename
|
||||
|
||||
-- check if image format is allowed
|
||||
local file_ext = df.get_filetype (image.filename)
|
||||
local file_ext = df.get_filetype(image.filename)
|
||||
local allowed = false
|
||||
for i,v in pairs(allowed_file_types) do
|
||||
if v == file_ext then
|
||||
@@ -207,11 +215,7 @@ local function OpenWith(images, choice, copy)
|
||||
if copy then
|
||||
|
||||
-- create unique filename
|
||||
while df.check_if_file_exists(df.sanitize_filename(new_name)) do
|
||||
new_name = df.filename_increment(new_name)
|
||||
-- limit to 50 more exports of the original export
|
||||
if string.match(df.get_basename(new_name), "_%d%d$") == "_50" then break end
|
||||
end
|
||||
new_name = df.create_unique_filename(new_name)
|
||||
|
||||
-- physical copy, check result, return if error
|
||||
local copy_success = df.file_copy(name, new_name)
|
||||
@@ -306,7 +310,7 @@ local function program_shortcut(event, shortcut)
|
||||
-- export images and reimport in collection -----------------------------------
|
||||
local function export2collection(storage, image_table, extra_data)
|
||||
|
||||
local new_name, new_image, result
|
||||
local temp_name, new_name, new_image, move_success
|
||||
|
||||
for image, temp_name in pairs(image_table) do
|
||||
|
||||
@@ -314,11 +318,7 @@ local function export2collection(storage, image_table, extra_data)
|
||||
|
||||
-- create unique filename
|
||||
new_name = image.path..PS..df.get_filename(temp_name)
|
||||
while df.check_if_file_exists(df.sanitize_filename(new_name)) do
|
||||
new_name = df.filename_increment(new_name)
|
||||
-- limit to 50 more exports of the original export
|
||||
if string.match(df.get_basename(new_name), "_%d%d$") == "_50" then break end
|
||||
end
|
||||
new_name = df.create_unique_filename(new_name)
|
||||
|
||||
-- move image to collection folder, check result, return if error
|
||||
move_success = df.file_move(temp_name, new_name)
|
||||
@@ -335,12 +335,26 @@ local function export2collection(storage, image_table, extra_data)
|
||||
dt.print (_("finished exporting"))
|
||||
end
|
||||
|
||||
|
||||
-- register new storage -------------------------------------------------------
|
||||
-- note that placing this declaration later makes the export selected module
|
||||
-- not to remember the choice "collection" when restarting DT, don't know why
|
||||
dt.register_storage("exp2coll", _("collection"), null, export2collection)
|
||||
|
||||
-- install the module in the UI
|
||||
local function install_module()
|
||||
if not ee.module_installed then
|
||||
-- register new module "external editors" in lighttable ------------------------
|
||||
dt.register_lib(
|
||||
MODULE_NAME,
|
||||
_("external editors"),
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}},
|
||||
dt.new_widget("box") {
|
||||
orientation = "vertical",
|
||||
table.unpack(ee.widgets),
|
||||
},
|
||||
nil, -- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
ee.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- combobox, with variable number of entries ----------------------------------
|
||||
local combobox = dt.new_widget("combobox") {
|
||||
@@ -393,33 +407,42 @@ local box1 = dt.new_widget("box") {
|
||||
button_update_list
|
||||
}
|
||||
|
||||
table.insert(ee.widgets, combobox)
|
||||
table.insert(ee.widgets, box1)
|
||||
|
||||
-- register new module "external editors" in lighttable ------------------------
|
||||
dt.register_lib(
|
||||
MODULE_NAME,
|
||||
_("external editors"),
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}},
|
||||
dt.new_widget("box") {
|
||||
orientation = "vertical",
|
||||
combobox,
|
||||
box1
|
||||
},
|
||||
nil, -- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not ee.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
ee.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- initialize list of programs and widgets ------------------------------------
|
||||
UpdateProgramList(combobox, button_edit, button_edit_copy, false)
|
||||
|
||||
|
||||
-- register new storage -------------------------------------------------------
|
||||
dt.register_storage("exp2coll", _("collection"), nil, export2collection)
|
||||
|
||||
|
||||
-- register the new preferences -----------------------------------------------
|
||||
for i = MAX_EDITORS, 1, -1 do
|
||||
dt.preferences.register(MODULE_NAME, "program_path_"..i, "file",
|
||||
_("executable for external editor ")..i,
|
||||
_("select executable for external editor") , _("(None)"))
|
||||
|
||||
dt.preferences.register(MODULE_NAME, "program_name_"..i, "string",
|
||||
_("name of external editor ")..i,
|
||||
_("friendly name of external editor"), "")
|
||||
|
||||
@@ -55,6 +55,8 @@ local OUTPUT = dt.configuration.tmp_dir .. PS .. "facerecognition.txt"
|
||||
-- namespace
|
||||
|
||||
local fc = {}
|
||||
fc.module_installed = false
|
||||
fc.event_registered = false
|
||||
|
||||
-- ensure we meet the minimum api
|
||||
du.check_min_api_version("5.0.0", "face_recognition")
|
||||
@@ -337,6 +339,22 @@ local function face_recognition ()
|
||||
end
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not fc.module_installed then
|
||||
dt.register_lib(
|
||||
"face_recognition", -- Module name
|
||||
_("face recognition"), -- Visible name
|
||||
true, -- expandable
|
||||
true, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 300}}, -- containers
|
||||
fc.widget,
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
fc.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- build the interface
|
||||
|
||||
fc.unknown_tag = dt.new_widget("entry"){
|
||||
@@ -466,16 +484,21 @@ fc.widget = dt.new_widget("box"){
|
||||
table.unpack(widgets),
|
||||
}
|
||||
|
||||
dt.register_lib(
|
||||
"face_recognition", -- Module name
|
||||
_("face recognition"), -- Visible name
|
||||
true, -- expandable
|
||||
true, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 300}}, -- containers
|
||||
fc.widget,
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not fc.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
fc.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
fc.tolerance.value = dt.preferences.read(MODULE, "tolerance", "float")
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ local function detect_rating(event, image)
|
||||
dt.print_error(_("exiftool not found"))
|
||||
return
|
||||
end
|
||||
local RAF_filename = tostring(image)
|
||||
local RAF_filename = df.sanitize_filename(tostring(image))
|
||||
local JPEG_filename = string.gsub(RAF_filename, "%.RAF$", ".JPG")
|
||||
local command = "exiftool -Rating " .. JPEG_filename
|
||||
dt.print_error(command)
|
||||
|
||||
@@ -35,7 +35,6 @@ USAGE
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
du.check_min_api_version("3.0.0", "geoJSON_export")
|
||||
|
||||
@@ -28,7 +28,6 @@ require "geoToolbox"
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
du.check_min_api_version("3.0.0", "geoToolbox")
|
||||
@@ -40,6 +39,12 @@ local function _(msgid)
|
||||
return gettext.dgettext("geoToolbox", msgid)
|
||||
end
|
||||
|
||||
|
||||
local gT = {}
|
||||
gT.module_installed = false
|
||||
gT.event_registered = false
|
||||
|
||||
|
||||
-- <GUI>
|
||||
local labelDistance = dt.new_widget("label")
|
||||
labelDistance.label = _("Distance:")
|
||||
@@ -572,6 +577,22 @@ local function altitude_profile()
|
||||
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not gT.module_installed then
|
||||
dt.register_lib(
|
||||
"geoToolbox", -- Module name
|
||||
"geo toolbox", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
gT.widget,
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
gT.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local separator = dt.new_widget("separator"){}
|
||||
local separator2 = dt.new_widget("separator"){}
|
||||
@@ -579,13 +600,7 @@ local separator3 = dt.new_widget("separator"){}
|
||||
local separator4 = dt.new_widget("separator"){}
|
||||
local separator5 = dt.new_widget("separator"){}
|
||||
|
||||
dt.register_lib(
|
||||
"geoToolbox", -- Module name
|
||||
"geo toolbox", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
dt.new_widget("box")
|
||||
gT.widget = dt.new_widget("box")
|
||||
{
|
||||
orientation = "vertical",
|
||||
dt.new_widget("button")
|
||||
@@ -664,10 +679,24 @@ dt.register_lib(
|
||||
clicked_callback = altitude_profile
|
||||
},
|
||||
labelDistance
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not gT.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
gT.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Preferences
|
||||
dt.preferences.register("geoToolbox",
|
||||
|
||||
@@ -36,6 +36,10 @@ local function _(msgid)
|
||||
return gettext.dgettext("gpx_export", msgid)
|
||||
end
|
||||
|
||||
local gpx = {}
|
||||
gpx.module_installed = false
|
||||
gpx.event_registered = false
|
||||
|
||||
local path_entry = dt.new_widget("entry")
|
||||
{
|
||||
text = dt.preferences.read("gpx_exporter", "gpxExportPath", "string"),
|
||||
@@ -127,31 +131,55 @@ local function create_gpx_file()
|
||||
end
|
||||
end
|
||||
|
||||
dt.register_lib(
|
||||
"gpx_exporter",
|
||||
"gpx export",
|
||||
true, -- expandable
|
||||
true, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
local function install_module()
|
||||
if not gpx.module_installed then
|
||||
dt.register_lib(
|
||||
"gpx_exporter",
|
||||
"gpx export",
|
||||
true, -- expandable
|
||||
true, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
gpx.widget,
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
gpx.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
gpx.widget = dt.new_widget("box")
|
||||
{
|
||||
orientation = "vertical",
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = _("export"),
|
||||
tooltip = _("export gpx file"),
|
||||
clicked_callback = create_gpx_file
|
||||
},
|
||||
dt.new_widget("box")
|
||||
{
|
||||
orientation = "vertical",
|
||||
dt.new_widget("button")
|
||||
orientation = "horizontal",
|
||||
dt.new_widget("label")
|
||||
{
|
||||
label = _("export"),
|
||||
tooltip = _("export gpx file"),
|
||||
clicked_callback = create_gpx_file
|
||||
},
|
||||
dt.new_widget("box")
|
||||
{
|
||||
orientation = "horizontal",
|
||||
dt.new_widget("label")
|
||||
{
|
||||
label = _("file:"),
|
||||
},
|
||||
path_entry
|
||||
label = _("file:"),
|
||||
},
|
||||
path_entry
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not gpx.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
gpx.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,7 +40,6 @@ local du = require "lib/dtutils"
|
||||
local df = require "lib/dtutils.file"
|
||||
local log = require "lib/dtutils.log"
|
||||
local dtsys = require "lib/dtutils.system"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
local namespace = 'module_hugin'
|
||||
@@ -227,4 +226,4 @@ dt.register_storage(namespace, _("hugin panorama"), show_status, create_panorama
|
||||
|
||||
|
||||
--
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
|
||||
564
contrib/image_time.lua
Normal file
564
contrib/image_time.lua
Normal file
@@ -0,0 +1,564 @@
|
||||
--[[
|
||||
|
||||
image_time.lua - synchronize image time for images shot with different cameras
|
||||
|
||||
Copyright (C) 2019, 2020 Bill Ferguson <wpferguson@gmail.com>.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
image_time - non-destructively modify the image time
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
image_time non destructively adjusts image times by modifying the
|
||||
database image exif_datetime_taken field. There are 4 modes: adjust time,
|
||||
set time, synchronize time, and reset time.
|
||||
|
||||
ADJUST TIME
|
||||
|
||||
adjust time mode lets you chose an offset in terms of years, months,
|
||||
days, hours, minutes, and seconds. The adjustment can be added or
|
||||
subtracted.
|
||||
|
||||
WARNING: When adding and subtracting months the result will usually
|
||||
be what is expected unless the time being adjusted is at the end of
|
||||
the month. This is because a month is a variable amount of time that
|
||||
can be 28, 29, 30 or 31 days depending on the month. Example: It's
|
||||
March 31st and I subtract a month which not sets the time to February
|
||||
31st. When that gets set to a valid time, then the date changes to
|
||||
March 3rd.
|
||||
|
||||
SET TIME
|
||||
|
||||
set time mode allows you to pick a date and time and set the image
|
||||
time accordingly. Fields may be left out. This is useful when
|
||||
importing scanned images that don't have an embedded date.
|
||||
|
||||
SYNCHRONIZE TIME
|
||||
|
||||
I recently purchased a 7DmkII to replace my aging 7D. My 7D was still
|
||||
serviceable, so I bought a remote control and figured I'd try shooting
|
||||
events from 2 different perspectives. I didn't think to synchonize the
|
||||
time between the 2 cameras, so when I loaded the images and sorted by
|
||||
time it was a disaster. I hacked a script together with hard coded values
|
||||
to adjust the exif_datetime_taken value in the database for the 7D images
|
||||
so that everything sorted properly. I've tried shooting with 2 cameras
|
||||
several times since that first attempt. I've gotten better at getting the
|
||||
camera times close, but still haven't managed to get them to sync. So I
|
||||
decided to think the problem through and write a proper script to take
|
||||
care of the problem.
|
||||
|
||||
RESET TIME
|
||||
|
||||
Select the images and click reset.
|
||||
|
||||
USAGE
|
||||
|
||||
ADJUST TIME
|
||||
|
||||
Change the year, month, day, hour, minute, second dropdowns to the amount
|
||||
of change desired. Select add or subtract. Select the images. Click
|
||||
adjust.
|
||||
|
||||
SET TIME
|
||||
|
||||
Set the time fields to the desired time. Select the images to change. Click
|
||||
set.
|
||||
|
||||
SYNCHRONIZE TIME
|
||||
|
||||
Select 2 images, one from each camera, of the same moment in time. Click
|
||||
the Calculate button to calculate the time difference. The difference is
|
||||
displayed in the difference entry. You can manually adjust it by changing
|
||||
the value if necessary.
|
||||
|
||||
Select the images that need their time adjusted. Determine which way to adjust
|
||||
adjust the time (add or subtract) and select the appropriate choice.
|
||||
|
||||
If the image times get messed up and you just want to start over, select reset time
|
||||
from the mode and reset the image times.
|
||||
|
||||
RESET TIME
|
||||
|
||||
Select the images and click reset.
|
||||
|
||||
ADDITIONAL SOFTWARE REQUIRED
|
||||
* exiv2
|
||||
|
||||
BUGS, COMMENTS, SUGGESTIONS
|
||||
* Send to Bill Ferguson, wpferguson@gmail.com
|
||||
|
||||
CHANGES
|
||||
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
local df = require "lib/dtutils.file"
|
||||
local ds = require "lib/dtutils.string"
|
||||
local gettext = dt.gettext
|
||||
|
||||
local img_time = {}
|
||||
img_time.module_installed = false
|
||||
img_time.event_registered = false
|
||||
|
||||
du.check_min_api_version("3.0.0", "image_time")
|
||||
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("image_time",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("image_time", msgid)
|
||||
end
|
||||
|
||||
local PS = dt.configuration.runnin_os == "windows" and "\\" or "/"
|
||||
local ERROR = -1
|
||||
|
||||
-- function to convert from exif time to system time
|
||||
local function exiftime2systime(exiftime)
|
||||
local yr,mo,dy,h,m,s = string.match(exiftime, "(%d-):(%d-):(%d-) (%d-):(%d-):(%d+)")
|
||||
return(os.time{year=yr, month=mo, day=dy, hour=h, min=m, sec=s})
|
||||
end
|
||||
|
||||
-- function to convert from systime to exif time
|
||||
local function systime2exiftime(systime)
|
||||
local t = os.date("*t", systime)
|
||||
return(string.format("%4d:%02d:%02d %02d:%02d:%02d", t.year, t.month, t.day, t.hour, t.min, t.sec))
|
||||
end
|
||||
|
||||
local function vars2exiftime(year, month, day, hour, min, sec)
|
||||
local y = tonumber(year) and string.format("%4d", year) or " "
|
||||
local mo = tonumber(month) and string.format("%02d", month) or " "
|
||||
local d = tonumber(day) and string.format("%02d", day) or " "
|
||||
local h = tonumber(hour) and string.format("%02d", hour) or " "
|
||||
local m = tonumber(min) and string.format("%02d", min) or " "
|
||||
local s = tonumber(sec) and string.format("%02d", sec) or " "
|
||||
return(y .. ":" .. mo .. ":" .. d .. " " .. h .. ":" .. m .. ":" .. s)
|
||||
end
|
||||
|
||||
local function exiftime2vars(exiftime)
|
||||
return string.match(exiftime, "(%d+):(%d+):(%d+) (%d+):(%d+):(%d+)")
|
||||
end
|
||||
|
||||
local function calc_time_difference(image1, image2)
|
||||
return math.abs(exiftime2systime(image1.exif_datetime_taken) - exiftime2systime(image2.exif_datetime_taken))
|
||||
end
|
||||
|
||||
local function adjust_image_time(image, difference)
|
||||
image.exif_datetime_taken = systime2exiftime(exiftime2systime(image.exif_datetime_taken) + difference)
|
||||
return
|
||||
end
|
||||
|
||||
local function calculate_difference(images)
|
||||
if #images == 2 then
|
||||
img_time.diff_entry.text = calc_time_difference(images[1], images[2])
|
||||
img_time.btn.sensitive = true
|
||||
else
|
||||
dt.print(_("Error: 2 images must be selected"))
|
||||
end
|
||||
end
|
||||
|
||||
local function synchronize_times(images, difference)
|
||||
for _, image in ipairs(images) do
|
||||
adjust_image_time(image, difference)
|
||||
end
|
||||
end
|
||||
|
||||
local function synchronize_time(images)
|
||||
local sign = 1
|
||||
if img_time.sdir.value == "subtract" then
|
||||
sign = -1
|
||||
end
|
||||
synchronize_times(images, tonumber(img_time.diff_entry.text) * sign)
|
||||
end
|
||||
|
||||
local function add_time(images)
|
||||
synchronize_times(images, tonumber(img_time.diff_entry.text))
|
||||
end
|
||||
|
||||
local function year_month2months(year, month)
|
||||
year_months = tonumber(year) and year * 12 or 0
|
||||
local months = tonumber(month) and tonumber(month) or 0
|
||||
|
||||
return year_months + months
|
||||
end
|
||||
|
||||
local function months2year_month(months)
|
||||
dt.print_log("months is " .. months)
|
||||
local year = math.floor(months / 12)
|
||||
local month = months - (year * 12)
|
||||
|
||||
return year, month
|
||||
end
|
||||
|
||||
local function get_image_taken_time(image)
|
||||
-- get original image time
|
||||
local datetime = nil
|
||||
|
||||
local exiv2 = df.check_if_bin_exists("exiv2")
|
||||
if exiv2 then
|
||||
p = io.popen(exiv2 .. " -K Exif.Image.DateTime " .. image.path .. PS .. image.filename)
|
||||
if p then
|
||||
for line in p:lines() do
|
||||
if string.match(line, "Exif.Image.DateTime") then
|
||||
datetime = string.match(line, "(%d-:%d-:%d- %d-:%d-:%d+)")
|
||||
end
|
||||
end
|
||||
p:close()
|
||||
end
|
||||
else
|
||||
dt.print(_("unable to detect exiv2"))
|
||||
datetime = ERROR
|
||||
end
|
||||
return datetime
|
||||
end
|
||||
|
||||
local function _get_windows_image_file_creation_time(image)
|
||||
local datetime = nil
|
||||
local p = io.popen("dir " .. image.path .. PS .. image.filename)
|
||||
if p then
|
||||
for line in p:lines() do
|
||||
if string.match(line, ds.sanitize_lua(image.filename)) then
|
||||
local mo, day, yr, hr, min, apm = string.match(line, "(%d+)/(%d+)/(%d+) (%d-):(%d+) (%S+)")
|
||||
if apm == "PM" then
|
||||
hr = hr + 12
|
||||
end
|
||||
datetime = vars2exiftime(yr, mo, day, hr, min, 0)
|
||||
end
|
||||
end
|
||||
p:close()
|
||||
else
|
||||
dt.print(_("unable to get information for ") .. image.filename)
|
||||
datetime = ERROR
|
||||
end
|
||||
return datetime
|
||||
end
|
||||
|
||||
local function _get_nix_image_file_creation_time(image)
|
||||
local datetime = nil
|
||||
local p = io.popen("ls -lL --time-style=full-iso " .. image.path .. PS .. image.filename)
|
||||
if p then
|
||||
for line in p:lines() do
|
||||
if string.match(line, ds.sanitize_lua(image.filename)) then
|
||||
datetime = vars2exiftime(string.match(line, "(%d+)%-(%d-)%-(%d-) (%d-):(%d-):(%d+)."))
|
||||
end
|
||||
end
|
||||
p:close()
|
||||
else
|
||||
dt.print(_("unable to get information for ") .. image.filename)
|
||||
datetime = ERROR
|
||||
end
|
||||
return datetime
|
||||
end
|
||||
|
||||
local function get_image_file_creation_time(image)
|
||||
-- no exif time in the image file so get the creation time
|
||||
local datetime = nil
|
||||
if dt.configuration.running_os == "windows" then
|
||||
datetime = _get_windows_image_file_creation_time(image)
|
||||
else
|
||||
datetime = _get_nix_image_file_creation_time(image)
|
||||
end
|
||||
return datetime
|
||||
end
|
||||
|
||||
local function get_original_image_time(image)
|
||||
local image_time = image.exif_datetime_taken
|
||||
local reset_time = nil
|
||||
|
||||
reset_time = get_image_taken_time(image)
|
||||
|
||||
if reset_time then
|
||||
if reset_time == ERROR then
|
||||
return image_time
|
||||
else
|
||||
return reset_time
|
||||
end
|
||||
else
|
||||
reset_time = get_image_file_creation_time(image)
|
||||
|
||||
if reset_time then
|
||||
if reset_time == ERROR then
|
||||
return image_time
|
||||
else
|
||||
return reset_time
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function reset_time(images)
|
||||
if #images > 0 then
|
||||
for _, image in ipairs(images) do
|
||||
image.exif_datetime_taken = get_original_image_time(image)
|
||||
end
|
||||
else
|
||||
dt.print_error(_("reset time: no images selected"))
|
||||
dt.print(_("please select the images that need their time reset"))
|
||||
end
|
||||
end
|
||||
|
||||
local function adjust_time(images)
|
||||
local SEC_MIN = 60
|
||||
local SEC_HR = SEC_MIN * 60
|
||||
local SEC_DY = SEC_HR * 24
|
||||
|
||||
local offset = nil
|
||||
local sign = 1
|
||||
|
||||
if #images < 1 then
|
||||
dt.print(_("please select some images and try again"))
|
||||
return
|
||||
end
|
||||
|
||||
if img_time.adir.value == _("subtract") then
|
||||
sign = -1
|
||||
end
|
||||
|
||||
for _, image in ipairs(images) do
|
||||
local y, mo, d, h, m, s = exiftime2vars(image.exif_datetime_taken)
|
||||
local image_months = year_month2months(y, mo)
|
||||
local months_diff = year_month2months(img_time.ayr.value, img_time.amo.value)
|
||||
y, mo = months2year_month(image_months + (months_diff * sign))
|
||||
local exif_new = vars2exiftime(y, mo, d, h, m, s)
|
||||
offset = img_time.ady.value * SEC_DY
|
||||
offset = offset + img_time.ahr.value * SEC_HR
|
||||
offset = offset + img_time.amn.value * SEC_MIN
|
||||
offset = offset + img_time.asc.value
|
||||
offset = offset * sign
|
||||
image.exif_datetime_taken = systime2exiftime(exiftime2systime(exif_new) + offset)
|
||||
end
|
||||
end
|
||||
|
||||
local function set_time(images)
|
||||
if #images < 1 then
|
||||
dt.print(_("please select some images and try again"))
|
||||
return
|
||||
end
|
||||
|
||||
local y = img_time.syr.value
|
||||
local mo = img_time.smo.value
|
||||
local d = img_time.sdy.value
|
||||
local h = img_time.shr.value
|
||||
local m = img_time.smn.value
|
||||
local s = img_time.ssc.value
|
||||
|
||||
for _, image in ipairs(images) do
|
||||
image.exif_datetime_taken = vars2exiftime(y, mo, d, h, m, s)
|
||||
end
|
||||
end
|
||||
|
||||
local function seq(first, last)
|
||||
local result = {}
|
||||
|
||||
local num = first
|
||||
|
||||
while num <= last do
|
||||
table.insert(result, num)
|
||||
num = num + 1
|
||||
end
|
||||
|
||||
return table.unpack(result)
|
||||
end
|
||||
|
||||
local function reset_widgets()
|
||||
dt.print_log("took the reset function")
|
||||
img_time.ayr.selected = 1
|
||||
img_time.amo.selected = 1
|
||||
img_time.ady.selected = 1
|
||||
img_time.ahr.selected = 1
|
||||
img_time.ayr.selected = 1
|
||||
img_time.amn.selected = 1
|
||||
img_time.asc.selected = 1
|
||||
img_time.adir.selected = 1
|
||||
img_time.syr.selected = #img_time.syr
|
||||
img_time.smo.selected = 1
|
||||
img_time.sdy.selected = 1
|
||||
img_time.shr.selected = 1
|
||||
img_time.smn.selected = 1
|
||||
img_time.ssc.selected = 1
|
||||
img_time.adir.selected = 1
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not img_time.module_installed then
|
||||
dt.register_lib(
|
||||
"image_time", -- Module name
|
||||
_("image time"), -- Visible name
|
||||
true, -- expandable
|
||||
true, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
img_time.widget,
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
img_time.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- widgets
|
||||
|
||||
img_time.widgets = {
|
||||
-- name, type, tooltip, placeholder,
|
||||
{"ayr", "combobox", _("years"), _("years to adjust by, 0 - ?"), {seq(0,20)}, 1},
|
||||
{"amo", "combobox", _("months"), ("months to adjust by, 0-12"), {seq(0,12)}, 1},
|
||||
{"ady", "combobox", _("days"), ("days to adjust by, 0-31"), {seq(0,31)}, 1},
|
||||
{"ahr", "combobox", _("hours"), _("hours to adjust by, 0-23"), {seq(0,23)}, 1},
|
||||
{"amn", "combobox", _("minutes"), _("minutes to adjust by, 0-59"), {seq(0,59)}, 1},
|
||||
{"asc", "combobox", _("seconds"), _("seconds to adjust by, 0-59"), {seq(0,59)}, 1},
|
||||
{"adir", "combobox", _("add/subtract"), _("add or subtract time"), {_("add"), _("subtract")}, 1},
|
||||
{"syr", "combobox", _("year"), _("year to set, 1900 - now"), {" ", seq(1900,os.date("*t", os.time()).year)}, 1},
|
||||
{"smo", "combobox", _("month"), _("month to set, 1-12"), {" ", seq(1,12)}, 1},
|
||||
{"sdy", "combobox", _("day"), _("day to set, 1-31"), {" ", seq(1,31)}, 1},
|
||||
{"shr", "combobox", _("hour"), _("hour to set, 0-23"), {" ", seq(0,23)}, 1},
|
||||
{"smn", "combobox", _("minute"), _("minutes to set, 0-59"), {" ", seq(0, 59)}, 1},
|
||||
{"ssc", "combobox", _("seconds"), _("seconds to set, 0-59"), {" ", seq(0,59)}, 1},
|
||||
{"sdir", "combobox", _("add/subtract"), _("add or subtract time"), {_("add"), _("subtract")}, 1},
|
||||
}
|
||||
|
||||
for _, widget in ipairs(img_time.widgets) do
|
||||
img_time[widget[1]] = dt.new_widget(widget[2]){
|
||||
label = widget[3],
|
||||
tooltip = widget[4],
|
||||
selected = widget[6],
|
||||
table.unpack(widget[5])
|
||||
}
|
||||
end
|
||||
|
||||
img_time.syr.selected = #img_time.syr
|
||||
|
||||
img_time.diff_entry = dt.new_widget("entry"){
|
||||
tooltip = _("Time difference between images in seconds"),
|
||||
placeholder = _("Select 2 images and use the calculate button"),
|
||||
text = "",
|
||||
}
|
||||
|
||||
img_time.calc_btn = dt.new_widget("button"){
|
||||
label = _("Calculate"),
|
||||
tooltip = _("calculate time difference between 2 images"),
|
||||
clicked_callback = function()
|
||||
calculate_difference(dt.gui.action_images)
|
||||
end
|
||||
}
|
||||
|
||||
img_time.btn = dt.new_widget("button"){
|
||||
label = _("synchronize image times"),
|
||||
tooltip = _("apply the time difference from selected images"),
|
||||
sensitive = false,
|
||||
clicked_callback = function()
|
||||
synchronize_time(dt.gui.action_images)
|
||||
end
|
||||
}
|
||||
|
||||
img_time.stack = dt.new_widget("stack"){
|
||||
dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
dt.new_widget("label"){label = _("adjust time")},
|
||||
dt.new_widget("section_label"){label = _("days, months, years")},
|
||||
img_time.ady,
|
||||
img_time.amo,
|
||||
img_time.ayr,
|
||||
dt.new_widget("section_label"){label = _("hours, minutes, seconds")},
|
||||
img_time.ahr,
|
||||
img_time.amn,
|
||||
img_time.asc,
|
||||
dt.new_widget("section_label"){label = _("adjustment direction")},
|
||||
img_time.adir,
|
||||
dt.new_widget("button"){
|
||||
label = _("adjust"),
|
||||
clicked_callback = function()
|
||||
adjust_time(dt.gui.action_images)
|
||||
end
|
||||
}
|
||||
},
|
||||
dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
dt.new_widget("label"){label = _("set time")},
|
||||
dt.new_widget("section_label"){label = _("date: ")},
|
||||
img_time.sdy,
|
||||
img_time.smo,
|
||||
img_time.syr,
|
||||
dt.new_widget("section_label"){label = _("time:")},
|
||||
img_time.shr,
|
||||
img_time.smn,
|
||||
img_time.ssc,
|
||||
dt.new_widget("button"){
|
||||
label = _("set"),
|
||||
clicked_callback = function()
|
||||
set_time(dt.gui.action_images)
|
||||
end
|
||||
}
|
||||
},
|
||||
dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
dt.new_widget("label"){label = _("synchronize image time")},
|
||||
dt.new_widget("section_label"){label = _("calculate difference between images")},
|
||||
img_time.diff_entry,
|
||||
img_time.calc_btn,
|
||||
dt.new_widget("section_label"){label = _("apply difference")},
|
||||
img_time.sdir,
|
||||
img_time.btn,
|
||||
},
|
||||
dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
dt.new_widget("label"){label = _("reset to original time")},
|
||||
dt.new_widget("separator"){},
|
||||
dt.new_widget("button"){
|
||||
label = _("reset"),
|
||||
clicked_callback = function()
|
||||
reset_time(dt.gui.action_images)
|
||||
end
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
img_time.mode = dt.new_widget("combobox"){
|
||||
label = _("mode"),
|
||||
tooltip = _("select mode"),
|
||||
selected = 1,
|
||||
changed_callback = function(this)
|
||||
img_time.stack.active = this.selected
|
||||
end,
|
||||
_("adjust time"),
|
||||
_("set time"),
|
||||
_("synchronize time"),
|
||||
_("reset time")
|
||||
}
|
||||
|
||||
img_time.widget = dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
reset_callback = function(this)
|
||||
reset_widgets()
|
||||
end,
|
||||
img_time.mode,
|
||||
img_time.stack,
|
||||
}
|
||||
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not img_time.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
img_time.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -41,7 +41,6 @@ format (all fields can be the empty string):
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
|
||||
local gettext = dt.gettext
|
||||
|
||||
|
||||
453
contrib/photils.lua
Normal file
453
contrib/photils.lua
Normal file
@@ -0,0 +1,453 @@
|
||||
--[[ photils Auto Tagging plugin
|
||||
copyright (c) 2020 Tobias Scheck
|
||||
|
||||
darktable is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
darktable is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with darktable. If not, see <http://www.gnu.org/licenses/>.
|
||||
--]]
|
||||
|
||||
--[[
|
||||
A darktable plugin that tries to predict keywords based on the selected image.
|
||||
This plugin uses photils-cli to handle this task. Photils-cli is an application
|
||||
that passes the image through a neural network, classifies it, and extracts the
|
||||
suggested tags. Everything happens offline without the need that your data are
|
||||
sent over the internet.
|
||||
|
||||
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
|
||||
* photils-cli - https://github.com/scheckmedia/photils-cli at the moment only
|
||||
available for Linux and MacOS
|
||||
|
||||
USAGE
|
||||
* require this script from your main lua file
|
||||
To do this add this line to the file .config/darktable/luarc:
|
||||
require "contrib/photils"
|
||||
* Select an image
|
||||
* Press "get tags"
|
||||
* Select the tags you want from a list of suggestions
|
||||
* Press "Attach .. Tags" to add the selected tags to your image
|
||||
--]]
|
||||
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
local df = require "lib/dtutils.file"
|
||||
local dtsys = require "lib/dtutils.system"
|
||||
|
||||
local MODULE_NAME = "photils"
|
||||
du.check_min_api_version("5.0.0", MODULE_NAME)
|
||||
|
||||
local PS = dt.configuration.running_os == "windows" and "\\" or "/"
|
||||
local gettext = dt.gettext
|
||||
gettext.bindtextdomain(MODULE_NAME,
|
||||
dt.configuration.config_dir .. PS .. "lua" .. PS .. "locale" .. PS)
|
||||
|
||||
local exporter = dt.new_format("jpeg")
|
||||
exporter.quality = 80
|
||||
exporter.max_height = 224
|
||||
exporter.max_width = 224
|
||||
|
||||
-- helper functions
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext(MODULE_NAME, msgid)
|
||||
end
|
||||
|
||||
local function num_keys(tbl)
|
||||
local num = 0
|
||||
for _ in pairs(tbl) do num = num + 1 end
|
||||
return num
|
||||
end
|
||||
|
||||
local function has_key(tbl, value)
|
||||
for k, _ in pairs(tbl) do
|
||||
if k == value then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local photils_installed = df.check_if_bin_exists("photils-cli")
|
||||
|
||||
--[[
|
||||
local state object
|
||||
|
||||
maybe per_page is a preference variable but I think 10
|
||||
is a good value for the ui
|
||||
]]
|
||||
local PHOTILS = {
|
||||
tags = {},
|
||||
confidences = {},
|
||||
page = 1,
|
||||
per_page = 10,
|
||||
selected_tags = {},
|
||||
in_pagination = false,
|
||||
tagged_image = "",
|
||||
module_installed = false,
|
||||
event_registered = false,
|
||||
plugin_display_views = {
|
||||
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100},
|
||||
[dt.gui.views.darkroom] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 100}
|
||||
},
|
||||
}
|
||||
|
||||
local GUI = {
|
||||
container = dt.new_widget("box") {
|
||||
orientation = "vertical",
|
||||
sensitive = true,
|
||||
dt.new_widget("button") {
|
||||
label = _("get tags"),
|
||||
sensitive = photils_installed,
|
||||
clicked_callback = function() PHOTILS.on_tags_clicked() end
|
||||
},
|
||||
reset_callback = function() PHOTILS.on_reset(true) end
|
||||
},
|
||||
stack = dt.new_widget("stack"),
|
||||
prev_button = dt.new_widget("button") {
|
||||
label = "<",
|
||||
sensitive = false,
|
||||
clicked_callback = function()
|
||||
PHOTILS.page = PHOTILS.page - 1
|
||||
PHOTILS.paginate()
|
||||
end
|
||||
},
|
||||
next_button = dt.new_widget("button") {
|
||||
label = ">",
|
||||
sensitive = false,
|
||||
clicked_callback = function()
|
||||
PHOTILS.page = PHOTILS.page + 1
|
||||
PHOTILS.paginate()
|
||||
end
|
||||
},
|
||||
tag_box = dt.new_widget("box") {orientation = "vertical"},
|
||||
tag_view = dt.new_widget("box") {orientation = "vertical"},
|
||||
page_label = dt.new_widget("label") {label = ""},
|
||||
error_view = dt.new_widget("box") {orientation = "vertical"},
|
||||
warning_label = dt.new_widget("label") {
|
||||
label = ""
|
||||
},
|
||||
restart_required_label = dt.new_widget("label") {
|
||||
label = _("requires a restart to be applied")
|
||||
},
|
||||
attach_button = dt.new_widget("button") {
|
||||
label = "",
|
||||
sensitive = false,
|
||||
clicked_callback = function() PHOTILS.attach_tags() end
|
||||
},
|
||||
confidence_slider = dt.new_widget("slider") {
|
||||
step = 1,
|
||||
digits = 0,
|
||||
value = 90,
|
||||
hard_max = 100,
|
||||
hard_min = 0,
|
||||
soft_max = 100,
|
||||
soft_min = 0,
|
||||
label = _("min confidence value")
|
||||
},
|
||||
warning = dt.new_widget("label")
|
||||
}
|
||||
|
||||
function PHOTILS.image_changed()
|
||||
local current_image = tostring(dt.gui.selection()[1])
|
||||
if current_image ~= PHOTILS.tagged_image then
|
||||
if PHOTILS.tagged_image ~= "" then
|
||||
PHOTILS.tagged_image_has_changed()
|
||||
end
|
||||
|
||||
PHOTILS.tagged_image = tostring(current_image)
|
||||
end
|
||||
end
|
||||
|
||||
function PHOTILS.tagged_image_has_changed()
|
||||
GUI.warning.label = _("The suggested tags were not generated\n for the currently selected image!")
|
||||
end
|
||||
|
||||
function PHOTILS.paginate()
|
||||
PHOTILS.in_pagination = true
|
||||
local num_pages = math.ceil(#PHOTILS.tags / PHOTILS.per_page)
|
||||
GUI.page_label.label = string.format(_(" page %s of %s "), PHOTILS.page,
|
||||
num_pages)
|
||||
|
||||
if PHOTILS.page <= 1 then
|
||||
PHOTILS.page = 1
|
||||
GUI.prev_button.sensitive = false
|
||||
else
|
||||
GUI.prev_button.sensitive = true
|
||||
end
|
||||
|
||||
if PHOTILS.page > num_pages - 1 then
|
||||
PHOTILS.page = num_pages
|
||||
GUI.next_button.sensitive = false
|
||||
else
|
||||
GUI.next_button.sensitive = true
|
||||
end
|
||||
|
||||
--[[
|
||||
calculates the start positon in the tag array based on the current page
|
||||
and takes N tags from that array to show these in darktable
|
||||
e.g. page 1 goes from 1 to 10, page 2 from 11 to 20 a.s.o.
|
||||
the paginaton approach is related to a problem with the dynamic addition
|
||||
of mutliple widgets https://github.com/darktable-org/darktable/issues/4934#event-3318100463
|
||||
]]--
|
||||
local offset = ((PHOTILS.page - 1) * PHOTILS.per_page) + 1
|
||||
local tag_index = 1
|
||||
for i = offset, offset + PHOTILS.per_page - 1, 1 do
|
||||
local tag = PHOTILS.tags[i]
|
||||
local conf = PHOTILS.confidences[i]
|
||||
|
||||
GUI.tag_box[tag_index].value = has_key(PHOTILS.selected_tags, tag)
|
||||
|
||||
if tag then
|
||||
if dt.preferences.read(MODULE_NAME, "show_confidence", "bool") then
|
||||
tag = tag .. string.format(" (%.3f)", conf)
|
||||
end
|
||||
|
||||
GUI.tag_box[tag_index].label = tag
|
||||
GUI.tag_box[tag_index].sensitive = true
|
||||
else
|
||||
GUI.tag_box[tag_index].label = ""
|
||||
GUI.tag_box[tag_index].sensitive = false
|
||||
end
|
||||
tag_index = tag_index + 1
|
||||
end
|
||||
|
||||
PHOTILS.in_pagination = false
|
||||
end
|
||||
|
||||
function PHOTILS.attach_tags()
|
||||
local image = dt.gui.selection()[1]
|
||||
for tag, _ in pairs(PHOTILS.selected_tags) do
|
||||
local dt_tag = dt.tags.create(tag)
|
||||
dt.tags.attach(dt_tag, image)
|
||||
end
|
||||
|
||||
dt.print(_("Tags successfully attached to image"))
|
||||
end
|
||||
|
||||
function PHOTILS.get_tags(image, with_export)
|
||||
local tmp_file = df.create_tmp_file()
|
||||
local in_arg = df.sanitize_filename(tostring(image))
|
||||
local out_arg = df.sanitize_filename(tmp_file)
|
||||
local executable = photils_installed
|
||||
|
||||
if dt.configuration.running_os == "macos" then
|
||||
executable = executable .. "/Contents/MacOS/photils-cli"
|
||||
end
|
||||
|
||||
if with_export then
|
||||
dt.print_log("use export to for prediction")
|
||||
local export_file = df.create_tmp_file()
|
||||
exporter:write_image(image, export_file)
|
||||
in_arg = df.sanitize_filename(tostring(export_file))
|
||||
end
|
||||
|
||||
local command = executable .. " -c " .. " -i " .. in_arg .. " -o " .. out_arg
|
||||
|
||||
local ret = dtsys.external_command(command)
|
||||
if ret > 0 then
|
||||
dt.print_error(string.format("command %s returned error code %d", command, ret))
|
||||
os.remove(tmp_file)
|
||||
|
||||
-- try to export the image and run tagging
|
||||
if not with_export then
|
||||
return PHOTILS.get_tags(image, true)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
for i = #PHOTILS.tags, 1, -1 do
|
||||
PHOTILS.tags[i] = nil
|
||||
PHOTILS.confidences[i] = nil
|
||||
end
|
||||
|
||||
for tag in io.lines(tmp_file) do
|
||||
local splitted = du.split(tag, ":")
|
||||
if 100 * tonumber(splitted[2]) >= GUI.confidence_slider.value then
|
||||
PHOTILS.tags[#PHOTILS.tags + 1] = splitted[1]
|
||||
PHOTILS.confidences[#PHOTILS.confidences+1] = splitted[2]
|
||||
end
|
||||
end
|
||||
|
||||
dt.print(string.format(_("%s found %d tags for your image"), MODULE_NAME,
|
||||
#PHOTILS.tags))
|
||||
os.remove(tmp_file)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function PHOTILS.on_tags_clicked()
|
||||
PHOTILS.page = 1
|
||||
GUI.warning.label = ""
|
||||
|
||||
PHOTILS.on_reset(false)
|
||||
|
||||
local images = dt.gui.selection()
|
||||
|
||||
if #images == 0 then
|
||||
dt.print(_("No image selected."))
|
||||
dt.control.sleep(2000)
|
||||
else
|
||||
if #images > 1 then
|
||||
dt.print(_("This plugin can only handle a single image."))
|
||||
dt.gui.selection({images[1]})
|
||||
dt.control.sleep(2000)
|
||||
end
|
||||
|
||||
if not PHOTILS.get_tags(images[1], true) then
|
||||
local msg = string.format(_("%s failed, see terminal output for details"), MODULE_NAME)
|
||||
GUI.warning_label.label = msg
|
||||
GUI.stack.active = GUI.error_view
|
||||
dt.print(msg)
|
||||
return
|
||||
end
|
||||
|
||||
if #PHOTILS.tags == 0 then
|
||||
local msg = string.format(_("no tags where found"), MODULE_NAME)
|
||||
GUI.warning_label.label = msg
|
||||
GUI.stack.active = GUI.error_view
|
||||
return
|
||||
end
|
||||
|
||||
GUI.stack.active = GUI.tag_view
|
||||
PHOTILS.paginate()
|
||||
end
|
||||
end
|
||||
|
||||
function PHOTILS.tag_selected(tag_button)
|
||||
if PHOTILS.in_pagination then return end
|
||||
|
||||
if tag_button.value then
|
||||
local tag = tag_button.label
|
||||
if dt.preferences.read(MODULE_NAME, "show_confidence", "bool") then
|
||||
local idx = string.find(tag, "%(") - 2
|
||||
tag = string.sub(tag, 0, idx)
|
||||
end
|
||||
|
||||
PHOTILS.selected_tags[tag] = tag
|
||||
else
|
||||
PHOTILS.selected_tags[tag_button.label] = nil
|
||||
end
|
||||
|
||||
local num_selected = num_keys(PHOTILS.selected_tags)
|
||||
if num_selected == 0 then
|
||||
GUI.attach_button.label = ""
|
||||
GUI.attach_button.sensitive = false
|
||||
else
|
||||
GUI.attach_button.label = string.format(_("attach %d tags"),
|
||||
num_selected)
|
||||
GUI.attach_button.sensitive = true
|
||||
end
|
||||
end
|
||||
|
||||
function PHOTILS.on_reset(with_view)
|
||||
if with_view then GUI.stack.active = 1 end
|
||||
|
||||
for k, _ in pairs(PHOTILS.selected_tags) do
|
||||
PHOTILS.selected_tags[k] = nil
|
||||
end
|
||||
|
||||
for _, v in ipairs(GUI.tag_box) do
|
||||
v.value = false
|
||||
end
|
||||
|
||||
GUI.attach_button.label = ""
|
||||
GUI.attach_button.sensitive = false
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not PHOTILS.module_installed then
|
||||
dt.register_lib(MODULE_NAME,
|
||||
"photils autotagger",
|
||||
true,
|
||||
true,
|
||||
PHOTILS.plugin_display_views,
|
||||
GUI.container,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
PHOTILS.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- add a fix number of buttons
|
||||
for _ = 1, PHOTILS.per_page, 1 do
|
||||
local btn_tag = dt.new_widget("check_button") {
|
||||
label = "",
|
||||
sensitive = false,
|
||||
clicked_callback = PHOTILS.tag_selected
|
||||
}
|
||||
|
||||
table.insert(GUI.tag_box, btn_tag)
|
||||
end
|
||||
|
||||
if not photils_installed then
|
||||
GUI.warning_label.label = _("photils-cli not found")
|
||||
dt.print_log(_("photils-cli not found"))
|
||||
else
|
||||
GUI.warning_label.label = _("Select an image, click \"get tags\" and get \nsuggestions for tags.")
|
||||
end
|
||||
|
||||
GUI.pagination = dt.new_widget("box") {
|
||||
orientation = "horizontal",
|
||||
GUI.prev_button,
|
||||
GUI.page_label,
|
||||
GUI.next_button
|
||||
}
|
||||
|
||||
|
||||
table.insert(GUI.error_view, GUI.warning_label)
|
||||
if not photils_installed then
|
||||
table.insert(GUI.error_view, df.executable_path_widget({"photils-cli"}))
|
||||
table.insert(GUI.error_view, GUI.restart_required_label)
|
||||
end
|
||||
table.insert(GUI.stack, GUI.error_view)
|
||||
table.insert(GUI.stack, GUI.tag_view)
|
||||
|
||||
table.insert(GUI.tag_view, GUI.pagination)
|
||||
table.insert(GUI.tag_view, GUI.tag_box)
|
||||
table.insert(GUI.tag_view, GUI.attach_button)
|
||||
table.insert(GUI.tag_view, GUI.warning)
|
||||
|
||||
table.insert(GUI.container, GUI.confidence_slider)
|
||||
table.insert(GUI.container, GUI.stack)
|
||||
|
||||
GUI.stack.active = 1
|
||||
|
||||
|
||||
|
||||
-- uses photils: prefix because script settings are all together and not seperated by script
|
||||
dt.preferences.register(MODULE_NAME,
|
||||
"show_confidence",
|
||||
"bool",
|
||||
_("photils: show confidence value"),
|
||||
_("if enabled, the confidence value for each tag is displayed"),
|
||||
true)
|
||||
|
||||
dt.register_event("mouse-over-image-changed",PHOTILS.image_changed)
|
||||
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not PHOTILS.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
PHOTILS.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -47,6 +47,12 @@ local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
local debug = require "darktable.debug"
|
||||
|
||||
local qt = {}
|
||||
qt.module_installed = false
|
||||
qt.event_registered = false
|
||||
qt.widget_table = {}
|
||||
|
||||
|
||||
|
||||
local gettext = dt.gettext
|
||||
|
||||
@@ -177,6 +183,27 @@ local function update_quicktag_list()
|
||||
end
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not qt.module_installed then
|
||||
dt.register_lib(
|
||||
"quicktag", -- Module name
|
||||
"quicktag", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 490}},
|
||||
|
||||
dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
table.unpack(qt.widget_table),
|
||||
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
qt.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
update_quicktag_list()
|
||||
|
||||
local new_quicktag = dt.new_widget("entry"){
|
||||
@@ -213,33 +240,32 @@ local new_qt_widget = dt.new_widget ("box") {
|
||||
|
||||
-- back UI elements in a table
|
||||
-- thanks to wpferguson for the hint
|
||||
local widget_table = {}
|
||||
|
||||
for i=1,qnr do
|
||||
widget_table[#widget_table + 1] = button[i]
|
||||
qt.widget_table[#qt.widget_table + 1] = button[i]
|
||||
end
|
||||
|
||||
widget_table[#widget_table + 1] = dt.new_widget("separator"){}
|
||||
widget_table[#widget_table + 1] = old_quicktag
|
||||
widget_table[#widget_table + 1] = new_qt_widget
|
||||
qt.widget_table[#qt.widget_table + 1] = dt.new_widget("separator"){}
|
||||
qt.widget_table[#qt.widget_table + 1] = old_quicktag
|
||||
qt.widget_table[#qt.widget_table + 1] = new_qt_widget
|
||||
|
||||
|
||||
--create module
|
||||
dt.register_lib(
|
||||
"quicktag", -- Module name
|
||||
"quicktag", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 490}},
|
||||
|
||||
dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
table.unpack(widget_table),
|
||||
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not qt.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
qt.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
-- create shortcuts
|
||||
for i=1,qnr do
|
||||
|
||||
@@ -35,6 +35,10 @@ local debug = require "darktable.debug"
|
||||
-- check API version
|
||||
du.check_min_api_version("3.0.0", "rename-tags")
|
||||
|
||||
local rt = {}
|
||||
rt.module_installed = false
|
||||
rt.event_registered = false
|
||||
|
||||
-- GUI entries
|
||||
local old_tag = darktable.new_widget("entry") { tooltip = "Enter old tag" }
|
||||
local new_tag = darktable.new_widget("entry") { tooltip = "Enter new tag" }
|
||||
@@ -99,6 +103,13 @@ local function rename_tags()
|
||||
rename_reset()
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not rt.module_installed then
|
||||
darktable.register_lib ("rename_tags", "rename tag", true, true, {[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 20},}, rt.rename_widget, nil, nil)
|
||||
rt.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- GUI
|
||||
local old_widget = darktable.new_widget ("box") {
|
||||
orientation = "horizontal",
|
||||
@@ -112,7 +123,7 @@ local new_widget = darktable.new_widget ("box") {
|
||||
new_tag
|
||||
}
|
||||
|
||||
local rename_widget = darktable.new_widget ("box") {
|
||||
rt.rename_widget = darktable.new_widget ("box") {
|
||||
orientation = "vertical",
|
||||
reset_callback = rename_reset,
|
||||
old_widget,
|
||||
@@ -120,6 +131,20 @@ local rename_widget = darktable.new_widget ("box") {
|
||||
darktable.new_widget("button") { label = "Go", clicked_callback = rename_tags }
|
||||
}
|
||||
|
||||
if darktable.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not rt.event_registered then
|
||||
darktable.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
rt.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
darktable.register_lib ("rename_tags", "rename tag", true, true, {[darktable.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 20},}, rename_widget, nil, nil)
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ USAGE
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
|
||||
du.check_min_api_version("2.0.2", "slideshowMusic")
|
||||
|
||||
@@ -99,6 +99,10 @@ end
|
||||
|
||||
-- Helper functions: BEGIN
|
||||
|
||||
local th = {}
|
||||
th.module_installed = false
|
||||
th.event_registered = false
|
||||
|
||||
local function pathExists(path)
|
||||
local success, err, errno = os.rename(path, path)
|
||||
if not success then
|
||||
@@ -122,6 +126,16 @@ local function createDirectory(path)
|
||||
end
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not th.module_installed then
|
||||
darktable.register_lib(LIB_ID,
|
||||
"transfer hierarchy", true, true, {
|
||||
[darktable.gui.views.lighttable] = { "DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 700 }
|
||||
}, th.transfer_widget, nil, nil)
|
||||
th.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Helper functions: END
|
||||
|
||||
|
||||
@@ -292,7 +306,7 @@ end
|
||||
|
||||
|
||||
|
||||
local transfer_widget = darktable.new_widget("box") {
|
||||
th.transfer_widget = darktable.new_widget("box") {
|
||||
orientation = "vertical",
|
||||
darktable.new_widget("button") {
|
||||
label = _("calculate"),
|
||||
@@ -347,12 +361,18 @@ darktable.preferences.register(
|
||||
|
||||
-- Preferences: END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
darktable.register_lib(LIB_ID,
|
||||
"transfer hierarchy", true, true, {
|
||||
[darktable.gui.views.lighttable] = { "DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 700 }
|
||||
}, transfer_widget, nil, nil)
|
||||
if darktable.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not th.event_registered then
|
||||
darktable.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
th.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -96,7 +96,7 @@ local resolutions = {
|
||||
}
|
||||
}
|
||||
|
||||
local framerates = {"15", "16", "23.98", "24", "25", "29,97", "30", "48", "50", "59.94", "60"}
|
||||
local framerates = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "15", "16", "23.98", "24", "25", "29,97", "30", "48", "50", "59.94", "60", "120", "240", "300"}
|
||||
|
||||
local formats = {
|
||||
["AVI"] = {
|
||||
|
||||
@@ -46,6 +46,44 @@ local function _(msgid)
|
||||
return gettext.dgettext("moduleExample", msgid)
|
||||
end
|
||||
|
||||
-- declare a local namespace and a couple of variables we'll need to install the module
|
||||
local mE = {}
|
||||
mE.widgets = {}
|
||||
mE.event_registered = false -- keep track of whether we've added an event callback or not
|
||||
mE.module_installed = false -- keep track of whether the module is module_installed
|
||||
|
||||
--[[ We have to create the module in one of two ways depending on which view darktable starts
|
||||
in. In orker to not repeat code, we wrap the darktable.register_lib in a local function.
|
||||
]]
|
||||
|
||||
local function install_module()
|
||||
if not mE.module_installed then
|
||||
-- https://www.darktable.org/lua-api/index.html#darktable_register_lib
|
||||
dt.register_lib(
|
||||
"exampleModule", -- Module name
|
||||
"exampleModule", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
-- https://www.darktable.org/lua-api/types_lua_box.html
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = _("MyButton"),
|
||||
clicked_callback = function (_)
|
||||
dt.print(_("Button clicked"))
|
||||
end
|
||||
},
|
||||
table.unpack(mE.widgets),
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
mE.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- https://www.darktable.org/lua-api/types_lua_check_button.html
|
||||
local check_button = dt.new_widget("check_button"){label = _("MyCheck_button"), value = true}
|
||||
@@ -90,35 +128,35 @@ local slider = dt.new_widget("slider")
|
||||
value = 52 -- The current value of the slider
|
||||
}
|
||||
|
||||
-- https://www.darktable.org/lua-api/index.html#darktable_register_lib
|
||||
dt.register_lib(
|
||||
"exampleModule", -- Module name
|
||||
"exampleModule", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
-- https://www.darktable.org/lua-api/types_lua_box.html
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
dt.new_widget("button")
|
||||
{
|
||||
label = _("MyButton"),
|
||||
clicked_callback = function (_)
|
||||
dt.print(_("Button clicked"))
|
||||
-- pack the widgets in a table for loading in the module
|
||||
|
||||
table.insert(mE.widgets, check_button)
|
||||
table.insert(mE.widgets, combobox)
|
||||
table.insert(mE.widgets, entry)
|
||||
table.insert(mE.widgets, file_chooser_button)
|
||||
table.insert(mE.widgets, label)
|
||||
table.insert(mE.widgets, separator)
|
||||
table.insert(mE.widgets, slider)
|
||||
|
||||
-- ... and tell dt about it all
|
||||
|
||||
|
||||
if dt.gui.current_view().id == "lighttable" then -- make sure we are in lighttable view
|
||||
install_module() -- register the lib
|
||||
else
|
||||
if not mE.event_registered then -- if we are not in lighttable view then register an event to signal when we might be
|
||||
-- https://www.darktable.org/lua-api/index.html#darktable_register_event
|
||||
dt.register_event(
|
||||
"view-changed", -- we want to be informed when the view changes
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then -- if the view changes from darkroom to lighttable
|
||||
install_module() -- register the lib
|
||||
end
|
||||
end
|
||||
},
|
||||
check_button,
|
||||
combobox,
|
||||
entry,
|
||||
file_chooser_button,
|
||||
label,
|
||||
separator,
|
||||
slider
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
)
|
||||
mE.event_registered = true -- keep track of whether we have an event handler installed
|
||||
end
|
||||
end
|
||||
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
-- kate: hl Lua;
|
||||
|
||||
@@ -204,24 +204,26 @@ end
|
||||
to see if the executable is there.
|
||||
]]
|
||||
|
||||
local executable = "ufraw-batch"
|
||||
local ufraw_batch_path_widget = dt.new_widget("file_chooser_button"){
|
||||
title = _("Select ufraw-batch[.exe] executable"),
|
||||
value = df.get_executable_path_preference(executable),
|
||||
is_directory = false,
|
||||
changed_callback = function(self)
|
||||
if df.check_if_bin_exists(self.value) then
|
||||
df.set_executable_path_preference(executable, self.value)
|
||||
if dt.configuration.running_os ~= "linux" then
|
||||
local executable = "ufraw-batch"
|
||||
local ufraw_batch_path_widget = dt.new_widget("file_chooser_button"){
|
||||
title = _("Select ufraw-batch[.exe] executable"),
|
||||
value = df.get_executable_path_preference(executable),
|
||||
is_directory = false,
|
||||
changed_callback = function(self)
|
||||
if df.check_if_bin_exists(self.value) then
|
||||
df.set_executable_path_preference(executable, self.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
dt.preferences.register("executable_paths", "ufraw-batch", -- name
|
||||
"file", -- type
|
||||
_('multi_os: ufraw-batch location'), -- label
|
||||
_('Installed location of ufraw-batch. Requires restart to take effect.'), -- tooltip
|
||||
"ufraw-batch", -- default
|
||||
ufraw_batch_path_widget
|
||||
)
|
||||
}
|
||||
dt.preferences.register("executable_paths", "ufraw-batch", -- name
|
||||
"file", -- type
|
||||
_('multi_os: ufraw-batch location'), -- label
|
||||
_('Installed location of ufraw-batch. Requires restart to take effect.'), -- tooltip
|
||||
"ufraw-batch", -- default
|
||||
ufraw_batch_path_widget
|
||||
)
|
||||
end
|
||||
|
||||
--[[
|
||||
Add a button to the selected images module in lighttable
|
||||
|
||||
@@ -329,6 +329,7 @@ local function _old_check_if_bin_exists(bin) -- only run on windows if preferen
|
||||
local path = nil
|
||||
|
||||
if string.match(bin, "\\") then
|
||||
|
||||
path = bin
|
||||
else
|
||||
path = dtutils_file.get_executable_path_preference(bin)
|
||||
@@ -540,7 +541,7 @@ function dtutils_file.check_if_file_exists(filepath)
|
||||
local p = io.popen("if exist " .. dtutils_file.sanitize_filename(filepath) .. " (echo 'yes') else (echo 'no')")
|
||||
local ans = p:read("*all")
|
||||
p:close()
|
||||
if string.match(ans, "yes") then
|
||||
if string.match(ans, "yes") then
|
||||
result = true
|
||||
end
|
||||
-- result = os.execute('if exist "'..filepath..'" (cmd /c exit 0) else (cmd /c exit 1)')
|
||||
@@ -690,8 +691,8 @@ dtutils_file.libdoc.functions["filename_increment"] = {
|
||||
|
||||
local result = df.filename_increment(filepath)
|
||||
filepath - string - filename to increment]],
|
||||
Description = [[filename_increment solves the problem of filename confllict by adding an
|
||||
increment to the filename. If the supplied filename has no increment then
|
||||
Description = [[filename_increment solves the problem of filename confllict by adding an
|
||||
increment to the filename. If the supplied filename has no increment then
|
||||
"01" is added to the basename. If the filename already has an increment, then
|
||||
1 is added to it and the filename returned.]],
|
||||
Return_Value = [[result - string - the incremented filename]],
|
||||
@@ -743,7 +744,7 @@ dtutils_file.libdoc.functions["create_unique_filename"] = {
|
||||
filepath - string - the path and filename requested]],
|
||||
Description = [[create_unique_filename takes a requested filepath and checks to see if
|
||||
it exists. If if doesn't then it's returned intact. If it already exists, then a two
|
||||
digit increment is added to the filename and it is tested again. The increment keeps
|
||||
digit increment is added to the filename and it is tested again. The increment keeps
|
||||
increasing until either a unique filename is found or there have been 100 attempts.]],
|
||||
Return_Value = [[result - string - the incremented filename]],
|
||||
Limitations = [[create_unique_filename will only attempt 100 increments.]],
|
||||
@@ -822,7 +823,7 @@ dtutils_file.libdoc.functions["executable_path_widget"] = {
|
||||
local widget = df.executable_path_widget(executables)
|
||||
executables - table - a table of strings that are executable names]],
|
||||
Description = [[executable_path_widget takes a table of executable names
|
||||
and builds a set of file selector widgets to get the path to the executable.
|
||||
and builds a set of file selector widgets to get the path to the executable.
|
||||
The resulting widgets are wrapped in a box widget and returned.]],
|
||||
Return_Value = [[widget - widget - a widget containing a file selector widget for
|
||||
each executable.]],
|
||||
@@ -837,10 +838,10 @@ dtutils_file.libdoc.functions["executable_path_widget"] = {
|
||||
function dtutils_file.executable_path_widget(executables)
|
||||
local box_widgets = {}
|
||||
table.insert(box_widgets, dt.new_widget("section_label"){label = "select executable(s)"})
|
||||
for _, executable in pairs(executables) do
|
||||
for _, executable in pairs(executables) do
|
||||
table.insert(box_widgets, dt.new_widget("label"){label = "select " .. executable .. " executable"})
|
||||
local path = dtutils_file.get_executable_path_preference(executable)
|
||||
if not path then
|
||||
if not path then
|
||||
path = ""
|
||||
end
|
||||
table.insert(box_widgets, dt.new_widget("file_chooser_button"){
|
||||
@@ -870,7 +871,7 @@ dtutils_file.libdoc.functions["sanitize_filename"] = {
|
||||
local sanitized_filename = df.sanitize_filename(filename)
|
||||
filename - string - a filepath and filename]],
|
||||
Description = [[sanitize_file places quotes around the filename in an
|
||||
operating system specific manner. The result is safe to pass as
|
||||
operating system specific manner. The result is safe to pass as
|
||||
an argument to the operating system.]],
|
||||
Return_Value = [[sanitized_filename - string - quoted filename]],
|
||||
Limitations = [[]],
|
||||
@@ -892,7 +893,7 @@ dtutils_file.libdoc.functions["mkdir"] = {
|
||||
|
||||
df.mkdir(path)
|
||||
path - string - a directory path]],
|
||||
Description = [[mkdir creates directories if not already exists. It
|
||||
Description = [[mkdir creates directories if not already exists. It
|
||||
create whole parents subtree if needed
|
||||
]],
|
||||
Return_Value = [[path - string - a directory path]],
|
||||
@@ -904,7 +905,7 @@ dtutils_file.libdoc.functions["mkdir"] = {
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.mkdir(path)
|
||||
function dtutils_file.mkdir(path)
|
||||
if not dtutils_file.check_if_file_exists(path) then
|
||||
local mkdir_cmd = dt.configuration.running_os == "windows" and "mkdir" or "mkdir -p"
|
||||
return dsys.external_command(mkdir_cmd.." "..path)
|
||||
@@ -983,5 +984,6 @@ if dt.configuration.running_os == "windows" then
|
||||
false)
|
||||
end
|
||||
|
||||
|
||||
return dtutils_file
|
||||
|
||||
|
||||
@@ -269,5 +269,31 @@ function dtutils_string.sanitize(str)
|
||||
end
|
||||
end
|
||||
|
||||
dtutils_string.libdoc.functions["sanitize_lua"] = {
|
||||
Name = [[sanitize_lua]],
|
||||
Synopsis = [[escape lua 'magic' characters from a pattern string]],
|
||||
Usage = [[local ds = require "lib/dtutils.string"
|
||||
|
||||
local result = ds.sanitize_lua(str)
|
||||
str - string - the string that needs to be made safe]],
|
||||
Description = [[sanitize_lua escapes lua 'magic' characters so that
|
||||
a string may be used in lua string/patten matching.]],
|
||||
Return_Value = [[result - string - a lua pattern safe string]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_string.sanitize_lua(str)
|
||||
str = string.gsub(str, "%-", "%%-")
|
||||
str = string.gsub(str, "%(", "%%(")
|
||||
str = string.gsub(str, "%)", "%%)")
|
||||
return str
|
||||
end
|
||||
|
||||
|
||||
|
||||
return dtutils_string
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
local dtutils_system = {}
|
||||
|
||||
local dt = require "darktable"
|
||||
require "official/yield" -- necessary for dt.control.execute()
|
||||
|
||||
dtutils_system.libdoc = {
|
||||
Name = [[dtutils.system]],
|
||||
|
||||
@@ -1,39 +1,78 @@
|
||||
# OpenInExplorer Darktable Plug-In.
|
||||
# Copyright (C) 2020 Volker Lenhardt
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# Volker Lenhardt <volker.lenhardt@unitybox.de>, 2020.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenInExplorer\n"
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-19 14:43+0200\n"
|
||||
"POT-Creation-Date: 2020-06-11 19:07+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Volker Lenhardt <volker.lenhardt@unitybox.de>\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: OpenInExplorer.lua:64
|
||||
#: OpenInExplorer.lua:71
|
||||
msgid ""
|
||||
"OpenInExplorer plug-in only supports Linux, macOS, and Windows at this time"
|
||||
msgstr "OpenInExplorer-Plug-in kann nur auf macOS, Linux oder Windows laufen"
|
||||
"OpenInExplorer plug-in only supports Linux and macOS, and Windows at this time"
|
||||
msgstr "OpenInExplorer-Plug-in unterstützt zur Zeit nur Linux, macOS oder Windows"
|
||||
|
||||
#: OpenInExplorer.lua:79
|
||||
#: OpenInExplorer.lua:87
|
||||
msgid ""
|
||||
"No links directory selected.\n"
|
||||
"Please check the dt preferences (lua options)"
|
||||
msgstr "Kein Verknüpfungs-Verzeichnis ausgewählt.\nBitte die dt-Voreinstellungen überprüfen (Lua-Optionen)"
|
||||
|
||||
#: OpenInExplorer.lua:91
|
||||
#, lua-format
|
||||
msgid ""
|
||||
"Links directory '%s' not found.\n"
|
||||
"Please check the dt preferences (lua options)"
|
||||
msgstr "Das Verknüpfungs-Verzeichnis '%s' konnte nicht gefunden werden.\nBitte die dt-Voreinstellungen überprüfen (Lua-Optionen)"
|
||||
|
||||
#: OpenInExplorer.lua:148
|
||||
msgid "Failed to create links. Missing rights?"
|
||||
msgstr "Die Verknüpfungen konnten nicht erstellt werden. Fehlende Rechte?"
|
||||
|
||||
#: OpenInExplorer.lua:164
|
||||
msgid "Please select an image"
|
||||
msgstr "Es wurde kein Bild ausgewählt"
|
||||
|
||||
#: OpenInExplorer.lua:98
|
||||
msgid "Please select fewer images (max 15)"
|
||||
#: OpenInExplorer.lua:170
|
||||
msgid "Please select fewer images (max. 15)"
|
||||
msgstr "Bitte nicht mehr als 15 Bilder auswählen"
|
||||
|
||||
#: OpenInExplorer.lua:105
|
||||
#: OpenInExplorer.lua:186
|
||||
msgid "show in file explorer"
|
||||
msgstr "im Dateimanager anzeigen"
|
||||
|
||||
#: OpenInExplorer.lua:107
|
||||
msgid "Opens the file manager at the selected image's location"
|
||||
#: OpenInExplorer.lua:188
|
||||
msgid "Open the file manager at the selected image's location"
|
||||
msgstr "Öffnet den Dateimanager an der Position des ausgewählten Bildes"
|
||||
|
||||
#: OpenInExplorer.lua:193
|
||||
msgid "OpenInExplorer: linked files directory"
|
||||
msgstr "OpenInExplorer: Verknüpfungs-Verzeichnis"
|
||||
|
||||
#: OpenInExplorer.lua:194
|
||||
msgid ""
|
||||
"Directory to store the links to the file names. Requires restart to take "
|
||||
"effect"
|
||||
msgstr "Verzeichnis für die Verknüpfungen zu den Dateinamen. Erfordert dt-Neustart"
|
||||
|
||||
#: OpenInExplorer.lua:197
|
||||
msgid "Select directory"
|
||||
msgstr "Verzeichnis auswählen"
|
||||
|
||||
#: OpenInExplorer.lua:203
|
||||
msgid "OpenInExplorer: use links"
|
||||
msgstr "OpenInExplorer: Verknüpfungen nutzen"
|
||||
|
||||
#: OpenInExplorer.lua:204
|
||||
msgid "Use links instead of multiple windows. Requires restart to take effect"
|
||||
msgstr "Verknüpfungen statt einzelner Fenster nutzen. Erfordert dt-Neustart"
|
||||
|
||||
73
locale/de_DE/LC_MESSAGES/photils.po
Normal file
73
locale/de_DE/LC_MESSAGES/photils.po
Normal file
@@ -0,0 +1,73 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Lua photils\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-05-12 13:12+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "get tags"
|
||||
msgstr "hole stichwörter"
|
||||
|
||||
msgid "requires a restart to be applied"
|
||||
msgstr "erfordert einen Neustart um angewendet zu werden"
|
||||
|
||||
msgid "min confidence value"
|
||||
msgstr "min vertrauenswert"
|
||||
|
||||
msgid ""
|
||||
"The suggested tags were not generated\n"
|
||||
" for the currently selected image!"
|
||||
msgstr ""
|
||||
"Die vorgeschlagenen Stichwörter wurden für das\n"
|
||||
"aktuell ausgewählte Bild nicht generiert!"
|
||||
|
||||
#, lua-format
|
||||
msgid " page %s of %s "
|
||||
msgstr " seite %s von %s "
|
||||
|
||||
msgid "Tags successfully attached to image"
|
||||
msgstr "Stichwörter erfolgreich an Bild angefügt"
|
||||
|
||||
#, lua-format
|
||||
msgid "Error writing to `%s`"
|
||||
msgstr "Fehler beim Schreiben in `%s`"
|
||||
|
||||
#, lua-format
|
||||
msgid "%s failed, see terminal output for details"
|
||||
msgstr "%s fehlgeschlagen, siehe Terminal-Ausgabe für Details"
|
||||
|
||||
#, lua-format
|
||||
msgid "%s found %d tags for your image"
|
||||
msgstr "%s gefunden %d-Stichwörter für Ihr Bild"
|
||||
|
||||
msgid "No image selected."
|
||||
msgstr "Kein Bild ausgewählt."
|
||||
|
||||
msgid "This plugin can only handle a single image."
|
||||
msgstr "Dieses Plugin kann nur ein einziges Bild verarbeiten."
|
||||
|
||||
#, lua-format
|
||||
msgid "attach %d tags"
|
||||
msgstr "%d stichwörter anhängen"
|
||||
|
||||
msgid "photils-cli not found"
|
||||
msgstr "photils-cli nicht gefunden"
|
||||
|
||||
msgid ""
|
||||
"Select an image, click \"Get Tags\" and get \n"
|
||||
"suggestions for tags."
|
||||
msgstr ""
|
||||
"Wählen Sie ein Bild aus, drücken Sie \"Hole Stichwörter\"\n"
|
||||
" und erhalten Sie Vorschläge für Stichwörter."
|
||||
@@ -46,10 +46,35 @@ du.check_min_api_version("3.0.0", "enfuse")
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("enfuse",dt.configuration.config_dir..PS .. "lua" .. PS .. "locale" .. PS)
|
||||
|
||||
local enf = {}
|
||||
enf.event_registered = false
|
||||
enf.module_installed = false
|
||||
enf.lib_widgets = {}
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("enfuse", msgid)
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not enf.module_installed then
|
||||
dt.register_lib(
|
||||
"enfuse", -- plugin name
|
||||
"enfuse", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
sensitive = enfuse_installed,
|
||||
table.unpack(enf.lib_widgets)
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
enf.module_installed = true
|
||||
end
|
||||
end
|
||||
-- add a new lib
|
||||
-- is enfuse installed?
|
||||
local enfuse_installed = df.check_if_bin_exists("enfuse")
|
||||
@@ -229,30 +254,30 @@ if enfuse_installed then
|
||||
local lib_widgets = {}
|
||||
|
||||
if not enfuse_installed then
|
||||
table.insert(lib_widgets, df.executable_path_widget({"ffmpeg"}))
|
||||
table.insert(enf.lib_widgets, df.executable_path_widget({"ffmpeg"}))
|
||||
end
|
||||
table.insert(lib_widgets, exposure_mu)
|
||||
table.insert(lib_widgets, depth)
|
||||
table.insert(lib_widgets, blend_colorspace)
|
||||
table.insert(lib_widgets, enfuse_button)
|
||||
table.insert(enf.lib_widgets, exposure_mu)
|
||||
table.insert(enf.lib_widgets, depth)
|
||||
table.insert(enf.lib_widgets, blend_colorspace)
|
||||
table.insert(enf.lib_widgets, enfuse_button)
|
||||
|
||||
|
||||
-- ... and tell dt about it all
|
||||
dt.register_lib(
|
||||
"enfuse", -- plugin name
|
||||
"enfuse", -- name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
sensitive = enfuse_installed,
|
||||
table.unpack(lib_widgets)
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not enf.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
enf.event_registered = true
|
||||
end
|
||||
end
|
||||
else
|
||||
dt.print_error("enfuse executable not found")
|
||||
error("enfuse executable not found")
|
||||
|
||||
@@ -37,7 +37,6 @@ USAGE
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
require "darktable.debug"
|
||||
require "official/yield"
|
||||
|
||||
du.check_min_api_version("2.1.0", "generate_image_txt")
|
||||
|
||||
|
||||
@@ -33,8 +33,22 @@ local du = require "lib/dtutils"
|
||||
|
||||
du.check_min_api_version("2.0.0", "image_path_in_ui")
|
||||
|
||||
local ipiu = {}
|
||||
ipiu.module_installed = false
|
||||
ipiu.event_registered = false
|
||||
|
||||
local main_label = dt.new_widget("label"){selectable = true, ellipsize = "middle", halign = "start"}
|
||||
|
||||
local function install_module()
|
||||
if not ipiu.module_installed then
|
||||
dt.register_lib("image_path_no_ui","Selected Images path",true,false,{
|
||||
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER",300}
|
||||
}, main_label
|
||||
)
|
||||
ipiu.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
local function reset_widget()
|
||||
local selection = dt.gui.selection()
|
||||
local result = ""
|
||||
@@ -54,10 +68,21 @@ end
|
||||
|
||||
main_label.reset_callback = reset_widget
|
||||
|
||||
dt.register_lib("image_path_no_ui","Selected Images path",true,false,{
|
||||
[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER",300}
|
||||
}, main_label
|
||||
);
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not ipiu.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
ipiu.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
dt.register_event("mouse-over-image-changed",reset_widget);
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ Plugin allows you to choose how many thumbnails you need per row
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
require "official/yield"
|
||||
|
||||
du.check_min_api_version("2.0.0", "selection_to_pdf")
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
--[[
|
||||
This file is part of darktable,
|
||||
Copyright 2016 by Tobias Jakobs.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]
|
||||
--[[
|
||||
darktable yield compatibility script
|
||||
|
||||
USAGE
|
||||
* require this script from your main Lua file in the first line
|
||||
|
||||
]]
|
||||
local dt = require "darktable"
|
||||
local yield_orig = coroutine.yield
|
||||
|
||||
if (dt.configuration.api_version_major < 4) then
|
||||
dt.control = {}
|
||||
dt.control.execute = function(command)
|
||||
yield_orig("RUN_COMMAND", command)
|
||||
end
|
||||
dt.control.read = function(command)
|
||||
yield_orig("FILE_READABLE", command)
|
||||
end
|
||||
dt.control.sleep = function(command)
|
||||
yield_orig("WAIT_MS", command)
|
||||
end
|
||||
end
|
||||
|
||||
-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
|
||||
-- kate: hl Lua;
|
||||
@@ -40,6 +40,10 @@ local gettext = dt.gettext
|
||||
|
||||
gettext.bindtextdomain("executable_manager",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local exec_man = {} -- our own namespace
|
||||
exec_man.module_installed = false
|
||||
exec_man.event_registered = false
|
||||
|
||||
-- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
-- F U N C T I O N S
|
||||
-- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@@ -86,12 +90,30 @@ local function update_combobox_choices(combobox, choice_table, selected)
|
||||
combobox.value = selected
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not exec_man.module_installed then
|
||||
dt.register_lib(
|
||||
"executable_manager", -- Module name
|
||||
"executable manager", -- Visible name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_BOTTOM", 100}}, -- containers
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
exec_man.selector,
|
||||
exec_man.stack,
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
exec_man.module_installed = true
|
||||
end
|
||||
end
|
||||
-- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
-- M A I N P R O G R A M
|
||||
-- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
local exec_man = {} -- our own namespace
|
||||
|
||||
local DARKTABLERC = dt.configuration.config_dir .. PS .. "darktablerc"
|
||||
|
||||
|
||||
@@ -117,8 +139,15 @@ for _,pref in ipairs(matches) do
|
||||
end
|
||||
|
||||
local executable_path_widgets = {}
|
||||
local executable_path_values = {}
|
||||
local placeholder_text = dt.configuration.running_os == windows and _("select an executable") or _("search path for executable")
|
||||
|
||||
for i,exec in ipairs(exec_table) do
|
||||
executable_path_values[exec] = dt.new_widget("entry"){
|
||||
text = df.get_executable_path_preference(exec),
|
||||
placeholder = placeholder_text,
|
||||
editable = false
|
||||
}
|
||||
executable_path_widgets[exec] = dt.new_widget("file_chooser_button"){
|
||||
title = _("select ") .. exec .. _(" executable"),
|
||||
value = df.get_executable_path_preference(exec),
|
||||
@@ -126,6 +155,7 @@ for i,exec in ipairs(exec_table) do
|
||||
changed_callback = function(self)
|
||||
if df.check_if_bin_exists(self.value) then
|
||||
df.set_executable_path_preference(exec, self.value)
|
||||
executable_path_values[exec].text = df.get_executable_path_preference(exec)
|
||||
end
|
||||
end
|
||||
}
|
||||
@@ -155,13 +185,18 @@ exec_man.selector = dt.new_widget("combobox"){
|
||||
|
||||
for i,exec in ipairs(exec_table) do
|
||||
exec_man.stack[i] = dt.new_widget("box"){
|
||||
dt.new_widget("section_label"){label = _("current")},
|
||||
executable_path_values[exec],
|
||||
dt.new_widget("section_label"){label = _("select")},
|
||||
executable_path_widgets[exec],
|
||||
dt.new_widget("section_label"){label = _("reset")},
|
||||
dt.new_widget("button"){
|
||||
label = "clear",
|
||||
tooltip = _("Clear path for ") .. exec,
|
||||
clicked_callback = function()
|
||||
df.set_executable_path_preference(exec, "")
|
||||
executable_path_widgets[exec].value = ""
|
||||
executable_path_values[exec].text = ""
|
||||
end
|
||||
}
|
||||
|
||||
@@ -174,19 +209,18 @@ update_combobox_choices(exec_man.selector, exec_table, 1)
|
||||
-- register the lib
|
||||
|
||||
|
||||
dt.register_lib(
|
||||
"executable_manager", -- Module name
|
||||
"executable manager", -- Visible name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_BOTTOM", 100}}, -- containers
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
exec_man.selector,
|
||||
exec_man.stack,
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not exec_man.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
exec_man.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
local dt = require "darktable"
|
||||
local du = require "lib/dtutils"
|
||||
local df = require "lib/dtutils.file"
|
||||
local ds = require "lib/dtutils.string"
|
||||
local dtsys = require "lib/dtutils.system"
|
||||
|
||||
local gettext = dt.gettext
|
||||
@@ -64,6 +65,7 @@ local CS = dt.configuration.running_os == "windows" and "&" or ";"
|
||||
|
||||
local LUA_DIR = dt.configuration.config_dir .. PS .. "lua"
|
||||
local LUA_SCRIPT_REPO = "https://github.com/darktable-org/lua-scripts.git"
|
||||
local CURR_API_VERSION = dt.configuration.api_version_string
|
||||
|
||||
dt.print_log("LUA_DIR is " .. LUA_DIR)
|
||||
|
||||
@@ -215,7 +217,7 @@ local function scan_scripts()
|
||||
-- scan the scripts
|
||||
local output = io.popen(find_cmd)
|
||||
for line in output:lines() do
|
||||
local l = string.gsub(line, LUA_DIR .. PS, "") -- strip the lua dir off
|
||||
local l = string.gsub(line, ds.sanitize_lua(LUA_DIR) .. PS, "") -- strip the lua dir off
|
||||
local script_file = l:sub(1,-5)
|
||||
if not string.match(script_file, "script_manager") then -- let's not include ourself
|
||||
if not string.match(script_file, "plugins") then -- skip plugins
|
||||
@@ -224,7 +226,7 @@ local function scan_scripts()
|
||||
if not string.match(script_file, "yield") then -- special case, because everything needs this
|
||||
add_script_data(script_file)
|
||||
else
|
||||
prequire(script_file) -- load yield.lua
|
||||
-- prequire(script_file) -- load yield.lua
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -283,7 +285,7 @@ local function deactivate(script, scriptname)
|
||||
end
|
||||
|
||||
local function create_enable_disable_button(btext, sname, req)
|
||||
return dt.new_widget("button")
|
||||
local button = dt.new_widget("button")
|
||||
{
|
||||
label = btext .. sname,
|
||||
tooltip = get_script_doc(req),
|
||||
@@ -309,6 +311,10 @@ local function create_enable_disable_button(btext, sname, req)
|
||||
end
|
||||
end
|
||||
}
|
||||
if CURR_API_VERSION >= "6.0.1" then
|
||||
button.ellipsize = "middle"
|
||||
end
|
||||
return button
|
||||
end
|
||||
|
||||
local function load_script_stack()
|
||||
@@ -449,6 +455,26 @@ local function link_downloads_directory()
|
||||
os.execute("ln -s " .. "$HOME/Downloads " .. LUA_DIR .. "/downloads")
|
||||
end
|
||||
|
||||
local function install_module()
|
||||
if not sm.module_installed then
|
||||
dt.register_lib(
|
||||
"script_manager", -- Module name
|
||||
"script manager", -- Visible name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 0}}, -- containers
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
sm.main_box,
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
sm.module_installed = true
|
||||
end
|
||||
end
|
||||
|
||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||
-- M A I N P R O G R A M
|
||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@@ -465,6 +491,8 @@ sm.script_names = {}
|
||||
sm.script_paths = {}
|
||||
sm.main_menu_choices = {}
|
||||
sm.main_stack_items = {}
|
||||
sm.event_registered = false
|
||||
sm.module_installed = false
|
||||
|
||||
-- see if we've run this before
|
||||
|
||||
@@ -710,6 +738,12 @@ sm.main_stack = dt.new_widget("stack"){
|
||||
table.unpack(sm.main_stack_items),
|
||||
}
|
||||
|
||||
if CURR_API_VERSION >= "6.0.1" then
|
||||
sm.main_stack.h_size_fixed = false
|
||||
sm.main_stack.v_size_fixed = false
|
||||
end
|
||||
|
||||
|
||||
-- make a combobox for the selector
|
||||
|
||||
sm.main_menu = dt.new_widget("combobox"){
|
||||
@@ -741,21 +775,21 @@ sm.main_box = dt.new_widget("box"){
|
||||
-- D A R K T A B L E I N T E G R A T I O N
|
||||
-- - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
-- register the module
|
||||
dt.register_lib(
|
||||
"script_manager", -- Module name
|
||||
"script manager", -- Visible name
|
||||
true, -- expandable
|
||||
false, -- resetable
|
||||
{[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 0}}, -- containers
|
||||
dt.new_widget("box") -- widget
|
||||
{
|
||||
orientation = "vertical",
|
||||
sm.main_box,
|
||||
},
|
||||
nil,-- view_enter
|
||||
nil -- view_leave
|
||||
)
|
||||
if dt.gui.current_view().id == "lighttable" then
|
||||
install_module()
|
||||
else
|
||||
if not sm.event_registered then
|
||||
dt.register_event(
|
||||
"view-changed",
|
||||
function(event, old_view, new_view)
|
||||
if new_view.name == "lighttable" and old_view.name == "darkroom" then
|
||||
install_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
sm.event_registered = true
|
||||
end
|
||||
end
|
||||
|
||||
-- set up the scripts block if we have them otherwise we'll wait until we download them
|
||||
|
||||
|
||||
Reference in New Issue
Block a user