request_url_withoutHTTP=,canonical_url_withHTTP=,canonical_url_withoutHTTP=,request_url_withoutHTTP_realspaces=. Desktopdev (Seite 1 von 2) « Tags « Blog « Bernhard Häussner
Bernhard Häussner
Tags: Artikel mit dem Tag «Desktopdev» durchstöbern

Javascript in Photoshop

22.10.2011, 13:32
Photoshop-Skripting-Beispielbild

Photoshop-Skripting-Beispielbild

Als ich kürzlich eine ganze Reihe Ausweise mit Namen ausfüllen musste, habe ich mir angeschaut, wie man Photoshop CS3 automatisieren kann. Ich war erstaunt herauszufinden, das PS mit Javascript gesteuert werden kann, neben den zusammenklickbaren Makros, VBScript und AppleScript.

Hello-Word-Beispieldateien

Da der komplette Code etwas länger ist, habe ich eine Beispiel-PSD und alle Skripte auf Github online gestellt. Dort unter Downloads findet sich eine zip-Datei mit allem Nötigen, um das hier beschriebene Hello-World-Skript auszuführen.

Dokumentation

Das Photoshop-Scripting ist sehr ausführlich dokumentiert. Zunächst gibt es im Ordner C:\Programme\Adobe\Adobe Utilities\ExtendScript Toolkit 2\SDK zwei Dokumente: Einmal Adobe Intro to Scripting.pdf, welches man lesen sollte, wenn man „noch nie programmiert hat“ oder nicht so genau weiß, was Anwendungen skripten allgemein ist.

Außerdem JavaScript Tools Guide CS3.pdf. Hier findet sich zunächst ein Überblick über das IDE von Adobe (ich habe aber alles direkt in kwrite geschrieben) und dann folgen die Dokumentationen zu den Libraries für Dateisystem, UI, HTTP, Sockets, und vieles weitere. In dem Dokument wird regelmäßig auf Beispieldateien aus dem Brige SDK hingewiesen, dieses findet sich hier.

Die Photoshop-Spezifischen APIs stehen in der Photoshop Javascript Referenz. Photoshop stellt ein DOM zur Verfügung.

Erstellen der Photoshop-Datei

Zuerst kann man eine ganz normale Photoshop-Datei erstellen. Gute Ebenen-Organisation erleichtert später das Skripten. Wenn möglich, packt man alle Ebenen, die bearbeitet werden sollen, zusammen in eine Gruppe (Ordner). Dann findet man sie im Skript leichter wieder. Wenn man, wie ich, mehrere Ausweise o.ä. auf eine DIN-A4-Seite zusammenstellt, kann man jeden Ausweis gleich und in einer eigenen Gruppe anlegen, auch das macht das Skripten einfacher.

Skripte ausführen

Um ein fertiges Skript auszuführen klickt man im Photoshop-Menü Datei > Skripten > Durchsuchen und wählt die .jsx Datei. Das Skript läuft sofort los, eventuell erscheinen Fehlermeldungen als Dialogbox.

Exkurs: Eingabe-Daten verarbeiten

Man könnte zwar rein technisch gesehen mit Javascript die Daten von einem FTP-Server laden, dann irgendwie verarbeiten und letztendlich daraus die Bilder generieren. Ich habe mich jedoch darauf beschränkt, die Daten von einer Tabelle auf einer Webseite zu kopieren (sie sind dann Tabulator-Getrennt in der Zwischenablage) und sie mit awk in ein entsprechendes JSON-Format zu bringen:

awk 'BEGIN{RS="\r\n";FS="\t+"} {print "{planet:\"" $1 "\",comment:\"" $2 "\"},"}' planeten.raw.txt > planeten.json.txt

Da ich auf Windows arbeite (awk läuft auf Linux, die Dateien liegen in einem Samba-Share), musste ich awk sagen, dass die Zeilen auch noch einen Wagenrücklauf beinhalten. Die entstandenen JSON-Daten habe ich dann in das Skript hart kodiert:

var data=[
  {name:"world"},
  {name:"venus"},
  {name:"mars"},
  {name:"pluto",
   comment:"You're not a real planet though. But still, welcome. "}
];

Photoshop-Ebenen bearbeiten und speichern

Hauptsächlich soll das Skript Photoshop-Textebenen mit neuen Texten versehen. Hierzu gibt es in der Dokumentation auch Beispiele. Im Wesentlichen iteriert man durch die Daten, findet in layerSets den Ebenen-Ordner mit den Texten, und bearbeitet dann die Eigenschaft textItem.contents der verschiedenen artLayers (Ebenen). Der Code sieht dann so aus:

