Can Sikuli search the sub-folders to find screenshots?

Asked by Parn Yin

For example, suppose I have put some screenshots under different folders under "C:\Test":

C:\Test\Cat
C:\Test\Login
C:\Test\Module1
C:\Test\Module2
...

Then I use setBundlePath to set the C:\Test as my screenshot folder, but Sikuli can't find the screenshots under "C:\Test\Login".
(1) Excuse me, is there a way to make it work for sub-folders?
If not, should I change the screenshot folder before using different Sikuli scripts?
I don't want to add all possible paths into BundlePath because I guess this may affect the searching performance. Am I right?

By the way, I have another question:
(2)
I've read this: http://doc.sikuli.org/globals.html#getImagePath
However, I am still not clear about the difference between ImagePath and BundlePath.
Excuse me, when should I use ImagePath and when should I use BundlePath?

Thank you.

Question information

Language:
English Edit question
Status:
Solved
For:
SikuliX Edit question
Assignee:
RaiMan Edit question
Solved by:
RaiMan
Solved:
Last query:
Last reply:
Revision history for this message
Best RaiMan (raimund-hocke) said :
#1

- the standard place, Sikuli looks for given images is in the bundle path, which is the folder of the source (xxx.sikuli)
- you can get this folder by using getBundlePath() (e.g. to store it for later restore, or just to know where you are)
- you can always set the bundle path to any other folder at runtime. Sikuli will search there for images from now on until another bundle path is set

So the bundle path is the one place, where Sikuli searches for images at any time and it must point to the folder, taht contains the image files (Sikuli does not look into subfolders).

The image path is a collection of more than one path (actually the bundle path is image path[0], since Sikuli internally always looks for images along this path (which is the reason for the "automagic" import of .sikuli and still finding all images)).

So the image path is a list of places, that is searched for images until one is found in one of these places (first found wins, so sequence of paths is relevant and you need some naming convention)

So in your case it depends on the usage of the subfolders, what is the best choice:
- sequential usage: setBundlePath()
- concurrent usage: use image path feature

But I just thought, it might be a good idea, to allow one folder to be set as bundle path and Sikuli will search all contained subfolders for an image file. I will put it on the request list.
e.g. setBundlePath(some_folder, tree=true)

Revision history for this message
Parn Yin (pyin) said :
#2

Hi RaiMan, I see.
Thank you for the quick and detailed explanation!!!

By the way, I have another python question.
Sorry, I just have been started to learn python since last month...

In my automation test, I created 2 functions to set the module path and the BundlePath, which use the path as parameter.
For example, the one for BundlePath:

def set_screenshot_path(my_screenshot_path):
    if not os.path.isdir(my_screenshot_path):
        print ("[EXCEPTION] " + my_screenshot_path + " does not exist.")
        raise Exception
    print ("The screenshot path is " + str(getBundlePath()))
    if not my_screenshot_path in str(getBundlePath()):
        setBundlePath(my_screenshot_path)
        print ("[SETTING] Change the screenshot path to my_screenshot_path.")
        print ("The screenshot path is " + str(getBundlePath()))

Then there is a problem about escape character.
For example, if the user assign "C:\test\new" as the my_screenshot_path, the path can't be get correctly becuase of the "\t" and "\n".

Is there a way to read the raw input from it?
I searched the google but failed to find a good solution.
e.g. there is one but it has no answer:
http://stackoverflow.com/questions/9457676/python-escape-character-when-parameter-is-a-path-windows

Excuse me RaiMan, could you please kindly help me when you have time?
Thank you.

Revision history for this message
Parn Yin (pyin) said :
#3

Excuse me RaiMan, could you please kindly help me when you have time?
Thank you.

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

Since getBundlPath returns a string, str() is not needed:

your def() a little bit pythonized ;-)

def set_screenshot_path(my_screenshot_path):
    print "The current screenshot path is " + getBundlePath()
    if not os.path.isdir(my_screenshot_path):
        raise Exception("%s does not exist."%my_screenshot_path)
    if not my_screenshot_path == getBundlePath(): # in does not filter subfolders
        print "[SETTING] Changing the screenshot path from %s to %s"%(getBundlePath(), my_screenshot_path)
        setBundlePath(my_screenshot_path)

