Weird behaviour with dialog boxes and variables in Lua script for Lightroom plugin - plugins

I'm writing a Lightroom plugin using the Lightroom SDK/API in the Lua language. I'm new to Lua. I've found a situation where my script only works if a Lightroom dialog box (LrDialogs.message("random message")) is present in one function. Without it the function falls over at a later point claiming a string variable (Image.dr in the last LrDialogs.message) is 'nil' as opposed to the normal value it has when the plugin is working properly. Anyone know what's going wrong? Here's the relevant code segment:
------ read output file for exif and write to LR metadata ------
function parseOutput(outputFilePath)
LrDialogs.message("random message")
local tblOutput = {} --to hold the output exif (1 column table, i.e. an array)
local tblImages = {} --to hold the images and their relevant metadata
for line in io.lines(outputFilePath) do
line = removeWhitespaces(line)
table.insert(tblOutput, line)
end
local str = table.remove(tblOutput) --remove last line in table/file (it's log info, not exif)
tblImages = extractExif(tblOutput) --pick out the exif key/value pairs and add to Image objects
end
function extractExif(tblOutput)
local Image = {} --pseudo object to hold metadata for each image
local tblImages = {}
local blnFlag = false
local intCount = 0
for k,v in pairs(tblOutput) do --iterate through each value in the table
if string.find(v, "^=.+") then
--test if new image other than the first one
if blnFlag == true then
--add Image to tblImages and then clear Image object
table.insert(tblImages, Image)
--Image = {} --don't technically need this
blnFlag = false
--LrDialogs.message("inside blnFlag test")
end
i, j = string.find(v, "/") -- **** MAC ONLY. Back slash for Windows *****
Image.filePath = string.sub(v, i) --returns the file path
Image.name = string.match(v, "([^/]+)$") --return the file name
blnFlag = true
elseif string.find(v, "ISO") ~= nil then
Image.iso = string.match(v, "%a+:(.+)") --get text (i.e value) to right of colon
elseif string.find(v, "Film") ~= nil then
Image.filmSim = string.match(v, "%a+:(.+)")
elseif string.find(v, "Setting") ~= nil then
Image.drMode = string.match(v, "%a+:(.+)")
elseif (string.find(v, "Auto") ~= nil) or (string.find(v, "Development") ~= nil) then
Image.dr = string.match(v, "%a+:(.+)")
else
end
end
LrDialogs.message(Image.name .. Image.iso .. Image.filmSim .. Image.drMode .. Image.dr)
return tblImages
end
function removeWhitespaces(str)
return string.gsub(str, "%s", "")
end

Related

How do I prevent hitting the recursion limit in roblox?

So I'm making an obfuscator that basically generates a bunch of variables with the byte value of a char.
In the end I have a loadstring that basically combines all the strings into one string.
Now, that works fine with small scripts, but its really when you get big scripts.
It hits the recursion limit.
Here's the code for the obfuscator rn
scr = [[
loadstring(game:HttpGet("https://raw.githubusercontent.com/python1111111111111/mosshub/main/main.lua"))()
print("loaded")
]]
local random = Random.new()
local letters = {'i','I','l','_','shus', 'a'}
function getRandomLetter()
return letters[random:NextInteger(1,#letters)]
end
function getRandomString(length, includeCapitals)
local length = length or 10
local str = ''
for i=1,length do
local randomLetter = getRandomLetter()
if includeCapitals and random:NextNumber() > .5 then
randomLetter = string.upper(randomLetter)
end
str = str .. randomLetter
end
return str
end
crypt = ""
bytes = ""
vars = {}
function c()
for b = 1,scr:len() do
var = getRandomString(250,false)
crypt = crypt..var.."="..""..string.byte(string.sub(scr,b,b))..";"
table.insert(vars, var)
end
crypt = crypt.."loadstring("
for i,v in pairs(vars) do
crypt = crypt.."string.char("..v..").."
end
crypt = crypt.."\"\")()"
end
c()
print(crypt)
setclipboard(crypt)

How do I change a value in the leaderstats via touching a part? (Roblox)

Here's the error and what I've tried.
Error: Workspace.Part.Script:4: attempt to index nil with 'leaderstats'
My code:
Making Leaderstats:
game.Players.PlayerAdded:Connect(function(plr)
local ls = Instance.new("Model")
ls.Name = "leaderstats"
ls.Parent = plr
local m = Instance.new("IntValue")
m.Name = "Stars"
m.Value = 0
m.Parent = ls
end)
Touch Code:
local collected = false
script.Parent.Touched:Connect(function()
if collected == false then
game.Players.LocalPlayer.leaderstats.Stars.Value = game.Players.LocalPlayer.leaderstats.Stars.Value + 1
end
collected = true
end)
A server script would suit your need better. In a localscript, the change would only appear for the player. Also, it's better practice to use the Players service offered by Roblox. Here's an example:
local Players = game:GetService('Players')
local collected = false
script.Parent.Touched:Connect(function(partTouched)
if partTouched.Parent:IsA('Model') and partTouched.Parent:FindFirstChild('Humanoid') and partTouched:IsA('BasePart') then -- Is this a player? If so, then..
local player = Players:GetPlayerFromCharacter(partTouched.Parent)
if collected == false then
player.leaderstats.Stars.Value += 1 -- shorter syntax
end
collected = true
--script.Parent:Destroy() optional if this part won't be used again.
end
end)
If you're planning to use this for many parts, using a central ModuleScript would save you lots of changing things back and forth: https://developer.roblox.com/en-us/api-reference/class/ModuleScript

How do you order annotations by offset in brat?

When using the rapid annotator tool brat, it appears that the created annotations file will present the annotation in the order that the annotations were performed by the user. If you start at the beginning of a document and go the end performing annotation, then the annotations will naturally be in the correct offset order. However, if you need to go earlier in the document and add another annotation, the offset order of the annotations in the output .ann file will be out of order.
How then can you rearrange the .ann file such that the annotations are in offset order when you are done? Is there some option within brat that allows you to do this or is it something that one has to write their own script to perform?
Hearing nothing, I did write a python script to accomplish what I had set out to do. First, I reorder all annotations by begin index. Secondly, I resequence the label numbers so that they are once again in ascending order.
import optparse, sys
splitchar1 = '\t'
splitchar2 = ' '
# for brat, overlapped is not permitted (or at least a warning is generated)
# we could use this simplification in sorting by simply sorting on begin. it is
# probably a good idea anyway.
class AnnotationRecord:
label = 'T0'
type = ''
begin = -1
end = -1
text = ''
def __repr__(self):
return self.label + splitchar1
+ self.type + splitchar2
+ str(self.begin) + splitchar2
+ str(self.end) + splitchar1 + self.text
def create_record(parts):
record = AnnotationRecord()
record.label = parts[0]
middle_parts = parts[1].split(splitchar2)
record.type = middle_parts[0]
record.begin = middle_parts[1]
record.end = middle_parts[2]
record.text = parts[2]
return record
def main(filename, out_filename):
fo = open(filename, 'r')
lines = fo.readlines()
fo.close()
annotation_records = []
for line in lines:
parts = line.split(splitchar1)
annotation_records.append(create_record(parts))
# sort based upon begin
sorted_records = sorted(annotation_records, key=lambda a: int(a.begin))
# now relabel based upon the sorted order
label_value = 1
for sorted_record in sorted_records:
sorted_record.label = 'T' + str(label_value)
label_value += 1
# now write the resulting file to disk
fo = open(out_filename, 'w')
for sorted_record in sorted_records:
fo.write(sorted_record.__repr__())
fo.close()
#format of .ann file is T# Type Start End Text
#args are input file, output file
if __name__ == '__main__':
parser = optparse.OptionParser(formatter=optparse.TitledHelpFormatter(),
usage=globals()['__doc__'],
version='$Id$')
parser.add_option ('-v', '--verbose', action='store_true',
default=False, help='verbose output')
(options, args) = parser.parse_args()
if len(args) < 2:
parser.error ('missing argument')
main(args[0], args[1])
sys.exit(0)

Class specific data files in matlab

I'm writing a matlab class which uses data from a .mat-file.
Is there any way I can place this data file inside the class directory structure and make it accessible only to my class?
I.e. I have a directory structure like this:
./MATLAB
+myPackage
#MyClass
MyClass.m
where MyClass.m looks like
classdef MyClass
methods
function y = getValue(obj, x)
load datafile.mat
y = interp1(datax, datay, x);
end
end
end
This works great as long as datafile.mat is in my working directory, but I'd like to move it to +myPackage/#MyClass/datafile.mat instead (or better yet +myPackage/datafile.mat). How would I make my class(es) find it there?
First, you may put the data file in a 'private' subfolder inside your directory structure:
./MATLAB
+myPackage
private
datafile.mat
#MyClass
MyClass.m
NB: This won't garantee that it is only accessible to your class, anyway it will be classified close to your class definition.
Then, you can find back data values like this in your method:
classdef MyClass
methods
function y = getValue(obj, x)
% Find back datafile name
localPath = fileparts(mfilename('fullpath'));
dataFilename = fullfile(localpath, '..', 'private', 'datafile.mat'); % '..' is to go up one folder, up to you to place th file somewhere else (e.g. under +MyPackage directly)
% Read file
data = load(dataFilename);
datax = data.datax;
datay = data.datay;
y = interp1(datax, datay, x);
end
end
end
EDIT: Example with Resources class
Here is an example with separate resource class to avoid loading file data each time getValue is called. I also added for the case you need to care if the file has been modified or not:
classdef Resources
methods(Static)
function [resource] = GetResource(name, careForUpdates)
%[
if (nargin < 2), careForUpdates = true; end
if (nargin < 1), error('Not enough input arguments.'); end
persistent cache;
if (isempty(cache)), cache = containers.Map('UniformValues', false); end
filename = Resources.GetFileName(name);
lastModified = dir(filename);
lastModified = datenum(lastModified.date);
if (~cache.isKey(name) || ...
(careForUpdates && (cache(name).LastModified < lastModified)))
data.LastModified = lastModified;
data.Value = load(filename);
cache(name) = data;
end
resource = cache(name).Value;
%]
end
end
methods(Static, Access=private)
function [fname] = GetFileName(name)
%[
persistent cache;
if (isempty(cache)), cache = containers.Map('UniformValues', false); end
if (~cache.isKey(name))
localpath = fileparts(mfilename('fullpath'));
switch(name)
case 'titi', cache(name) = fullfile(localpath, 'titi.mat');
case 'toto', cache(name) = fullfile(localpath, 'toto.mat');
case 'tata', cache(name) = fullfile(localpath, 'tata.mat');
otherwise, error('Resource `%s` is unknown', name)
end
end
fname = cache(name);
%]
end
end
end
This can be used like this
data = Resources.Get('titi');
Or, if not interested by file updates:
careForFileUpdates = false;
data = Resources.Get('titi', careForFileUpdates);

How to sort property -value pair in alphabetical order with Matlab

I want to add a property-value pair to existing file. In the mean time all the properties should be ordered in alphabetical order. For example :
[Info] % property 1
value 1
[system] % property 2
value 2
How can i add additional property such that all properties will be sorted in alphabetical order. I was able to add property -value pair to the end of the file using
fh = fopen(filename,'a') but i am not able to sort them alphabetically.
so far i tried this as follows but with this one it keeps printing only the new property-value pair . I want to print remaining properties onces it prints the new one.
function [] = myfun(filename ,propName,propvalue)
rfh = fopen(filename,'r');
tname = tempname();
wfh = fopen(tname,'w');
line = fgetl(rfh);
while ischar(line)
if (line(1) == '[') && (line(end) == ']')
property = lower(line(2:end-1)) % from ini file
String2 = property;
String1 = propName;
[sat] = sor(String1,String2)% subfunction
if sat == -1
fprintf(wfh,'[%s]\r\n%s\r\n',propName,propvalue);
else
fprintf(wfh,'%s\r\n',line);
end
else
fprintf(wfh,'%s\r\n',line);
end
line = fgetl(rfh);
end
fclose(rfh);
fclose(wfh);
movefile(tname,filename,'f')
function [sat] = sor(String1,String2)
Index = 1;
while Index < length(String1) && Index < length(String2) && String1(Index) == String2(Index)
Index = Index + 1;
end
% Return the appropriate code
if String1(Index) < String2(Index)
sat= -1
elseif String1(Index) > String2(Index)
sat= +1
else % the characters at this position are equal -- the shorter of the two strings should be "less than"
if length(String1) == length(String2)
sat = 0
elseif length(String1) < length(String2)
sat = -1
else
sat = +1
end
end
Is this a .ini file? You might want to take a look at INIConfig from the MATLAB File Exchange, a set of routines for handling INI files arranged in a convenient class. I haven't used it, but perhaps it might do what you need.
If not, you can always:
Read in the file
Loop through it line by line
When you find a line starting with [ followed by a word alphabetically later than the property you'd like to insert, insert your property and value
Include the remainder of the file
Write the whole file back out again.
How about read the file into a struct?
function fileData = readFileIntoStruct( fileName )
%
% read [property] value pairs file into struct
%
fh = fopen( fileName, 'r' ); % read handle
line = fgetl( fh );
while ischar( line )
% property
tkn = regexp( line, '\[([^\]+)]\]', 'once', 'tokens' );
% read next line for value
val = fgetl( fh );
fileDate.(tkn{1}) = val;
line = fgetl( fh ); % keep reading
end
fclose( fh ); % don't forget to close the file at the end.
Now you have all the data as a struct with properties as fieldnames and values as the field value.
Now you can update a property simply by:
function fileData = updateProperty( fileData, propName, newVal )
if isfield( fileData, propName )
fileData.(propName) = newVal;
else
warning( 'property %s does not exist - please add it first', propName );
end
You can add a property:
function fileData = addProperty( fileData, propName, newVal )
if ~isfield( fileData, propName )
fileData.(propName) = newVal;
else
warning ( 'property %s already exists, use update to change its value', propName );
end
You can sort the properties alphabetically using orderfields:
fileData = orderfields( fileData );
You can write the struct back to file simply using:
function writeDataToFile( newFileName, fileData )
fopen( newFileName , 'w' ); %write handle
propNames = fieldnames( fileData );
for ii = 1:numel( propNames )
fprintf( fh, '[%s]\r\n%s\r\n', propNames{ii}, fileData.(propNames{ii}) );
end
fclose( fh );
Assumptions:
The properties' names are legitimate Matlab field names (see variable naming for details).
The value of each property is always a string.
I did not include any error-checking code in these examples (files not found, wrongly formatted strings, etc.)
I assume the input file is strictly "[prop] val" pairs without any additional comments etc.