for (var dataI = 0; dataI < data.length; dataI++) {
    
  var textGroup = activeDocument.layerSets[0];
  
  textGroup.artLayers[0].textItem.contents=
    "Hello, "+data[dataI].name+"!";
  
  if(data[dataI].comment) {
    textGroup.artLayers[1].textItem.contents=
      data[dataI].comment;
  } else {
    textGroup.artLayers[1].textItem.contents=
      "Welcome to Photoshop scripting.";
  }
  
  // code zum speichern
  
}

Speichern und Exportieren

Ich bearbeite immer das activeDocument und speichere das neue Bild dann als Kopie. Hier überschreibt Photoshop bereits generierte Dateien ohne nachzufragen. Die Speicher-Optionen können als XxxSaveOptions-Objekte mitgegeben werden.

// Erstellen der Optionen fuer JPEG-Export:
var jpgOpts = new JPEGSaveOptions();
jpgOpts.embedColorProfile=true;
jpgOpts.quality=12; // hoechste Q

// Dokument speichern:
activeDocument.saveAs(filename,jpgOpts,true/*=als Kopie*/);

Den neuen Pfad generiere ich aus dem Dateinamen das aktuellen Dokuments und einer fortlaufenden Nummer. Dazu benutze ich eine Reihe nützlicher Funktionen. Damit ich diese nicht in jeder Skript-Datei einfügen muss, benutze ich die include-Funktion von Adobes ExtendedScript:

#include util.jsxinc

Der vollständige Code der util.jsxinc findet sich im Github-Repository.

Benutzeroberfläche

Bisher wird einfach nur eine Sanduhr angezeigt während das Skript läuft, also erstelle ich ein Fenster mit einem Fortschrittsbalken. Man kann auch komplexere UIs zur Eingabe von Parametern usw. erstellen, aber dies lohnt sich vermutlich selten. Ein Fortschrittsbalken mit Cancel-Funktion ist jedoch nicht so schwer zu erstellen. Im Wesentlichen benötigt man dies:

var dlg = new Window('window', _(names));
dlg.progressbar = dlg.add('progressbar');
dlg.progressbar.preferredSize = [400,20];
dlg.show();

// Zur Haelfte fertig:
dlg.progressbar.value = 50;

Hilfslinen-Skript

Ein weiteres Skript kann Hilfslinien erstellen. Am Besten kopiert man es in den Ordner Adobe Photoshop CS3\Vorgaben\Skripten, damit es immer im Skript-Menü auftaucht. Ich finde es immer extrem nervig, für Druckränder und Mittellinien die ganzen Hilfslinien zu erstellen. Genauso nervig ist es, für jedes Druckformat eine Vorlage zu basteln. Lieber generiere ich die Hilfslinien mit diesem kurzen Skript.

ExtendScript hat auch noch weitere Features, wie Reflection und Operator Overloading, welches dann bei den UnitValues Rechnen mit Einheiten ermöglicht. In ExtendScript geht das:

alert( (UnitValue(3,"mm") + UnitValue(7,"pt")*0.9).as("cm") );

Da es für die Hilfslinienerstellung keine Funktionen gibt, habe ich die Methode createGuide(offs,orientation) mit dem ScriptListener erstellt. Man kopiert Adobe Photoshop CS3\Skript Handbuch\Hilfsprogramme\ScriptListener.8li nach Adobe Photoshop CS3\Zusatzmodule\Automatisieren und startet PS neu. Dieses Plugin erstellt auf dem Desktop ein ScriptingListenerJS.log, in dem die u.A. die aufgenommenen Aktionen als Javascript-Code gespeichert werden. Diesen Code muss man dann noch parametrisieren und erhält so für alles, was man in Photoshop anklicken kann, den entsprechenden Code.

Auch die Ressourcen-Deskriptoren habe ich im Skript verwendet. Mit ihnen kann man aus einem String direkt einen ganzen UI-Baum erstellen.

Zusammenfassung

Ressourcen zum Text:

Das Skripten von Photoshop ist schon nach kurzem Einarbeiten für den Javascript-Kenner wohl wesentlich einfacher als das zusammenklicken von Aktionen und Stapelverarbeitungen. Wer sich intensiver mit der Dokumentation befasst kann darüber hinaus sehr flexibel mit Daten aus verschiedenen Quellen automatisiert Dokumente erstellen.

Kommentare: keine

Servicemenüs für Dolphin (neu)

15.01.2010, 18:44

Wenn man häufig ein paar einfache Scripte über verschiedene Dateien laufen lassen will, kann man die ensprechenden Befehle mit Dolphin über ein frei einstellbares Servicemenü aufrufen. Man benötigt lediglich eine besonders formatierte .desktop-Datei und dann kann man das Skript in Zukunft über das Kontextmenü aufrufen.

