ich habe in InDesign ein eigenes Panel laufen, bei dem ich mit Buttons verschiedene JSX ausführen kann, die sich auf das geöffnete InDesign-Dokument auswirken.
Jetzt möchte ich aber, dass in dem Panel selber was passiert. Zum Beispiel, dass er mir im Panel den Namen des gerade ausgewählten Objektes anzeigt. Ich weiß, dass ich das auch über die Ebenen-Palette sehen kann - allerdings wird das unübersichtlich, wenn ich dort viele Ebenen habe, die teilweise zugeklappt sind.
Jetzt muss das auch so funktionieren, dass ich nicht extra nen Button klicken muss. Sprich - wenn ich ein Objekt auswähle, soll er im Panel den Objekt-Namen anzeigen.
Du kannst den js-Code auch in dei Index-Datei schreiben. Bezüglich Wartung finde ich die Verlinkung und Auslagerung in eine eigene Datei aber praktischer.
So war es gedacht ;)
Mit dem EventListener »afterSelectionChanged«, meinst du das? Der Button war nur zur Demonstration.
Hab ein wenig mit afterSelectionChanged rumgespielt.
Über das Panel passiert gar nichts. Über ein externe Skriptdatei bekomme ich auch bei verschiedenen eventListener nur Fehlermeldungen, dass das Objekt nicht mehr verfügbar ist.
hat es geklappt. Allerdings bekomme ich beim InDesign öffnen, dass "undefined is not an object" ist. Vermute mal, dass das kommt, weil noch kein Objekt ausgewählt ist.
Außerdem muss ich erst Rolands Button zweimal im Panel klicken, bis auch der "afterSelectionChanged" funktioniert.
Hallo tsone, was heißt "beim InDesign öffnen" genau? Du startest InDesign und erwartest, dass die Arbeit mit app.selection funktioniert? Genau das ist aber nicht der Fall solange kein Dokument geöffnet ist.
Lass mal das hier als Startup-Script laufen:
Und dann ändere das Startup-Script zu dem hier:
Dazwischen natürlich InDesign neustarten.
[b]Warum versuche ich Textauswahlen auszublenden? Der Listener liefert mit Textauswahlen quasi jeden "Pups". Also zum Beispiel wenn die Einfügemarke im Text bewegt wird. Oder wenn der Anwender Text tippt, dann gilt bei jeder neuen Einfügemarke: "Hallihallo! Die Auswahl hat sich geändert!" Wirklich nach jedem Zeichen! Kann nervig werden. Braucht man jetzt nicht so rigoros umsetzen wie ich das hier mache, aber man sollte darauf gefasst sein, dass jede Menge Meldungen in kurzer Zeit auf einen zukommen können.
Es wäre zu überlegen, ob Du überhaupt mit afterSelectionChanged arbeiten solltest. Stattdessen könntest Du ein [b]IdleTask der app hinzufügen und dem einen listener für [b]IdleEvent.ON_IDLE mit einer entsprechenden Handler-Funktion mitgeben, die die Auswahl prüft und auswertet. Da könnte man sogar einen Intervall für's Nachschauen einbauen.
1. Ich hatte ja nur vermutet, dass Du bei der Aktivierung des Listeners kein Dokument offen hast. Oder den Fall: Der Benutzer schließt alle Dokumente. Dann sollte der Listener ja ohne Fehlermeldung einfach weiterlaufen. Bis der Benutzer entscheidet das Panel zu schließen.
2. Um eine echte Fehlerbehandlung zu machen, könntest Du mit try-catch arbeiten um den Fehler zu untersuchen.
Falls kein Dokument geöffnet ist, wirft das hier einen Fehler:
Welche Fehlernummer?
Du könntest darauf reagieren, und den weiteren Code nicht ausführen. Ob das für alle Situationen reicht? Möglicherweise nicht.
Im Falle des Schließens eines Dokuments bekomme ich einen weiteren Fehler geworfen: "Es sind keine Dokumentenfenster geöffnet" mit Fehlernummer 90886.
Den könntest Du dann auch in die Kategorie der "bekannten" Fehler einordnen. Vielleicht treten ja noch andere auf, die Du behandeln möchtest.
Also: Testen! ***** Mit herzlichem Gruß, Uwe Laubender
"Es sind keine Dokumentenfenster geöffnet" muss heißen: "Es sind keine Dokumentfenster geöffnet." $ID/NoWindowOpenError
Dieser Fehler wird auch geworfen, wenn Du beispielsweise mit mehreren Fenstern eines Dokuments arbeitest und eines davon schließt. Ob Textansicht oder Layoutansicht macht keinen Unterschied.
Die Formulierung der Meldung ist ein wenig widersprüchlich. Könnte etwas präziser sein. "Das Fenster der Auswahl ist nicht mehr geöffnet." wäre vielleicht besser…
Experimentiere auch mal mit dem Seitenwerkzeug während der Listener läuft. Weiss nicht, ob Du auch ausgewählte Seiten in Deinem Panel aufnehmen möchtest. ***** Mit herzlichem Gruß, Uwe Laubender
1. Mit der IdleTask fragst Du, bzw. kannst Du im Intervall abfragen wie es um die Auswahl bestellt ist. Und da InDesign im Moment der Abfrage "idle" ist, also nichts macht, wird der Anwender auch nicht ausgebremst. So jedenfalls die Theorie.
2. Mit dem afterSelectionChanged eventhandler wirst Du vermutlich stärker filtern müssen, wenn es um Textauswahlen geht. Es steht zu befürchten, dass das auf die Performance gehen könnte. Davon abgesehen: Du kannst den auch an das activeDocument hängen und musst ihn nicht unbedingt an der Application festmachen.
Was bei beiden passieren kann: Wenn Du mal einen Zähler mitlaufen lässt, wirst Du feststellen, dass der handler mal "durchrutscht". Soll heißen, dass manche events zwar registriert, der Zähler hochgezählt, die Aktion im handler aber nicht ausgeführt wird. Ist mir bei meinen Tests jedenfalls so ergangen. ***** Mit herzlichem Gruß, Uwe Laubender
Hallo Timo, ja, das ist 'ne "Wissenschaft für sich"…
Ich würde mich jedenfalls auf regen Gedankenaustausch freuen, wenn Du Dich weiterhin mit diesem Thema beschäftigst. Der Testaufwand ist nicht unerheblich. ***** Mit herzlichem Gruß, Uwe Laubender
Hallo Roland, ja, ich dachte auch erst, dass das so funktioniert.
Teste mal mit dem Story-Editor oder mit einem zweiten Fenster des gleichen Dokuments. Schließe dann den Story-Editor oder das zweite Fenster. Das wird einen Fehler werfen, den Du nicht abfängst.
Ich denke, dass wir beim afterSelectionChanged eventhandler nicht um ein ordentliches try/catch herumkommen.
Hier mal ein ScriptUi-Window vom Typ Palette, das dies mit Deinem Code verdeutlicht:
Du hast recht Uwe, da hat es was. Den Texteditor könnte man noch mit instanceof LayoutWindow abfangen, aber damit gibt es dann einen Fehler bei app.activeWindow.
Ich habe es jetzt mal damit versucht:
statt
Mit dem Texteditor wird dann halt die aktuelle Auswahl nicht angezeigt. Muss mir das aber auch noch mal genauer ansehen, wenn etwas mehr Zeit ist.
Hallo Roland, was stört Dich an einem try/catch, das die beiden bekannten Fehler $ID/NoDocumentOpenError 90884 und $ID/NoWindowOpenError 90886 erkennt und abfängt und nur bei neuen Fehlern beispielsweise protokolliert? ***** Mit herzlichem Gruß, Uwe Laubender
Hier die Version, die funktioniert. Anmerkung: Ich benutze ein ScriptUI-Fenster vom Typ "palette" mit einem "statictext"-Element, das einen Rollbalken aufweist.
Der Rollbalken funktioniert mit CS6. Aber nicht mehr mit CC oder höher.
Die Auswertung im Fenster enthält einen Zähler plus den Konstruktornamen aller ausgewählten Objekte. Das ist noch nicht sehr aussagekräftig für einen Anwender, dient nur dazu, das Konstrukt zu testen.
#targetengine rolandsIdea
var c = 0;
var w = new Window("palette"); var myStaticText = w.add ( "statictext", [0,0,250,100], c+" : "+"Es ist nichts ausgewählt." , { multiline: true , scrolling : true // Will not work in CC and above. No scroll bar available. } );
Wobei ich sagen muss, dass ich mir noch nicht ganz sicher bin, weshalb das funktioniert und der Fehler $ID/NoWindowOpenError mit Nummer 90886 nicht geworfen wird.
Beim Schließen eines Storyeditor-Fensters konnte ich diesen in früheren Versionen provozieren. Diesmal nicht. ***** Mit herzlichem Gruß, Uwe Laubender
was stört Dich an einem try/catch, das die beiden bekannten Fehler $ID/NoDocumentOpenError 90884 und $ID/NoWindowOpenError 90886 erkennt und abfängt und nur bei neuen Fehlern beispielsweise protokolliert?
An sich spricht – denke ich – in diesem Fall nichts dagegen, mit try/catch den Fehler zu umgehen. Wenn das mit windows[0].selection ginge, wäre es halt kürzer.
Wobei ich sagen muss, dass ich mir noch nicht ganz sicher bin, weshalb das funktioniert und der Fehler $ID/NoWindowOpenError mit Nummer 90886 nicht geworfen wird.
Ich hab testweise an windows[0] und windows[1] (bei 2 Fenstern eines Dokuments) die EventListeners AFTER_ACTIVATE und BEFORE_DEACTIVATE angehängt.
Beim Schließen von windows[1] feuert zuerst AFTER_SELECTION_CHANGED. app.selection ergibt zu diesem Zeitpunkt schon einen Fehler.
Danach löst BEFORE_DEACTIVATE von windows[1] aus und daraufhin AFTER_ACTIVATE von windows[0]. In Folge ist app.selection dann wieder gültig.
Das ist jetzt auch nur Spekulation, aber app.selection könnte eigentlich über windows[1] auf die aktuelle Auswahl zugreifen. Als aktives Fenster wird noch app.windows[1] geführt, obwohl es gar nicht mehr aktiv ist. Dieser Fehler wird aber in app.selection nicht abgefangen und bleibt es dann hängen.
app.windows[0].selection ist allerdings zu jedem Zeitpunkt gültig, egal, ob jetzt ein oder zwei Fenster geöffnet sind oder welches gerade den Index 0 hat.
Hallo Roland, hab Dank für's Testen. Ich muss aber mal nachfragen.
Ich hab testweise an windows[0] und windows[1] (bei 2 Fenstern eines Dokuments) die EventListeners AFTER_ACTIVATE und BEFORE_DEACTIVATE angehängt.
Ok.
Beim Schließen von windows[1] feuert zuerst AFTER_SELECTION_CHANGED. app.selection ergibt zu diesem Zeitpunkt schon einen Fehler.
Beim Schließen von windows[1] ? Also dasjenigen Fensters, das nicht das aktive ist? Das würde ich mal genauer begutachten wollen. Nur um nachzuvollziehen, wie Dein Versuchsaufbau war.
Hast Du das windows[1] per Mausklick über das UI geschlossen oder per Skriptbefehl mit windows[1].close() ?
Falls per Mausklick, wird ja dann windows[1] vermutlich kurzfristig zum aktiven Fenster. Und erhält somit den index 0. Oder auch nicht. Siehe die Fälle A und B weiter unten.
Wir sollten an dieser Stelle erstmal Begrifflichkeiten klären. Wenn Du von windows[0] und windows[1] sprichst, dann sprichst Du immer über die gleichen Fenster, oder?
Also Beispiel:
Dokument geöffnet. Zwei Fenster des gleichen Dokuments geöffnet. Fenster 1: windows[0], das aktive Fenster mit Namen: Unbenannt-1:1 @ 50% Fenster 2: windows[1], das nicht aktive Fenster: Unbenannt-1:2 @ 50%
Du schließt nun Fenster 2 per Mausklick. Wie?
A. Mit 2 Mausklicks? Klick ins Fenster, um es dann mit einem zweiten Mausklick zu schließen? Der Name von Fenster 1 ändert sich zu: Unbenannt-1 @ 53%
B. Mit 1 Mausklick? Klick in das x des Tabs von Fenster 2 ? Der Name von Fenster 1 ändert sich zu: Unbenannt-1 @ 53%
A. und B. könnten unterschiedliche Reihenfolgen von events auslösen. Im Fall A ändert sich auch der Index des Fensters bis zum Zeitpunkt des Schließens zwingend. Bei B nicht.
Und ein weiterer Unterschied könnte sein, wenn Fenster 2 per methode close() geschlossen wird. Könnte aber auch mit Fall B abgedeckt sein.
Vielleicht hab' ich ja am Wochenende ein weing Zeit, das genauer zu untersuchen. Ich hoffe, mit meinen Fragen oder Bemerkungen nicht allzusehr zu verwirren. ***** Mit herzlichem Gruß, Uwe Laubender
Ich hab in meinem Fall dem linken Fenster (zu Beginn aktiv) durch das Skript Window.AFTER_ACTIVATE angehängt und dem rechten Fenster Window.BEFORE_DEACTIVATE (beide gleiches Dok). Dann wurde das rechte Fenster aktiviert und über das UI (x) geschlossen.
Die Überlegung war einfach die, dass immer eines der Fenster den Index 0 aufweisen muss, auch wenn dieser zwischen den Fenstern wechselt.
Ich muss mich jetzt für ein paar Tage aus der Diskussion ausklinken, Urlaub \o/, bin eigentlich schon gar nicht mehr da ;) Vielleicht hast du bis dahin schon Genaueres herausgefunden.
Hallo Roland, danke für die genauere Beschreibung.
Bei mir sieht es allerdings auch so aus als könnte ich mich die nächsten 14 Tage kaum um den Fall kümmern.
Aber vielleicht geht ja Timo noch mal ran an die Sache.
Dir wünsche ich einen schönen Urlaub! Bei mir steht ein Umzug an. Zwar in der gleichen Stadt, aber trotzdem zeit- und nervenaufreibend. Lebe im Moment in einer Baustelle. Und das kann man wortwörtlich so nehmen. ***** Mit herzlichem Gruß, Uwe Laubender
dieses Thema trifft genau mein Problem, bei dem ich auch gerade hänge. Der "AfterSelectionChange"-Event tritt ein, wenn jemand ein Fenster schliesst - "app.selection" ist dann im EventHandler schon nicht mehr gültig. Gleiches Spiel mit app.activeDocument. Du hast oben diese Try/Catch-Anweisung vorgeschlagen, um den Handler-Fehler abzufangen ... nur, bei mir stürtzt Indesign dann ab, wenn er es 'tried'! Läuft das bei Dir? Magst Du mir helfen?
(Ich werde wohl auch auf das Idle-Event gehen. Meh! Ist halt nicht so Echt-Zeit, aber wohl sinnvoll.)
Liebe Grüße, Stephan
Hier mein Test-Script:
#targetengine miscellaneous var w = new Window ("palette { text: 'Test Palette', preferredSize: [220,16], alignChildren:['fill','top']}"); w.sText = w.add( "statictext", undefined, 'Tell Selection' );
var ad = app.activeDocument; var s = app.selection; } return (s.length == 0)?('Nothing selected'):(s[0].constructor.name); }
function updateSel() { w.sText.text = getParas(); w.graphics.backgroundColor = w.graphics.newBrush (w.graphics.BrushType.SOLID_COLOR, [Math.random(), Math.random(), Math.random()]); // to show when updateSel is called }
Hallo Stephan, deinen Code hab' ich noch nicht getestet. Mit welchem InDesign hast Du's probiert?
Da bei Dir bereits bei app.selection.length == 0 der Absturz kommt, kann ich an dieser Stelle einen Bug vermuten. Vielleicht funktionieren alternative Schreibweisen und ein sich Herantasten?
Sollte keinen Unterschied machen, aber wer weiß?:
app.properties.selection.toString() == ""
Vielleicht hilf ja auch das event-Objekt zu untersuchen? Möglich, dass da was Brauchbares vorhanden ist.
function updateSel( event ) { var x , e ; for( x in event ) { try{ $.writeln( x +"\t"+ event[x].toString() ) } catch(e){ $.writeln( x +"\t"+ e.message ) }; }; };
Hallo Stephan, hab' mal einen Kurztest mit Deinem Code und InDesign CC 2018 gestartet. Bisher keine Abstürze. Auch nicht nach Schließen von irgendwelchen Layout-Windows.
Problem allerdings: Das Dialogfeld kann hinter den Anwendungsrahmen geraten. ***** Mit herzlichem Gruß, Uwe Laubender
Danke Uwe, für das Testen und den Hinweis daß es bei Dir läuft. Habe mich dadurch erst dran gemacht nach Außen zu schauen, zwei Plugins und drei Autostartskripte entfernt... am Ende war es Rorohikos "StoryParker", daß im Zusammenspiel mit dem Testskript Indesign reproduzierbar einfror. Schon komisch.
Jetzt kann ich weiter machen. Dein Abfangen/Umwandeln der Event-Error-Messages hat mir auch geholfen. Und jetzt teste ich mal parallel, ob's mit dem Idle-Task eh eine bessere Lösung wird.
Hier noch die schnelle rescourcenschonenden verquickung des 'idleTasks' mit 'afterSelectionChanged' ...
Wenn die Selection sich ändert geht eine Flagge hoch. Und beim ersten Leerlauf guckt der Eventhandler erstmal nach der Flagge ob's überhaupt eine Veränderung abzuarbeiten gab. Wenn sich die Selection also sehr schnell und häufig ändert, dann wird darauf frühestens beim nächsten Leerlauf reagiert. Das ist toll, bei z.B. Texteingaben (insertion points).
Danke dafür, Uwe!
#targetengine miscellaneous var w = new Window ("palette { text: 'Test Palette', preferredSize: [220,16], alignChildren:['fill','top']}");
function getParas() { if (app.documents.length == 0) { return "No documents are open."; } else { try { var ad = app.activeDocument; var s = app.selection; } catch(e) { if(e.number == 90884 || e.number == 90886) { return "Closed Window" }; else { return "Error: "+e.number +"\r"+ e.message }; } } return (s.length == 0)?('Nothing selected'):(s[0].constructor.name); }
function updateSel(event) { if (w.sText.needs_update) { w.sText.text = getParas(); w.graphics.backgroundColor = w.graphics.newBrush (w.graphics.BrushType.SOLID_COLOR, [Math.random(), Math.random(), Math.random()]); // to show when updateSel is called w.sText.needs_update = false; } }
app.eventListeners.add("afterSelectionChanged", function (){w.sText.needs_update = true;},{name:'paraStyleChanger_selChange'});