Aller au contenu

Langages — API Java et runners de scripting

OculiX est un outil de RPA — automatisation visuelle d’écrans et d’applications — héritier direct de SikuliX, projet vivant depuis 2010. Il sert toute personne qui doit reproduire à l’écran une suite d’actions : un opérateur qui exporte son rapport quotidien, une équipe ops qui pilote un logiciel legacy sans API, un workflow d’intégration entre deux applications qui ne se parlent pas, un testeur qui valide un parcours utilisateur de bout en bout. Le test n’est qu’un cas d’usage parmi d’autres.

Derrière l’IDE simple, il y a une vraie bibliothèque Java. Tout ce que le Recorder produit, vous pouvez le coder, le versionner, le tester, l’intégrer dans n’importe quel projet existant. Deux voies, un seul moteur — c’est ce qui fait que la même automation peut tenir sur le PC d’un utilisateur métier comme dans un pipeline CI/CD enterprise.

Bibliothèque Java propre

L’API publique vit dans org.sikuli.script. Tout est typé, documenté en Javadoc, testable, mockable. Pas de DSL ésotérique : du Java de production, comme le reste de votre code.

Vingt ans de polissage

SikuliX existe depuis 2010, OculiX en est la continuation MIT depuis 2026. Le moteur de matching visuel a été retravaillé sur des centaines de cas d’usage industriels — du retail à la défense.

Aucun lock-in vendeur

Code source MIT, dépendances open source, exécution 100 % locale. Vous n’envoyez rien dans un cloud, vous ne payez personne par bot, vous ne dépendez pas d’une roadmap éditeur.

Multi-langage, multi-runtime

Java natif, Jython, JRuby, CPython 3 via Operix, PowerShell, AppleScript, Robot Framework. Vous choisissez la voie qui correspond à votre équipe et à votre stack — pas l’inverse.

Cette page décrit les deux voies d’utilisation : consommer l’API depuis votre propre projet Java, ou écrire un script dans l’IDE OculiX où le runner adapté est choisi automatiquement selon le langage utilisé.

L’artefact oculixapi se consomme comme n’importe quelle bibliothèque Java. Ajoutez la dépendance, importez org.sikuli.script.*, et appelez l’API directement.

<dependency>
<groupId>io.github.oculix-org</groupId>
<artifactId>oculixapi</artifactId>
<version>3.0.4-rc1</version>
</dependency>
import org.sikuli.script.Screen;
import org.sikuli.script.Region;
import org.sikuli.script.Match;
public class ExportReport {
public static void main(String[] args) throws Exception {
Screen screen = new Screen();
screen.wait("app_icon.png", 10).click();
screen.wait("main_window.png", 10);
Region dialog = screen.find("save_dialog.png");
dialog.type("filename_field.png", "rapport.csv");
dialog.click("save_button.png");
// waitVanish synchronise avec la disparition du spinner
screen.waitVanish("loading_spinner.png", 30);
}
}

Côté outillage, vous gardez ce que vous utilisez déjà

Section intitulée « Côté outillage, vous gardez ce que vous utilisez déjà »

Votre IDE habituel

IntelliJ, Eclipse, VS Code — l’autocomplétion et le refactoring fonctionnent sur les classes OculiX comme sur n’importe quelle autre lib.

Validation à la compilation

Le compilateur Java attrape les fautes de frappe et les mauvais types. Pas besoin de lancer le script pour découvrir une signature incorrecte.

Tests JUnit

Les classes OculiX se mockent avec Mockito comme n’importe quel autre composant. Tests unitaires possibles sans environnement graphique.

CI Java standard

Maven, Gradle, Jenkins, GitHub Actions — votre pipeline existant absorbe OculiX sans changement.