Die Datei sieht z.B. so aus:

[Desktop Entry]
Encoding=UTF-8
Type=Service
ServiceTypes=KonqPopupMenu/Plugin,all/allfiles
Actions=timeBu

[Desktop Action timeBu]
Name=Create backup
Name[de]=Backup erstellen
Icon=document-revert
Exec=cp %U %U.`date +%%Y-%%m-%%d.%%H-%%M-%%S` && kdialog --title "Backup" --passivepopup "Backup of `basename %U` created. " 5

Unter ServiceTypes kann man die mine-typen angeben, bei denen das Script im Aktionen-Menü angezeigt werden soll, unter Name steht der Text, der dann angezeigt wird. Man kann Übersetzungen anbieten. Das Icon ist am besten eines der KDE-Icons. Nach Exec steht dann der Code, der ausgeführt werden soll. Mit %U lässt sich der Dateiname der ausgewählten Datei referenzieren, %% wird zu % umgewandelt. Diese Datei kommt dann in den Ordner:

~/.kde4/share/kde4/services/ServiceMenus

Neben dem Beispiel habe ich auch noch ein Menüeintrag erstellt, um optipng auszuführen:

auf ähnliche weise lassen sich auch andere praktische Sachen verwirklichen. Ich habe z.B. noch Servicemenüs für Konqueror erstellt, die sich mit einem Skript konvertieren lassen. Eine paar duzend Servicemenüs finden sich auch auf kde-apps.org.

Kommentare: keine

Qt4 Designer und Ruby - erste Schritte

06.01.2010, 11:23

Vor Kurzem habe ich ein bisschen mit Ruby herumgespielt und festgestellt, dass es keine ordentliche und aktuelle Kurzeinführung gibt, wie man die Interfaces aus dem Qt4 Designer mit Ruby Programmen verbindet. Darum habe ich die ersten Schritte hier kurz zusammengefasst:

Als erstes installiert man die Qt4-Development-Tools und die Ruby-Qt-Komponenten mit dem Paketmanager.

Interface im Qt4 Designer erstellen

Dann kann man sich ans Buttons zusammenklicken machen. Das Beispielinterface sieht im Qt Designer so aus:

Ich habe zwei QPushButtons und ein QListWidget in Layouts gepackt. Was es an Widgets gibt und was diese können steht in der Qt4 Dokumentation. In der Ruby API heißen die Klassen stets Qt::Name stat QName. Beim Basteln wichtig ist, dass ObjectName irgendwie Sinn hat, weil damit die Widgets dann benutzt werden können.

Rubycode mit rbuic erstellen

Als nächstes muss man die vom Designer erzeugte XML-Datei mit der Endung .ui in Ruby-Code umwandeln. Dazu gitb es rbuic bzw. rbuic4:

rbuic4 Sample.ui -o Sample.ui.rb

Jetzt haben wir ein Rubyscript in dem das Interface erstellt wird.

Interface in Rubyscript einbinden

Jetzt ist es Zeit dem Interface etwas Leben einzuhauchen. Dazu verbindet man die Signale - wer Webinterfaces Programmiert kennt das als „Events“ - mit den Entsprechenden Slots, die so ähnlich wie callbacks funktionieren. Das geht recht fix mit QMetaObject::connectSlotsByName. Damit dies funktioniert müssen die Funktionen, die aufgerufen werden sollen das Namensmuster on_ObjectName_Signal einhalten, wobei der ObjectName vorher im Designer festgelegt wurde und das Signal heißt z.B. triggered.

Wie man die GUI-Komponente in der Anwendung benutzt, z.B. hier durch Subclassing, und anderes will ich direkt am Beispiel erklären:

#!/usr/bin/ruby

#Laden der Qt-Klasse:
require 'Qt'

#Manchmal auch:
#require 'Qt4'

#Laden des generierten Interfaces:
require 'Sample.ui.rb'

#Initialisieren der Anwendung
app = Qt::Application.new ARGV

