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.
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); }}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 :
@Configurationpublic class OculixConfig { @Bean public Screen primaryScreen() { return new Screen(0); }}
@Servicepublic 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"); }}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 bundle | Runner choisi |
|---|---|
<nom>.py | Jython |
<nom>.rb | JRuby |
<nom>.ps1 | PowerShell |
<nom>.scpt | AppleScript |
<nom>.robot | Robot 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.
| Runner | Quand le choisir |
|---|---|
| Jython | Le 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 3 | Besoin de numpy / pandas / requests / asyncio en parallèle d’OculiX (via Operix) |
| PowerShell | Pipelines Windows, intégration Active Directory, scripts ops existants |
| AppleScript | macOS 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 purgreeting = "Bonjour, " + getDate()print greeting
# Interop Java — instanciez n'importe quelle classe JVMdate = Date()print date.toString()
# L'API OculiX directementclick("button.png")wait("done.png", 5)from sikuli import *
# Itère sur tous les écrans détectésfor 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écifiquesecondary = 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 OCRcontent = 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 valeurfrom sikuli import *
# Drag-and-drop d'un fichier vers un dossiersource = find("file_to_move.png")target = find("destination_folder.png")dragDrop(source, target)
# Combinaison de touches — Ctrl+Shift+Stype("s", KeyModifier.CTRL | KeyModifier.SHIFT)
# Saisie de texte international (UTF-8)type("Café crème — 3 €")def, class, for, while, if/elif/else.import pour les modules Python purs et les classes Java.os, sys, re, json, datetime, collections, itertools, functools.% ou .format()).print() comme fonction sans from __future__ import print_function.java.net.URL, org.json, org.apache.http, etc.).Deux raisons assumées :
Pour les besoins Python 3 modernes, le projet Operix (voir ci-dessous) apporte la réponse propre.
require 'sikuli'
screen = Sikuli::Screen.newscreen.wait('app_icon.png', 10).clickscreen.wait('main_window.png', 10)
# Itération idiomatique Ruby sur findAllscreen.findAll('row_anchor.png').each do |row| row.right(200).clickend
# Blocs Ruby pour les patterns courants3.times do screen.click('next_button.png') sleep 1endMê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.
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 :
java -jar oculixide.jar -r py3 mon-script.pyMais 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.
from operix import Sikuliimport pandas as pdimport 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ésultatreport = 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.
$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.scpttell application "OculiX" click image "button.png" wait image "done.png" with timeout 5end tell
-- Combinable avec les outils macOS natifstell application "Finder" set theFiles to every file of folder "Downloads"end tellDisponible uniquement sur macOS. Intègre OculiX aux workflows Automator, Shortcuts, et aux scripts systèmes pilotés par des raccourcis clavier globaux.
*** Settings ***Library OculiXLibraryLibrary 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.
java -jar oculix-server.jar --port 5555OculiX 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.
# Soumettre un script via HTTPcurl -X POST http://oculix-runner:5555/run \ -H "Content-Type: application/json" \ -d '{"bundle": "/scripts/checkout.sikuli", "args": []}'
# Vérifier l'étatcurl http://oculix-runner:5555/statusorg.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 JDKimport java.nio.file.Files as Filesimport java.nio.file.Paths as Paths
content = Files.readString(Paths.get("config.json"))print content
# Appels HTTP synchrones via la JVMimport java.net.URI as URIimport java.net.http.HttpClient as HttpClientimport java.net.http.HttpRequest as HttpRequestimport 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 à OculiXfrom org.sikuli.script import Screen, Region, Pattern, Matchfrom org.sikuli.script import VNCScreen, ADBScreenfrom org.sikuli.script import PaddleOCREngineDé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 bundledef 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)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ésultatclick("submit.png")waitVanish("loading_spinner.png", 30)wait("success_message.png", 5)import java.text.SimpleDateFormat as DateFormatimport 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 critiquebefore = take_screenshot("before-submit")click("submit.png")wait("done.png", 10)after = take_screenshot("after-submit")# Lit le numéro de commande affiché à l'écran et le réutiliseorder_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 appclick("search_field.png")type(order_id + Key.ENTER)wait("order_details.png", 10)Settings.DebugLogs = True # logs internes verbeuxSettings.ActionLogs = True # log de chaque click/type/etc.Settings.InfoLogs = TrueSettings.SaveLastImage = True # écrit le dernier match raté sur disque
# Trace au point précis du scriptprint "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 syssys.stdout = open("/tmp/oculix-trace.log", "a")