Merge branch 'master' into fix_check_if_bin_exists_and_executable_paths

This commit is contained in:
wpferguson
2020-12-24 09:54:29 -05:00
committed by GitHub
38 changed files with 2347 additions and 451 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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"), "")

View File

@@ -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")

View File

@@ -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)

View File

@@ -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")

View File

@@ -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",

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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)

View File

@@ -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")

View File

@@ -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

View File

@@ -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"] = {

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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]],

View File

@@ -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"

View 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."

View File

@@ -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")

View File

@@ -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")

View File

@@ -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);

View File

@@ -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")

View File

@@ -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;

View File

@@ -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

View File

@@ -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