import org.junit.jupiter.api.Test;
import org.sikuli.script.Screen;
import static org.junit.jupiter.api.Assertions.*;
class CheckoutFlowTest {
@Test
void checkoutCompletesUnderTenSeconds() throws Exception {
Screen screen = new Screen();
long start = System.currentTimeMillis();
screen.click("add_to_cart.png");
screen.click("checkout.png");
screen.wait("payment_form.png", 5);
screen.type("card_field.png", "4111111111111111");
screen.click("pay_button.png");
screen.wait("order_confirmation.png", 10);
long duration = System.currentTimeMillis() - start;
assertTrue(duration < 10_000, "Checkout doit aboutir en moins de 10 s");
}
}

Le test est exécutable depuis Maven (mvn test), depuis votre IDE (clic droit → Run test), ou depuis votre CI. La même classe peut tester votre code métier et le parcours utilisateur visuel — pas besoin de deux frameworks.

Rien ne vous empêche d’enregistrer un Screen comme bean Spring et de l’injecter dans vos services métier :

@Configuration
public class OculixConfig {
@Bean
public Screen primaryScreen() {
return new Screen(0);
}
}
@Service
public class ReportExporter {
private final Screen screen;
public ReportExporter(Screen screen) {
this.screen = screen;
}
public void exportDailyReport(String filename) throws Exception {
screen.click("file_menu.png");
screen.click("export_to_csv.png");
screen.wait("save_dialog.png", 5);
screen.type("filename_field.png", filename);
screen.click("save_button.png");
}
}
  • Votre équipe a déjà un backend Java et veut y intégrer de l’automatisation visuelle sans changer d’outillage.
  • Vous voulez versionner, reviewer et tester les scénarios d’automatisation avec les mêmes pratiques que votre code métier (Git, pull requests, JUnit, couverture).
  • Vous orchestrez OculiX depuis un service Spring Boot, un worker batch, un job Quartz, ou tout autre composant Java déjà en production.
  • Vous avez besoin de typage fort pour des scénarios complexes (workflows à dizaines d’étapes, branchements selon réponse API, etc.).

L’IDE OculiX permet d’écrire un script directement dans son éditeur, sans projet Maven, sans compilation. Vous lancez l’IDE, vous créez un bundle, vous appuyez sur Run. Le runner adapté est sélectionné automatiquement.

Un bundle .sikuli est en réalité un dossier qui contient le script et ses images. Le runner est inféré de l’extension du fichier de script à l’intérieur de ce dossier :

Fichier dans le bundleRunner choisi
<nom>.pyJython
<nom>.rbJRuby
<nom>.ps1PowerShell
<nom>.scptAppleScript
<nom>.robotRobot Framework

Tout ce mécanisme vit dans le module IDE (org.sikuli.support.runner.Runner), pas dans l’API. C’est cohérent : l’API Java n’a pas besoin de runner, elle expose directement les classes.

RunnerQuand le choisir
JythonLe défaut historique, le plus de doc disponible, accès complet à la JVM
JRubyÉquipes Ruby, syntaxe plus expressive pour les workflows métier
CPython 3Besoin de numpy / pandas / requests / asyncio en parallèle d’OculiX (via Operix)
PowerShellPipelines Windows, intégration Active Directory, scripts ops existants
AppleScriptmacOS uniquement, intégration Automator / Shortcuts / scripts système
Robot FrameworkÉquipes QA déjà sur Robot, besoin de syntaxe keyword-based

C’est le runner le plus utilisé en pratique, héritage direct de SikuliX. Syntaxe Python 2.7, exécution à l’intérieur de la JVM, accès complet à toutes les classes Java du classpath.

