Save captures with custom file name?

Asked by Dr-Z

The sikuli IDE still lacks a bunch of important editing features (such as undo, find, search&replace, ...), so using the sikuli IDE for capturing reference images and one's favorite editor for python programming is probably a good idea.

One major drawback is that the filenames of the captures have seemingly random names, so that the python is hard to read without tons of comments. Of course, one can manually rename the captures, but all references in the scripts need to be renamed as well.

Wouldn't it be great if there were an option to ask for a custom filename after marking a new region in capture mode?

Or has anyone already come up with a good workaround for this problem?

Question information

Language:
English Edit question
Status:
Answered
For:
SikuliX Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:
Revision history for this message
Mike Lee (m-lee) said :
#1

When you capture an image, assign it to a variable like this...

my_name_for_this_image = "53453245345.png"

You can group such images together..

####################################################
# GRAPHICAL CONSTANTS #
####################################################

counter = "53453245345.png"
plus = "53453245346.png"
times = "53453245347.png"
etc..

and even keep them in a separate file as a library

1) Create your 'library script' (perhaps called 'my_lib.sikuli')
   This 'library' is a normal sikuli script and contains all your library "def" code and constants as above

   It is recommended to define all used images as 'graphical constants' (has to be at the beginning of the library script).
   So they can be used easily in more than one function and even in scripts using the library.
   for example...
     main_google_logo = "127927459102.png"
   (make sure the png image exists in your my_lib.sikuli directory before using the library)

   example for a function...
      def click_google() :
       click(main_google_logo)
       return ()

2) Create a script where you want to use the library (perhaps called 'uses_my_lib.sikuli')
   Start your new script with these lines...
     libpath = "<path to your library>" + "\\my_lib.sikuli" # \\ is for windows path
     execfile(libpath + "\\my_lib.py")
     path=getBundlePath() # optional line, saves path of current script
     setBundlePath(libpath)

   Now you can just call your library functions...
     click_google()

...

Revision history for this message
Mike Lee (m-lee) said :
#2

Please let me know if this answers your question !

Revision history for this message
Dr-Z (jmkuhnigk) said :
#3

Although I still think that such an option to name or rename a capture in the IDE would make sense in some cases, I will try out your solution because I like the concept of separating the 'ugly' snippet code (edited in the sikuli IDE) from the test logic (edited in my favorite python editor).
Thanks for the quick help!

Revision history for this message
RaiMan (raimund-hocke) said :
#4

@ Dr-Z
when using Mike Lee's 2) be aware, that images captured while developing the script 'uses_my_lib.sikuli' are stored in 'uses_my_lib.sikuli'. On running the script, they will not be found as long as they are not moved to 'my_lib.sikuli' and implemented as a 'grafical constant'.

This is because after saying setBundlePath(libpath) all find()'s afterwards will look for images that do not have an absolute path in the directory 'libpath'.

@ Mike
when suggesting your solution, you should add this remark.
BTW what about the FAQ?

Revision history for this message
chimanfu (jfu) said :
#5

what if I want to break it down into 3 or more modules, each in separate file, like (image library, function library, main script to execute tests)? Any recommendation for a good test framework structure?

Revision history for this message
RaiMan (raimund-hocke) said :
#6

@ chimanfu

When staying with the Sikuli IDE as development tool, I think in the moment the most flexible way is to use the python execfile to include function libraries, to use setBundlePath() to point to an image library and to have a clear naming convention, that is used to store everything into variables (this means, in your functions and main scripts, you will not see any thumbnails).

--- This is for Mac/Linux. On Windows you have to use \\ in strings containing a path!

--- image library:
create an e.g. myImages.sikuli, that only contains the pictures and the references to them in variables with a naming convention:
appnameContextButtonStart = "filename-of-a-captured-picture"

Since the generated myImages.py will be executed with execfile, you can place some globally used variables containing whatever you need.

Whenever you need more images, edit myImages.sikuli.

To activate the image library in a script you have to say:

libImg = "path-to-myImages.Sikuli/"
execfile( libImg+"myImages.py" ) # to make the image variables known
setBundlePath(libImg) # could also be a statement in myImages.sikuli

instead of the filenames of the captured images itself you wold use the corresponding variables:
click( appnameContextButtonStart )

--- function library
create a myFunctions.sikuli that only contains def()'s and may be some globally used variables.