#Eine Klasse, um das Verhalten des Interfaces festzulegen:
class AppCommunicate < Qt::MainWindow
  #Die Liste der Slots
  #Sie werden von setupUi automatisch verbunden
  slots :on_actionQuit_triggered, :on_add_clicked, :on_rem_clicked
  def initialize
    puts "init"
    @item_count=0
    #Da wir vererben initialisieren wir so:
    super
    #Jetzt erstellen wir das Interface
    @ui=Ui_MainWindow.new
    #Und weisen es dem MainWindow zu
    @ui.setupUi self
    #Man könnte Slots auch manuell verbinden:
    #connect a.actionQuit, SIGNAL('triggered()'), $qApp, SLOT('quit()')
  end
  #Ein Handler für das Beenden:
  def on_actionQuit_triggered
    puts "Shutting down."
    #So wird die Anwendung gestoppt:
    $qApp.quit
  end
  # "add" ist der name des Buttons, bei click wird dieses signal gesendet
  def on_add_clicked
    puts "Adding item"
    #Es gibt attr_readers für alle Widgets
    #So können wir ihre Methoden aufrufen
    @ui.listWidget.addItem("Hallo Welt ##{@item_count} at #{Time.now.ctime}")
    #Hier könnten z.B. Daten der Anwendung ausgegeben werden
    #Zu demonstrationszwecken nummerieren wir die Einträge sichtbar
    @item_count+=1
  end
  def on_rem_clicked
    puts "Removing item"
    #In cpp könten wir die items deleten
    #Aber in ruby loopen wir über die markierten indizes
    @ui.listWidget.selectedIndexes.each do |i|
      #und nehmen sie aus der Liste
      @ui.listWidget.takeItem i.row
      #da wir keine Referenz speichern löscht sie der GC
    end
  end
end

#Wir erstellen eine Instanz unseres Verhaltens
wdg=AppCommunicate.new do |w|
    #Wir müssen das Interface zeigen
    #Wird auch alle child-widgets zeigen
    w.show
end

#Hier wird die GUI-Schleife gestartet:
app.exec

#TADA!

Wer das ganze ausprobieren will, kann sich die Beispieldateien hier herunterladen:

Ich hoffe das war hilfreich, ich finde es doch hin und wieder ganz nett ein Interface zu haben, vor allem für Scripte, die länger laufen und interaktiv sein sollen.

Kommentare: keine

KDE4 Twitter Benachrichtigungen

18.11.2009, 12:29

Da ich gerne das Webinterface von twitter benutze, aber auch gleich wissen will, ob jemand etwas neues getwittert hat, habe ich ein kleines Bash-Script geschrieben, welches eine Meldung aufploppen lässt, sollte jemand einen neuen tweet schreiben.

Es benutzt kdialog, die Meldungen integrieren sich also Problemlos in den KDE-Desktop. Es ist auch recht kurz und einfach.

Einfach herunterladen und in $PATH ablegen. Zum Starten genügt:

nohup twitterNotify.sh &

Da es nicht sonderlich viel Aufwand betreibt, um die XML-Antworten der Twitter-API zu parsen kann nicht zu viel Information übermittelt werden, außer woher die tweets kommen.

Kommentare: keine

YaSvenT - eine PHP-GTK-Anwendung

10.01.2009, 08:46

Zugegeben - PHP ist nicht unbedingt die beste Programmiersprache wenn es um Desktop-Anwendungen geht. Doch um einen Einstieg in GUI-Programmierung zu finden, wollte ich erstmal mit einer mir gut bekannten Programmiersprache anfangen. Und mit den Objekten von PHP5 ist der Unterschied zu z.B. Java auch nicht mehr allzu groß, wenn auch deutlich spürbar. Also habe ich mich daran gemacht ein GUI-Basiertes Server-Kontrollzentrum zu basteln.

Einmal konfiguriert kann es Backups von MySQL-Datenbanken und den Dateien machen, das Error-Log anzeigen, das Access-Log in eine SQlite-Datenbank umwandeln und daraus einen HTML-Bericht erstellen, und Programme wie SSH-Konsole, Webbrowser, MySQL-Tunnel oder SFTP-Browser auf einen Klick vorkonfiguriert öffnen. Das ist recht praktisch, wenn man nicht den Ganzen Tag URLs und Befehlsfolgen tippen will. Da ich für das Meist sowieso schon Shell-Skripte hatte, lag das Ziel hauptsächlich darin, ein nettes GUI zu basteln.

Mit PHP Desktop-Anwendungen zu basteln hat den Nachteil, dass unter gängigen Linux-Distributionen zwar alles funktionieren dürfte, allerdings unter Windows PHP und GTK benötigt wird. Außerdem ist die Dokumentation von PHP-GTK noch nicht auf der vom übrigen PHP gewohnten Qualität. Einige Widgets sind nicht oder unvollständig gelistet, Beispiele sind teilweise rar und Englisch-Kenntnisse wären ratsam. Ich denke die nächste GUI-Anwendung werde ich in Java versuchen und dann vergleichen, aber mir kam es nicht so vor, als würde ich in einer Perl-Skriptesammlung für die Personal Homepage coden, und ich denke PHP ist langsam erwachsen.

Kommentare: keine
[ Seite 1 2 ] Nächste Seite »
 
Χρόνογραφ
© 2008-2017 by Bernhard Häussner - Impressum - Login
Kurz-Link zur Homepage: http://1.co.de/