from sikuli import *
import java.util.Date as Date
# Du Python pur
greeting = "Bonjour, " + getDate()
print greeting
# Interop Java — instanciez n'importe quelle classe JVM
date = Date()
print date.toString()
# L'API OculiX directement
click("button.png")
wait("done.png", 5)
from sikuli import *
# Itère sur tous les écrans détectés
for i in range(Screen.getNumberScreens()):
s = Screen(i)
print "Écran %d : %d × %d à (%d, %d)" % (i, s.getW(), s.getH(), s.getX(), s.getY())
# Cible un écran spécifique
secondary = Screen(1)
secondary.click("notification.png")
from sikuli import *
# Trouve la zone du tableau par son en-tête (image)
header = find("table_header.png")
table_body = header.below(500)
# Lit le contenu textuel de la zone via OCR
content = table_body.text()
print content
# Ou trouve une cellule par son libellé
order_total = table_body.findText("Total")
order_total.right(200).click() # clic dans la colonne valeur
from sikuli import *
# Drag-and-drop d'un fichier vers un dossier
source = find("file_to_move.png")
target = find("destination_folder.png")
dragDrop(source, target)
# Combinaison de touches — Ctrl+Shift+S
type("s", KeyModifier.CTRL | KeyModifier.SHIFT)
# Saisie de texte international (UTF-8)
type("Café crème — 3 €")
  • Blocs basés sur l’indentation, def, class, for, while, if/elif/else.
  • import pour les modules Python purs et les classes Java.
  • Les modules de la bibliothèque standard qui ne dépendent pas d’extensions C : os, sys, re, json, datetime, collections, itertools, functools.
  • Pas de f-strings (utilisez % ou .format()).
  • Pas de print() comme fonction sans from __future__ import print_function.
  • Aucune extension C — donc pas de numpy, pas de pandas, pas de requests. Les équivalents JVM fonctionnent (java.net.URL, org.json, org.apache.http, etc.).

Deux raisons assumées :

  1. Compatibilité SikuliX. Tous les scripts écrits entre 2010 et 2026 tournent sans modification. Cette rétrocompatibilité compte pour des milliers d’équipes encore en production.
  2. Processus unique. Pas de saut FFI, pas de coût de démarrage, pas de coordination entre runtimes. Le script et le moteur OculiX partagent la même JVM, le même classpath, la même mémoire.

Pour les besoins Python 3 modernes, le projet Operix (voir ci-dessous) apporte la réponse propre.

require 'sikuli'
screen = Sikuli::Screen.new
screen.wait('app_icon.png', 10).click
screen.wait('main_window.png', 10)
# Itération idiomatique Ruby sur findAll
screen.findAll('row_anchor.png').each do |row|
row.right(200).click
end
# Blocs Ruby pour les patterns courants
3.times do
screen.click('next_button.png')
sleep 1
end

Même surface d’API que Jython, ergonomie Ruby. Bon choix pour les équipes déjà familières avec JRuby ou habituées à Ruby, et pour les workflows métier où la syntaxe Ruby s’exprime mieux que la syntaxe Python.

CPython 3 — via Operix, le pont vers Python moderne

Section intitulée « CPython 3 — via Operix, le pont vers Python moderne »

Pour utiliser du Python 3 récent avec numpy, pandas, requests, asyncio, httpx ou toute autre lib moderne en parallèle d’OculiX, le projet compagnon Operix expose OculiX depuis CPython 3 via un pont py4j.

OculiX peut aussi exécuter du Python 3 via un simple sous-processus :

Fenêtre de terminal
java -jar oculixide.jar -r py3 mon-script.py

Mais cette approche a deux limites : un coût de démarrage à chaque exécution (la JVM se relance), et pas de session partagée entre les appels — chaque script repart de zéro.

Operix maintient une session py4j ouverte : la JVM OculiX est démarrée une fois, et chaque appel Python passe par une connexion socket persistante. Latence négligeable, état partagé entre les appels, possibilité d’orchestrer plusieurs scripts depuis le même processus Python.