If you are referencing images in your def()'s, that are not in the image library, you have to reference them with its absolute path, even if they are stored in myFunctions.sikuli, since all images would be loaded from the image library because of setBundlePath().
This can be made flexible by using a global variable that contains the path to myFunctions.sikuli. In a def() you could capture a picture and then edit the reference to:
find(libFunc + "filename-of-a-captured-picture") # libFunc would contain the correct path.

To activate the function library in a script you have to say:

libFunc = "path-to-myFunctions.Sikuli"
execfile( libFunc+"/myFunc.py" ) # to make your functions available

--- So if you have a naming convention, that has its own image/function library according to the top level (e.g.app), this could be very straight forward (not taking care 100% for slashes here!)

app = "toplevel-name"
libPath = "path-to-your-libraries" # contains all library.sikuli's
execfile( libPath + app + "Images.sikuli/" + app + "Images.py" ) # containing setBundlePath()
execfile( libPath + app + "Functions.sikuli/" + app + "Functions.py" )

This could even be made more flexible by using command line arguments and Env.getOS().

Once it is running, you could pack everything together in one .sikuli using subdirectories for the libraries. The only statement, that would have to be changed would be libPath=
libPath = getBundlePath() # path to the running script
even this could be evaluated by looking for libraries in the script path: if present, these will be used.

Having everything in a .skl (a zipped .sikuli) would not work this way (not evaluated yet).

The other downside in the moment is, that you cannot tell the IDE, where to store your captured pictures. I hope this will be added to the IDE preferences some day together with a preset/option for the naming pattern.

Revision history for this message
Bruno Vieira (giuliapo) said :
#7

Hello all.

I like the idea of separating the images, the functions and the logic in different files. But I have a question: how do you handle the similirity property of the images?

For instance, I have two variables with the name of logos:
ie_google_logo = "1285226229228.png"
ie_bing_logo = find(Pattern("1285226251979.png").similar(0.75))

In the case of the ie_google_logo, I'm following the idea of having the name in the var to be used on the functions file.
In the case of the ie_bing_logo, I would like to use the "similar" property.

How to handle this? If the find command stays in this image library, it will be executed when it's called from the functions file, which we don't want.

Advises?
Thanks,
Bruno

Revision history for this message
RaiMan (raimund-hocke) said :
#8

When you want to use this approach, you have to say:

ie_google_logo = "1285226229228.png"
ie_bing_logo = Pattern("1285226251979.png").similar(0.75)

and somewhere else:
find(ie_bing_logo)

--- or you say:
ie_google_logo = "1285226229228.png"
ie_bing_logo = "1285226251979.png"

and somewhere else:
find(Pattern(ie_bing_logo).similar(0.75))

This depends on what you think is stable:
- in case 1 the combination image + similarity is the "constant" represented by a Pattern object.
- in case 2 only the image is the "constant", since you want to play around in your code with the similarity

In your case, I guess it is the first one.

--- Tip: extend your naming convention to make things clear:
ie_bing_logo_p = Pattern("1285226251979.png").similar(0.75)
this may signal, that the variable contains a pattern and not only an image reference.

--- one more thing: even in case 1 you can do things like case 2:
find(ie_bing_logo_p.similar(0.8))
which makes a new Pattern object on base of the existing one.

Revision history for this message
Hari Kiran Vankayala (harikiran1985) said :
#9

Can you please let me know how can i navigate to the path of the captured images.
Or is there any settings script need to be run to change the default captured path using Capture method in Screen class?

Thanks,
Hari.

Revision history for this message
fani (sagerer) said :
#10

Hey,

sorry, but I don't really get how this solves the problem... If I capture a shot and shutil.copy() it in the destination directory the name is random. So how can I give the shot a name in the sikuli IDE?

Cheers,

Stefanie

Revision history for this message
RaiMan (raimund-hocke) said :
#11

@ Stefanie
If you ask questions by commenting a question, you should subscribe to the question, to get noticed about possible answers (I did it for you this time ;-)

In the IDE you can name images after having them captured using the Preview pane (click on image thumbnail).

In a script with dynamic capturing, you might do it this way:

targetdir ="some existing directory"
img = capture() # temp file name
shutil.copy(img, os.path.join(targetdir, "newname.png")

Can you help with this problem?

Provide an answer of your own, or ask Dr-Z for more information if necessary.

To post a message you must log in.