IDE: imported helper.py files are not automatically reimported if changed

Asked by SeltiX on 2020-02-25

****** the fix of bugs and enhancements are tracked here:
https://github.com/RaiMan/SikuliX1/issues/315

****** workaround
use .sikuli scripts until fixed (see comment #12)
---------------------------------------------------------------------------------------------

I just start using Sikulix and I just have a simple Main.py file and a second Helper.py. If I Run my code from my Main file Sikulix does not use my last version of Helper.py (even if I save). It's like is using some cache of it. If I close and open Sikulix and try to Run it will work as expected.

Question information

Language:
English Edit question
Status:
Answered
For:
Sikuli Edit question
Assignee:
No assignee Edit question
Last query:
2020-02-25
Last reply:
2020-03-01
RaiMan (raimund-hocke) said : #2

Running SikuliX 2.0.3?
Do you have a Python background? It is not a Python interpreter, it is Jython. The language is at level 2.7.

The IDE as such runs with one global Jython interpreter instance and hence an import is only done once.
If you use the scripts as .sikuli, there is an auto-reimport feature (so you might switch to .sikuli).
If your helper.py does not have any global side-effects, you can use reload("helper.py") (which is done for .sikuli automatically).
I will register an enhancement request to implement the auto-reimport for plain .py files as well (2.0.4)

If you run your stuff from commandline, it will work as expected.

So you might do your edits in the IDE and run the main.py from commandline in parallel (be aware: some seconds startup time per run)

SeltiX (seltix-) said : #3

Yes its sikulix jar 2.0.3.
I dont know what you mean with python background. If you're asking about my knolage its basic, I normal work with PHP and Javascript.
I try the windows sikuli IDE but it does not let me open other py files in the same directory, I then switch back to jar version.
Yes, if I run the py file from Cmd it works as expected.
I notice the same append with imagens, if I change imagens and maintain the same name the new image is not loaded.
I dont know if that append with the imagens because im using the Pattern command to manually specific the image path to a subfolder since I did not find any way in the IDE to allow me to organize all imagens inside a folder.

RaiMan (raimund-hocke) said : #4

So you are not a Python expert.

In this case I heavily recommend to not work with plain .py files, but with the standard .sikuli folders.

Then you do not have any problems with imports (like the above mentioned) and the .sikuli folder is the standard place for the images used in this script.

If you want to organise your images in one place, then this is the recommended way:
- create a script that only contains images
- when capturing a new image, use the imageName = <capture> convenience, which captures and names the image in one step.
- this images.sikuli simply import in your scripts, that need any images and simply use the name like "imageName" - the images will be found auto-magically (reason behind: an imported .sikuli will also be on the ImagePath automatically)
Take care: have a naming convention for your images to avoid name clashes.

SeltiX (seltix-) said : #5

I did not really understand what I should do :S is there any information I can read? I cant find anything online.
sorry for asking :O

SeltiX (seltix-) said : #6

ok I think I found how to do it, with the help of this info : http://doc.sikuli.org/globals.html

for others who may search for this, my setup to have the images in a dedicated folder with sikulix-2.0.3.jar is :

├── myapp.sikuli/
│ ├── myapp.py
│ ├── helpers.sikuli/
│ │ ├── helpers.py
│ ├── images.sikuli/
│ │ ├── images.py
│ │ ├── all my image files....

>>> myapp.py :

import os

myPath = os.path.dirname(getBundlePath())
if not "helpers" in sys.path: sys.path.append("helpers")
if not "images" in sys.path: sys.path.append("images")

import helpers
import images

reload(helpers)
reload(images)
...

>>> helpers.py and images.py
from sikuli import *
...

im I doing it correctly?
Thanks!

RaiMan (raimund-hocke) said : #11

I am really sorry for all the buzz.

The solution mentioned in comment #6 works.

My problem when testing was

from helpers import *

instead of

import helpers
from helpers import *

For the automatics, a plain import is needed (from xxx import * alone does not work).

RaiMan (raimund-hocke) said : #12

the folder structure should be like this:

├── myapp.sikuli/
│ ├── myapp.py

  |── helpers.sikuli/
│ ├── helpers.py

 |── images.sikuli/
│ ├── images.py
│ ├── all my image files....

... all .sikuli should be in the same folder (more convenient and supported by automatics)

>>> myapp.py :

# no path handling needed - automatic

import helpers
import images

# reload is not needed for .sikuli (automatic)
#reload(helpers)
#reload(images)

>>> helpers.py (1st line)
from sikuli import *

... in images.py only needed if any SikuliX features are used, hence not needed if only images are defined.

RaiMan (raimund-hocke) said : #13

I will post a solution with plain Python files shortly

RaiMan (raimund-hocke) said : #14

works with SikuliX 2.0.x

----------------------------- main.py
# this is a solution with plain .py files
# this is main.py

# add the path to import images.py
imagePath = makePath(getBundlePath(), "images")
if not imagePath in sys.path: sys.path.append(imagePath)

# add the path to ImagePath to access the images
ImagePath.add(imagePath)

# images.py is in subfolder images
# which is in same folder as main.py
import images
reload(images)
from images import *

# helper.py is in same folder as main.py
import helper
reload(helper)
from helper import *

# this would/should be sufficient
# if the auto-re-import also works with plain .py files
import images
from images import *
import helper
from helper import *

print "from main"
for e in sys.path: print e

callMe() # in helper

# all three print: image1.png: (130x31)
print Image.create(image1)
print Image.create("image1")
print Image.create("image1.png")

# as long as image2 is not defined in images.py
#this gives: [error] NameError ( name 'image2' is not defined )
#print Image.create(image2) # uncomment to use

#this gives image2.png: (0x0)
#[error] ImagePath: find: not there: image2.png
#print Image.create("image2") # uncomment to use

---------------------------- helper .py
from sikuli import *

def callMe():
  print "from help"

--------------------------- folder images
image1.png

# images.py
image1 = "image1.png"

#if uncommented, image2 will be known
#image2 = image1

SeltiX (seltix-) said : #15

hello again,

i sucefully structured the project as you said, just resuming :

├── myapp.sikuli/
│ ├── myapp.py

  |── helpers.sikuli/
│ ├── helpers.py

 |── images.sikuli/
│ ├── images.py
│ ├── all my image files....

# myapp.py :

import helpers
import images
...
pattern = images.getImageByName("...")
example = helpers.functionName()

# helpers.py ( used in myapp.py and images.py )

(nothing to import)

# images.py ( used in myapp.py to return Pattern() images by name )
import helpers
from sikuli import *
...
example = helpers.functionName()

RESULT :
- the imported files are now automatically reloading on each execution as expected
- the images still does not realod if replaced ( only after close/open sikulix-2.0.3.jar) : this reload is important when youre tweaking the image with editor

I also already try the final step of "export as jar" (since I want to share my project with my "normal users" work friends and for that I wold need some type of EXE or a "one file" Java executable (for simplity of sharing and for code protection), and after reading this (https://answers.launchpad.net/sikuli/+question/688604) I have a question and another problem :

- you said the jar file is just a zip-container but after opening it with 7zip all the files are encoded, I supose they are not reversible right?
- the "java -jar" command works but only when there is no imports like mine. If I import "helpers" or "images" the jar will not work :
[error] script [ gogbot$py.class ] stopped with error at line --unknown--
[error] --UnKnown-- ( --UnKnown-- )

( btw, the "Export as jar" options still shows a popup at the end with "not set" text )

RaiMan (raimund-hocke) said : #16

--- - the images still does not realod if replaced ( only after close/open sikulix-2.0.3.jar) : this reload is important when youre tweaking the image with editor

The images are cached internally and not reloaded automatically if changed.

You might switch off cashing (Settings.setImageCach(0))

... or use Image.reset() (clears the cache) when needed.

--- exported jar
yes, the .py are compiled into a Jython-internal format, that is not human-readable.
Wether they are not "reversible" with appropriate tools I cannot tell you, never checked that.
But google is your friend.

--- imports do not work
I will check and come back later hopefully with a solution.

RaiMan (raimund-hocke) said : #17

sorry, typo:

You might switch off cashing (Settings.setImageCache(0))

RaiMan (raimund-hocke) said : #18

thanks for the pointer with the empty popup: fixed in 2.0.4

RaiMan (raimund-hocke) said : #19

This is a temporary solution until it works with plain .py files as well.

For development use the working .sikuli structure (what you already do in the IDE).

In main.py add before the imports (it does nothing during development, but does the trick when running the jar)

if not getBundlePath() in sys.path: sys.path.insert(0, getBundlePath())

For distribution make an extra folder containing (copies!):

- main.sikuli
  - main.py
  - helper.py
  - images.py
  - copies of all images from images.sikuli

open this main.sikuli in the IDE (to not get confused no other tabs, only this one)

run exportAsJar from the file menu, which should produce the main_sikuli.jar.

Now you can test the jar:

java -jar <path-to>\sikulix.jar -r <path-to>\main_sikuli.jar.

The magic behind:
- helper .py are created and compiled (__main__.py and __run__.py)
- all .py files are compiled to the internal form
- the stuff is packed into the jar
- when running the jar:
   - everything is exported to a temp-folder
   - now the temp-folder is the bundle-path (but not added to sys.path automatically)
   - hence the one-liner above does the trick

Hope it helps

SeltiX (seltix-) said : #20

thanks again!

ok i will just use the reset for development :

if debug:
    Image.reset()

about the jar file, after your previus reply I start testing and I start by creating the standalone jar version :

 java -jar .\sikulixsetup-1.1.3.jar scriptjar .\myapp.sikuli\

I then open the resulting file "myapp_sikuli.jar" with 7zip and manually added the "helpers$py.class" and "images$py.class" generated by "sikulix-2.0.3.jar" when running the code plus all the images to the root of the jar file ( where the "myapp$py.class" is located )
I then run the jar with :

 java -jar .\myapp_sikuli.jar

it now detects the imported files but something is not working correctly with the regions, I have this line :

self.region = Region(self.region).offset(0, 30).setH(30)

this work as expected in the IDE but here it returns null... its something in the "setH()" because it works if I remove it...

in the end I created this .bat file to automate the compile process :

java -jar .\_build\sikulixsetup-1.1.3.jar scriptjar .\myapp.sikuli\

"%programfiles%\7-Zip\7z" a myapp_sikuli.jar .\helpers.sikuli\helpers$py.class .\images.sikuli\images$py.class -i!.\images.sikuli\*.png

java -jar .\myapp_sikuli.jar

PS : I detected a diferent bug, the select() prompt does not work when the code is running from "sikulix-2.0.3.jar" but it does work when running from the jar file

SeltiX (seltix-) said : #21

about your solution, I try but because I have a settings folder with txt files for customization :

├── myapp.sikuli/
│ ├── myapp.py

  |── helpers.sikuli/
│ ├── helpers.py

 |── images.sikuli/
│ ├── images.py
│ ├── all my image files....

 |── settings/
│ ├── default.txt
│ ├── other config files.ext....

I got my warning saying it does not find the configs.txt files, those files are read from helpers.py with this path :

path_current = os.path.dirname(os.path.realpath(__file__))
path_settings = path_current + "\\..\\settings"

you said when running the jar everithing is exported to a temp-folder, I supose I have to put my settings folder there or is there a better way to have the settings folder next to the jar file? I will continue to work around your solution and disable my settings importer to compare to my previous tests

SeltiX (seltix-) said : #22

i just finish isolating the problem and I can confirm is something with the Region() "setW()" and "setH()". I remove those operations and the script starts to work as expected.

After a litle time of runing I got another problem with Region(), in the "highlight()" method :

region.highlight(0.3)

and I got this error :

        at java.lang.Thread.sleep(Native Method)
        at org.sikuli.util.ScreenHighlighter.closeAfter(ScreenHighlighter.java:142)
        at org.sikuli.util.ScreenHighlighter.highlight(ScreenHighlighter.java:223)
        at org.sikuli.script.Region.highlight(Region.java:2148)
        at org.sikuli.script.Region.highlight(Region.java:2126)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: timeout value is negative

After changing the timout from 0.3 to 1 it does not throw the error anymore

SeltiX (seltix-) said : #23

hello, after disabling the settings importer method I can confirm it does work as expected, without any of the problems described before using sikulixsetup-1.1.3.jar.

i wold prefer to have the allinone standalone version but for temporary solution if there is a way I can read files located in the same path as the jar file it wold be great :D

SeltiX (seltix-) said : #24

PS : the "Export as jar" deletes all my images after creating the jar, just informing, i dont know if that is expected ( the ones I copy to myapp.sikuli/ )

RaiMan (raimund-hocke) said : #25

--- PS : the "Export as jar" deletes all my images after creating the jar, just informing, i dont know if that is expected ( the ones I copy to myapp.sikuli/ )

- this happens if you save the main.sikuli, that is prepared for distribution in the IDE (due to the setting, that not used images are deleted).

So do not do that.

prepare everything in the developement versions and save that stuff.
then close all the tabs and create the copy of the distribution version outside the IDE.
Open the distribution main.sikuli and ONLY use export-as-jar - DO NOT save - just close.

RaiMan (raimund-hocke) said : #26

If the Settings folder is intended to live outside the jar-pack, then you either have to expect it in a special location (user home, working directory, based on an environment setting) or you have to ask the user in your main using:

Do.popFile("", "myApp - locate the settings folder")

RaiMan (raimund-hocke) said : #27

--- problems with 1.1.3

If you are happy with that: fine.

... but be aware, that I do no longer support versions prior to 2.0.x

RaiMan (raimund-hocke) said : #28

From my side I take this thread as finished.

If you get to new problems/insights please start a new question for every isolated topic.

SeltiX (seltix-) said : #29

hello,
version 2.0.X of sikuli setup? I only find the 1.1.3 : https://launchpad.net/sikuli/+download
is there another version?

SeltiX (seltix-) said : #30

should I open another ticket about the select() problem in sikulix-2.0.3.jar ?

RaiMan (raimund-hocke) said : #31

--- version 2.0.X of sikuli setup?
does not exist. 1.1.3 was the last one with setup.

RaiMan (raimund-hocke) said : #32

--- select() problem in sikulix-2.0.3.jar
is fixed in version 2.0.4

SeltiX (seltix-) said : #33

--- does not exist. 1.1.3 was the last one with setup.
ok, then if there is no more recent version of setup, is it still possible to embed sikulix manually? can you point me on what to search?

(sorry for posting again)

RaiMan (raimund-hocke) said : #34

--- (sorry for posting again)

no, that is ok - it is related and is ok to ask, if you are lost somehow ;-)

If I understand right:
You want to have a fat jar, that runs your application (the scripts) by simply saying:

java -jar myApp.jar possible, parameters, ...

so you do not want your audience to fiddle around with other stuff than Java?

Honestly: A fat jar, that runs out of the box has about 70 - 80 MB - surely not the right approach.

Make a self extracting zip-container, that contains sikulixapi.jar and the jython.jar. Create a setup cmd and/or a run cmd for your scripts packed using export-as-jar. Put this package somewhere in the cloud (for Windows nuget might be the right place, or Github for the world). This is a one-time-for-ever effort.

So you have a chance to follow the SikuliX development much easier and you can concentrate on your apps.

SeltiX (seltix-) said : #35

Yes thats it, almost everyone has java already installed.
The size is not a problem, I prefer to make it easy to use and sikulixapi.jar alone is >50mb, having a standalone file of 70 - 80mb is not a problem if that is the price of make it only dependent of java

Can you help with this problem?

Provide an answer of your own, or ask SeltiX for more information if necessary.

To post a message you must log in.