data_analysis_pipeline.py
from operix import Sikuli
import pandas as pd
import requests
# Étape 1 — récupération via API (CPython moderne)
data = requests.get('https://api.exemple.com/orders').json()
df = pd.DataFrame(data)
df['total'] = df['quantity'] * df['unit_price']
df.to_csv('orders.csv', index=False)
# Étape 2 — automation visuelle (OculiX via Operix)
sikuli = Sikuli.screen()
sikuli.wait('app_icon.png', 10).click()
sikuli.wait('import_dialog.png', 10)
sikuli.type('filename_field.png', 'orders.csv')
sikuli.click('import_button.png')
sikuli.waitVanish('loading_spinner.png', 60)
# Étape 3 — validation visuelle du résultat
report = sikuli.find('summary_table.png').text()
assert 'Total : ' in report, "Le rapport n'a pas été généré correctement"

C’est exactement le scénario où l’écosystème CPython 3 apporte une vraie valeur : ETL avec pandas, appels API avec httpx ou requests, ML léger avec scikit-learn, le tout déclenché ET vérifié par OculiX.

  • Vous avez déjà du code Python 3 en production que vous voulez étendre avec de l’automation visuelle.
  • Votre pipeline a une étape data lourde (numpy / pandas / scipy) qui n’a pas d’équivalent JVM commode.
  • Vous voulez mélanger OCR / matching visuel d’OculiX avec des appels HTTP modernes (auth OAuth, WebSockets, async/await).
mon-workflow.ps1
$screen = New-Object org.sikuli.script.Screen
$screen.Click("button.png")
$screen.Wait("done.png", 5)
# Intégration avec le reste de la chaîne Windows
$users = Get-ADUser -Filter 'Department -eq "Sales"'
foreach ($user in $users) {
$screen.Type("user_field.png", $user.SamAccountName)
$screen.Click("create_account.png")
$screen.Wait("success.png", 10)
}

Utile pour les pipelines orientés Windows où le reste de la chaîne d’outils est déjà en PowerShell : Active Directory, Exchange, MECM/SCCM, etc.

-- ma-tache-macos.scpt
tell application "OculiX"
click image "button.png"
wait image "done.png" with timeout 5
end tell
-- Combinable avec les outils macOS natifs
tell application "Finder"
set theFiles to every file of folder "Downloads"
end tell

Disponible uniquement sur macOS. Intègre OculiX aux workflows Automator, Shortcuts, et aux scripts systèmes pilotés par des raccourcis clavier globaux.

*** Settings ***
Library OculiXLibrary
Library Collections
*** Variables ***
${TIMEOUT} 10s
*** Test Cases ***
Soumettre un formulaire vide affiche le message d'erreur
Click submit_button.png
Wait error_message.png ${TIMEOUT}
${text}= Read Text Region 0 0 1920 1080
Should Contain ${text} Champ obligatoire
Soumettre un formulaire valide redirige vers la confirmation
Type name_field.png Jean Dupont
Type email_field.png [email protected]
Click submit_button.png
Wait confirmation.png ${TIMEOUT}

La bibliothèque de keywords Robot Framework expose l’API OculiX dans la syntaxe verbeuse mais lisible de Robot. Idéal pour les équipes QA déjà équipées en Robot, ou pour mélanger des étapes web (SeleniumLibrary) avec des étapes desktop (OculiXLibrary) dans un même scénario.

Fenêtre de terminal
java -jar oculix-server.jar --port 5555

OculiX tourne en mode headless et accepte les scripts via une API HTTP. Conçu pour les runners CI et les outils d’orchestration qui doivent lancer des scripts à distance sans démarrer un IDE complet.

Fenêtre de terminal
# Soumettre un script via HTTP
curl -X POST http://oculix-runner:5555/run \
-H "Content-Type: application/json" \
-d '{"bundle": "/scripts/checkout.sikuli", "args": []}'
# Vérifier l'état
curl http://oculix-runner:5555/status

Network runner — pour l’exécution distribuée

Section intitulée « Network runner — pour l’exécution distribuée »