BTW: raise in functions only makes sense, if you really want to stop the complete execution here. Otherwise the functions should return true/false or value/none (like Sikuli's exists() )

def set_screenshot_path(my_screenshot_path):
    print "[INFO] The current screenshot path is " + getBundlePath()
    if not os.path.isdir(my_screenshot_path):
        print "[ERROR] %s does not exist."%my_screenshot_path
        return None
    if not my_screenshot_path == getBundlePath(): # in does not filter subfolders
        print "[SETTING] Changing the screenshot path from %s to %s"%(getBundlePath(), my_screenshot_path)
        setBundlePath(my_screenshot_path)
        return getBundlePath()

if not set_screenshot_path(new_screenshot_path):
    print "[FATAL] terminating"
    exit(1)
    # or any corrective action

*** your problem does ...
is not your def, but at the point, where the new_screenshot_path is set.

set_screenshot_path("c:\\test")

or

set_screenshot_path(r"c:\test")

the interpretation of escape characters only takes place when Python has to "read" a literal "some text"

Revision history for this message
Parn Yin (pyin) said :
#5

Yeah, pythonized... thank you RaiMan!

In fact I don't have any programming experience before studying Python.
What you taught me help me to think more like a programmer, make my project better and better.
I've learned a lot from you!(bow)

For the problem I mentioned above, I need to describe the background.
I'm developing the automation test project but other guys will use it also.
They will copy the project to their machine and run it.
But they may not copy it to the same place as mine, so that they will use "set_screenshot_path" to set their path.

Just like a software or a tool in real life, the users would not be comfortable if you ask them to follow some specail rules.
If they can just copy their directory path(e.g.C:\test\new) from Windows Explorer's address bar and paste it to use, they will be happy.
If I need to tell them "Remember to use '\\' or 'r' otherwise it may not work sometimes", they will complain and take out the guns and saws...:)

So, I want to find a way to treat the parameter "my_screenshot_path" as raw input, it would be better than telling other guys rules and ask them to remember.

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

Totally agreed.

But since we are scripting/programming with a capable system/framework, there are other means to make your script independent of its location in the file system.

If you tell your people "just put your script where you want, this location will be taken as the place to collect the screenshots", then you can do the following:

import os
scriptDir = getBundlePath() # this is the path to script.sikuli
screenShotDir = os.path.dirname(d) # this is the folder containing script.sikuli

Revision history for this message
Parn Yin (pyin) said :
#7

I still can't get the expected result I want.
But I found another stupid way to fix that:

def set_screenshot_path(my_screenshot_path):
    my_screenshot_path = repr(my_screenshot_path)
    my_screenshot_path = str(my_screenshot_path.replace("\\\\", "\\"))
    my_screenshot_path = my_screenshot_path.strip("'")

And I found str() is needed for getBundlPath in
    print ("[INFO] The current screenshot path is " + str(getBundlePath()))
otherwise eclipse will say:
    TypeError: cannot concatenate 'str' and 'NoneType' objects
So I will still use str().

You really helped me a lot!
Thank you very much RaiMan!
Have a good day!

Revision history for this message
Parn Yin (pyin) said :
#8

Thanks RaiMan, that solved my question.

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

Uuups, that looks really strange ;-)

again pythonized:

assuming my_screenshot_path is a global variable:

def set_screenshot_path(path):
    global my_screenshot_path
    my_screenshot_path = repr(path).replace(r"\\", r"\").strip("'")

this reduces all eventually given \\ or \\\\ \\\\\\ or ... to \

But with one exception: \t or \n or \r in the path will be converted to real tab, newline or carriage-return characters by repr().
This is only a selection of common escaped characters
(a bunch more: http://docs.python.org/2/reference/lexical_analysis.html#string-literals)

As long as all folder/subfolder names start with capital letters - no problem.

So if you say, your users will copy the path string from the explorer to the script, then you might have this problem.
On the other hand, if they do so, the string will never contain \\ internally, because one would have to say either

"c:\\\\Test\\\\Folder"

or

r"c:\\Test\\Folder"

So if you do not tell them, they would not do it and someone who knows Python will not do it either.

So if you tell them: copy the path from explorer to the script and have it enclosed with " or '

you will get:
"c:\Test\Folder"

in the script, which will internally be
c:\Test\Folder

which is fine.

So if you want to be on the safe side with this concept (letting people copy the path string into the script), then you have to take care for the escape problem.

Depending on how they might use your scripts, it might be a solution, to get the path as a runtime command line parameter.

Still the easiest way is to use some rule for the screen-shot-path based on the location of the script (again easiest: same folder as the script)

BTW: getBundlePath() in fact returns a String and does not need a str() and this might only be complained by Eclipse at edit time, since it does not know the method getBundlePath().

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

sorry, forgot to correct:

    my_screenshot_path = repr(path).replace("\\\\", "\\").strip("'")

raw strings r"" cannot have trailing \

Revision history for this message
Parn Yin (pyin) said :
#11

Thanks again, RaiMan!