org.sikuli.scriptrunner.NetworkRunner permet d’exécuter un script sur la machine A alors qu’OculiX est invoqué depuis la machine B. Cas d’usage typique : un plan de contrôle sur votre poste qui déclenche des scripts simultanément sur plusieurs machines agents, chacune exécutant son scénario localement et remontant le résultat.

Toute bibliothèque JVM ajoutée au classpath est appelable directement, sans glue particulière :

# I/O fichier moderne via JDK
import java.nio.file.Files as Files
import java.nio.file.Paths as Paths
content = Files.readString(Paths.get("config.json"))
print content
# Appels HTTP synchrones via la JVM
import java.net.URI as URI
import java.net.http.HttpClient as HttpClient
import java.net.http.HttpRequest as HttpRequest
import java.net.http.HttpResponse.BodyHandlers as BodyHandlers
client = HttpClient.newHttpClient()
request = HttpRequest.newBuilder(URI.create("https://api.exemple.com/status")).build()
response = client.send(request, BodyHandlers.ofString())
print response.body()
# Classes spécifiques à OculiX
from org.sikuli.script import Screen, Region, Pattern, Match
from org.sikuli.script import VNCScreen, ADBScreen
from org.sikuli.script import PaddleOCREngine

Déposez un fichier .py à côté de votre bundle .sikuli et importez-le comme n’importe quel module Python :

# helpers.py — déposé à côté du bundle
def login(user, password):
click("user_field.png")
type(user + "\t" + password + "\n")
wait("home.png", 10)
def logout():
click("avatar.png")
click("logout_menu.png")
waitVanish("home.png", 5)
main.sikuli/main.py
from helpers import login, logout
login("alice", "hunter2")
# … workflow …
logout()

OculiX ajoute automatiquement le répertoire parent du bundle au sys.path, donc les imports relatifs marchent comme attendu.

import time
def click_until(image, total_timeout=30):
end = time.time() + total_timeout
while time.time() < end:
if exists(image):
click(image)
return True
time.sleep(1)
raise FindFailed(image)
if exists("dialog_yes.png"):
click("yes.png")
elif exists("dialog_no.png"):
click("no.png")
else:
click("default_action.png")
rows = findAll("row_anchor.png")
for r in rows:
r.right(200).click() # clic 200 px à droite de chaque ligne
# Soumet le formulaire, attend la fin du chargement, valide le résultat
click("submit.png")
waitVanish("loading_spinner.png", 30)
wait("success_message.png", 5)
import java.text.SimpleDateFormat as DateFormat
import java.util.Date as Date
def take_screenshot(prefix):
ts = DateFormat("yyyyMMdd-HHmmss").format(Date())
path = "evidence/%s-%s.png" % (prefix, ts)
Screen(0).capture().getFile(path)
return path
# Avant et après l'action critique
before = take_screenshot("before-submit")
click("submit.png")
wait("done.png", 10)
after = take_screenshot("after-submit")

Récupérer une valeur via OCR pour la réutiliser

Section intitulée « Récupérer une valeur via OCR pour la réutiliser »
# Lit le numéro de commande affiché à l'écran et le réutilise
order_label = find("order_number_label.png")
order_id = order_label.right(200).text().strip()
print "Commande créée :", order_id
# Recherche cette commande dans une autre app
click("search_field.png")
type(order_id + Key.ENTER)
wait("order_details.png", 10)
Settings.DebugLogs = True # logs internes verbeux
Settings.ActionLogs = True # log de chaque click/type/etc.
Settings.InfoLogs = True
Settings.SaveLastImage = True # écrit le dernier match raté sur disque
# Trace au point précis du script
print "Sur le point de cliquer : " + str(target)
target.highlight(2, "red") # visualise ce qu'OculiX a trouvé

Toute la sortie atterrit dans la console de l’IDE (ou sur stdout si vous avez lancé via la CLI). Pour les sessions de debug longues, redirigez vers un fichier :

import sys
sys.stdout = open("/tmp/oculix-trace.log", "a")