[[TOC]] = Squeak FAQ (Stand !WiSe 21/22) = == Tastatur und Maus == === Wie schaffe ich es, dass ein Morph auf Tastaturevents reagiert? === Wenn ihr abfragen wollt, welche Taste aktuell gedrückt wird, oder wollt, dass bei einem Tastendruck ein bestimmtes Stück Code ausgeführt wird, stellt !GameMecha eine gute Lösung bereit. Solltet ihr genauere Tastaturevents benötigen, solltet ihr die Morphic Variante wählen. Die Morphic-Variante funktioniert grundsätzlich es folgendermaßen: 1. Es muss deutlich gemacht werden, dass der Morph bestimmte Events behandelt. Für Tastaturevents implementiert der Morph dafür folgende Methode, die einfach alle Tastaturevents akzeptiert. handlesKeyboard: anEvent ^ true 2. Wenn ihr das Event akzeptiert habt, dann wird es euch zur Behandlung übergeben über eine der folgenden Methoden: keyDown: keyUp: keyStroke: "Wenn die Taste gedrückt gehalten wird" 3. Das Image ist momentan so eingestellt, dass der Morph über dem die Maus steht die Events bekommt. Da diese Option nicht garantiert aktiviert ist, kann/muss man manchmal klarstellen an wen die Keyboardevents gerade gehen sollen. Im Pong Beispiel seht ihr in SWAPong>>#mouseEnter:, SWAPong>>#mouseLeave: wie man das machen könnte. Um herauszufinden, wie ihr mit Events umgeht schaut dann einmal in die Implementierung von Pong oder setzt einfach mal einen Breakpoint in der Methode (indem ihr die Methode `halt` auf irgendeinem Object aufruft, z.B. `self halt`) die das Event behandelt (keyDown:, keyUp: oder keyStroke:). Dann könnt ihr euch direkt ansehen wie so ein Event aussieht. Alternativ schaut euch mal !UserInputEvent und die entsprechenden Unterklassen an. === Wie schaffe ich, dass ein Morph auf Mausevents reagiert? === 1. Wie bei Tastaturevents muss zuerst eine Methode implementiert werden, die angibt, ob ein Morph Events bekommen will. Diese Methoden heißen wie folgt: * {{{ handlesMouseDown:}}} * {{{ handlesMouseMove:}}} * {{{ handlesMouseOverDragging:}}} * {{{ handlesMouseOver:}}} * {{{ handlesMouseStillDown:}}} * {{{ handlesMouseWheel:}}} 2. Wenn der Morph ein Event behandelt dann wird eine der folgenden, entsprechenden Methoden aufgerufen: * {{{ mouseDown:}}} * {{{ mouseEnterDragging:}}} * {{{ mouseEnter:}}} * {{{ mouseLeaveDragging:}}} * {{{ mouseLeave:}}} * {{{ mouseUp:}}} * {{{ mouseMove:}}} * {{{ mouseStillDown:}}} (siehe auch Morph>>#mouseStillDownThreshold) * {{{ mouseUp:}}} * {{{ mouseWheel:}}} == !GameMecha == === Was ist !GameMecha und wieso sollte ich es benutzen? === !GameMecha, kurz für Game Mechanics, ist ein Projekt welches von Studierenden für Studierende erstellt wurde. Es stellt einige, bewusst simpel gehaltene, Funktionalitäten bereit, welche in SWA oft benötigt werden. !GameMecha stellt also Standardlösungen bereit, damit diese schnell umgesetzt werden können und anderen Aspekten des Spiels dadurch mehr Zeit zur Verfügung steht. Es ist keine Pflicht !GameMecha zu verwenden, insbesondere wenn komplexere Varianten benötigt werden als !GameMecha bereitstellt. === Was kann !GameMecha? === ==== Kollisionserkennung ==== !GameMecha stellt ein Framework für Kollisionserkennung zwischen Morphs bereit. Die Kollisionserkennung ist dabei nicht Pixel-genau sondern für rechteckige und runde Bounding Boxes ausgelegt. Den Morphs wird dazu eine `GMCollisionDetectionShape` über #gmCollisionDetectionStrategy: zugewiesen. Danach werden die Morph einem `GMCollisionHandler` hinzugefügt, über welchen anschließend abgefragt werden kann, ob zwei Morphs miteinander kollidieren. Ein Beispiel wie die !GameMecha Kollisionserkennung verwendet wird ist in `GMCollisionDetectionAcceptance` zu sehen. ==== Tastatureingaben ==== ===== Tastendruck abfragen ===== !GameMecha ermöglicht es bei Morphs betriebssystemunabhängig abzufragen, welche Tasten aktuell gedrückt sind. Dazu muss der Morph über #gmRegisterToKeyHandler bei dieser !GameMecha Funktionalität registriert werden. Im Anschluss kann, z.B. im #step, über #gmIsKeyPressed: abgefragt werden, ob ein bestimmter Character gedrückt ist. Ein Beispiel ist in `GMKeyHandlerAcceptance` und `GMKeyHandlerAcceptanceKeyMorph` zu finden. ===== Bei Tastendruck Code ausführen ===== Es ist auch möglich, bestimmte Aktionen auf Tastendrücke zu registrieren. Ein Beispiel für den Ablauf ist in `GMRegisterKeyEventsAcceptance` zu finden. ===== Betriebssystemabhängige Keycodes ===== Wenn ihr nicht !GameMecha verwendet und das Problem habt, dass die Keycodes die ihr erhaltet auf jedem Betriebssystem unterschiedlich sind, könnt ihr das Character-Keycode-Mapping von !GameMecha wiederverwenden. Seht euch dafür auf der Klassenseite von `GMKeyHandler` die entsprechenden Methoden an, #initializeKeyLookupLinux etc. ==== !Bilder/Sounds laden & cachen ==== Über die Subklassen von `GMResourceLoader` können !Bilder/Sounds vom Dateisystem geladen und gecached werden. Beispiele sind in `GMAnimatedImageLoaderAcceptance`, `GMImageLoaderAcceptance` und `GMSoundLoaderAcceptance` zu finden. ===== !Bild/Sound in Methode speichern ===== Wenn ihr eure Resourcen in Methoden speichern möchtet, könnt ihr dies über GameMecha tun. Verwendet dafür die Subklassen von `GMResourceLoader`. Beispielsweise: {{{ GMImageLoader new storeResourceInMethod: #createdMethod inInstance: GMDemoGameFixtures fromFile: 'planet.png' "'planet.png' sollte im 'Resources' Ordner liegen. Es wird eine Methode #createdMethod auf Klassenseite von GMDemoGameFixtures angelegt." }}} ==== Beispiel für Maus Handling ==== !GameMecha selbst stellt keine Interaktionen mit der Maus bereit, in `GMMouseHandlingTutorial` findet ihr allerdings ein Beispiel, wie man in Squeak mit der Maus umgeht. === Gibt es Beispiele, wie man !GameMecha verwendet? === !GameMecha kommt direkt mit einigen Beispielen. Beispiele für einzelne Funktionalitäten sind in im Paket `GM-AcceptanceTest` zu finden. Die meisten dieser Beispiele sind Morphs und können geöffnet werden, z.B. über {{{GMCollisionDetectionAcceptance new openInWorld}}}. Ein größeres Beispiel mit mehreren Funktionalitäten ist das `GMDemoGame`. Auch dieses kann geöffnet und im gespielt werden. == Wie füge ich einer "fremden" Klasse eine Methode hinzu? (z.B. #isMario zu Morph) == Um Methoden, die nicht zu Klassen innerhalb eures Projekts gehören, doch in eure Projekt-Pakete / GitHub Repository mit einzubeziehen, damit ihr diese Erweiterungen oder Anpassungen bei euch speichern könnt, gibt es das Konzept der '''Extension Methods'''. Jede Methode, die in einer Methodenkategorie `*Packagename` oder `*Packagename-xyz` liegt, wird als Extension Method dem Paket ''Packagename'' angerechnet. Die Versionsverwaltung erkennt diese Methoden und wird sie berücksichtigen, wenn eine neue Version von ''Packagename'' gespeichert werden soll. Habt ihr Methoden in fremden Klassen verändert, die es schon vor eurem Projekt gab (wenn ihr also bestehendes Verhalten invasiv für eure Zwecke abändert), solltet ihr diese Methoden in eine Kategorie `*Packagename-override` einordnen. == Swa Lint == Ihr könnt euren Code mit einem bereits in eurem Image vorhandenen Linter analysieren. Wichtig: Ein Linter zeigt euch durhc Analyse interessante Stellen - aber ob zum Beispiel eine Methode lang ist, weil sie refactored werden muss oder weil die Länge sinnvoll ist, müsst ihr als Team entscheiden. Auch sind vermutlich nicht alle Tests für euch von Relevanz; es ist übersichtlicher eine wirklich relevante, kleinere Menge Linter Tests laufen zu lassen. Kategorien wie 'Pharo bugs' sind für euer Projekt nicht relevant. Ihr könnt !SwaLint oben in der Main Docking Bar unter Apps als !SwaLint Code Critics finden. Ihr könnt auch {{{SwaLint open}}} im Workspace ausführen. == Wie können wir einfache Nutzerdialoge einbauen, ohne eine eigene Ui dafür zubauen? (z.B. einen Spielernamen abfragen) == Für einfache Dialoge könnt ihr die UIManager Klasse verwenden. Mit {{{UIManager default request: 'Please enter your name'}}} könnte man zum Beispiel einen Spielernamen für eine Highscoreliste abfragen. == Wie erstellt man ein .sar für die Abgabe? == Ein sar-File ist einem zip ähnlich (ein sar enthält eine preamble, wodurch es installiert werden kann) und enthält euren Code sowie relevante Daten. Ihr könnt euch an diesem Skript orientieren: {{{ "Insert your info here" sarname := 'MyProject'. packages := {'BaselineOfMyProject'. 'MyProject-UI'. 'MyProject-Core'}. folders := {'MyData'. 'MyData2'}. "init" preambleContent := ''. zip := ZipArchive new. "add packages" packages do: [:package | mczStream := RWBinaryOrTextStream on: (String new: 10000). workingCopy := MCWorkingCopy forPackage: (MCPackage new name: package). version := workingCopy newVersion fileOutOn: mczStream. (zip addString: mczStream contents as: package, '.mcz') desiredCompressionLevel: 0. preambleContent := preambleContent, 'self fileInMonticelloZipVersionNamed: ''' , package, '.mcz''.', String cr]. "add folders" folders do: [:folder | zip addTree: Smalltalk imagePath match: [:e | e fullName beginsWith: Smalltalk imagePath , FileDirectory slash , folder]. preambleContent := preambleContent, '(self membersMatching: ''' , folder, ''', FileDirectory slash, ''*.*'') do: [ :f | self extractMember: f].', String cr.]. "only add one preamble!" zip addString: preambleContent as: 'install/preamble'. "write sar" zip writeToFileNamed: sarname , '.sar'. }}} Weitere Informationen und Anleitungen siehe [http://wiki.squeak.org/squeak/3324 SARInstaller Dokumentation]. == Mac == Wenn ihr Squeak unter MacOS nutzt, müsst ihr das aus dem Ordner herausziehen in dem es nach dem entpacken liegt. Ansonsten kann Squeak gewisse Dateien nicht schreiben, was problematisch ist. (Hier ist mehr zum Hintergrund: https://www.synack.com/blog/untranslocating-apps/) = Squeak FAQ Archiv = == Dokumentation == === Wo finde ich Dokumentation? === Es gibt für einige Klassen Dokumentation gleich im Browser. Der "?" Button zwischen Class und Instance zeigt die Klassenkommentare an. Weiterhin findet ihr hier Informationen: * [https://github.com/hpi-swa-lab/SqueakByExample-english Squeak by Example] * [http://static.squeak.org/tutorials/morphic-tutorial-1.html Morphic Tutorial] * [http://wiki.squeak.org/squeak/30 Squeak-Wiki] * [https://www.hpi.uni-potsdam.de/hirschfeld/trac/SqueakCommunityProjects/wiki/squeak_screencasts Squeak Screencasts] === Woher ist gute Einführungsliteratur für !Squeak/Smalltalk zu finden? === Das Buch [https://github.com/hpi-swa-lab/SqueakByExample-english Squeak by Example] ist besonders geeignet, da es explizit auf alle wichtigen Tools in Squeak und auch auf das Morphic-Framework eingeht. Außerdem wird an vielen Beispielen die minimale Syntax und wichtige Konzepte wie die Nachrichtenbasierung erklärt. Weiterhin gibt es noch einige praktische Tutorials zu Morphic im Squeak-Wiki, die auch Hinweise zur Arbeit mit Squeak geben. [http://wiki.squeak.org/squeak/792#Morphic Morphic-Wiki] Zum Einstieg in !Squeak/Smalltalk sind viele gute Bücher kostenlos verfügbar. Eine gute Übersicht bietet [http://stephane.ducasse.free.fr/FreeBooks.html Stephane Ducasse: Free Books] [https://www.hpi.uni-potsdam.de/hirschfeld/trac/SqueakCommunityProjects/wiki/squeak_screencasts Screencasts] über Squeak gibt es ebenfalls. == Grafik und Morphic == === Wie werden Grafiken im Image verwendet? === ==== Anzeigen von Bildern ==== #images-in-squeak Folgender Code zeichnet das Bild anImage.jpg das in dem Ordner liegt in dem ihr auch das ! findet in einen `ImageMorph`: {{{ ImageMorph new image: (Form fromFileNamed: 'anImage.jpg'); openInHand. }}} Wenn das Bild in einem Unterordner liegt, ermöglicht die Klasse `FileDirectory` plattformunabhängige Dateipfade ("\" für Windows, "/" für Linux und OS X) {{{ ImageMorph new image: (Form fromFileNamed: (FileDirectory uri: 'folder/anImage.jpg') fullName); openInHand }}} Der Dreh- und Angelpunkt ist hier die Klasse `Form`. Die Basis von `Form` ist die "Squeak Canvas", eine Bitmap, die Bilder enthält, oder auf der gezeichnet werden kann. Diese Objekte können dann in einem `ImageMorph` verwendet werden. Es gibt in Squeak den `ImageReadWriter` als abstrakte Klasse und dazu verschiedene Implementierungen für z.B. BMP und PNG (`PNGReadWriter`, `BMPReadWriter`). Auf Klassenseite von `ImageReadWriter` findet sich dann auch `ImageReadWriter class>>formFromFileNamed:`. {{{ ImageMorph new image: (ImageReadWriter formFromFileNamed: (FileDirectory uri: 'folder/anImage.jpg') fullName); openInHand }}} ==== Speichern und Austauschen von Bildern ==== Bilder können auch im Image gespeichert werden. Das hat den Vorteil, das ihr die Bilder über Monticello verwalten könnt und so alle Teammitglieder über das Repository die aktuellen Bilder bekommen. Dazu speichert ihr die Bilder in Methoden, die Array-Repräsentationen der Grafiken zurückgeben. Dazu ladet ihr das Bild, wie oben beschrieben, zuerst in eine Form und aus dieser erzeugt ihr dann das Array (siehe. z.B. `Form>>storeOn`). === Welche Dateiformate für Bilder sind für Squeak geeignet? === Benutzt hier am besten PNG, JPG oder BMP. Zum Einlesen bietet sich dann beispielweise die `ReadWriter` Klassen an. Übrigens können Exemplare der Klasse `Form` auch mit Transparenz umgehen. === Können !ImageMorph bzw. deren enthaltene Form mit Alphatranparenz umgehen? Wie können ganze Morphs ausgeblendet werden? === Verwendet die Methode `Morph>>hide`, um einen Morph sofort ohne fade out zu verstecken. Denkt bitte daran, dass ausgeblendete Morphs dennoch im Speicher verbleiben. Eine Squeak-Methoden-Suche nach `alpha` oder `alpha:` (eine Nachricht, die wahrscheinlich Klassen haben, bei denen man eine Transparenz einstellen kann) liefert eine Klasse namens: `AnimAlphaBlendingCanvas`. === Wie schaffe ich es, dass ein Morph nicht mehr verschiebbar ist? === Werft einen Blick auf die Methode `Morph>>lock`. === Wie können Formen/Bilder skaliert werden? === {{{ImageMorph}}}s lassen sich zwar nicht skalieren, jedoch {{{Form}}}s. Hierfür würde man die folgenden Methoden verwenden: {{{ Form >> #scaledToSize: Form >> #scaledToSize:smoothing: Form >> #scaledIntoFormOfSize: Form >> #scaledIntoFormOfSize:smoothing: Form >> #magnifyBy: }}} Damit kann, verzerrt oder die Größe von Forms geändert werden: {{{ (ActiveWorld imageForm scaledToSize: 64@64 smoothing: 8) asMorph openInHand. (ActiveWorld imageForm scaledIntoFormOfSize: 64@64 smoothing 8) asMorph openInHand. (ActiveWorld imageForm magnifyBy: (64@64) / ActiveWorld extent) asMorph openInHand. }}} === Wie können Imagemorphs skaliert werden? === {{{ form := ImageMorph defaultForm. form := form magnify: form boundingBox by: 2@7 smoothing: 2. form asMorph openInWorld. }}} === Warum habe ich Lücken zwischen meinen ThumbnailImageMorphs? Wenn ThumbnailImageMorphs in einem Schachbrettmuster angeordnet werden, dann kann Folgendes passieren: 1.) Layouting Das Layouting versucht anhand der Spezifikation die zum Beispiel durch die Frames gegeben wurden die Morphs passend zu positionieren und zu skalieren. Dabei kommen manchmal auch Gleitkommazahlen für die konkreten Bounds der einzelnen Morphs heraus. Da am Ende alles auf Pixel abgebildet wird werden diese Werte aber auf Ganzahlen gerundet. Das ist erst einmal nicht weiter schlimm, da damit eure Spezifikation bis auf einen kleinen Fehler eingehalten wird. Der Fehler ist auch nicht für jede Zelle wieder plötzlich neu sondern zum Beispiel alternierend zwischen Reihen. 2.) Anpassen der Morphs Für normale Morphs ist ersichtlich, dass alles bündig liegt. Das liegt daran, dass die Morphs die neue Größe die vom Layout vorgegeben wurde einfach akzeptieren und sich so zeichnen. Die ThumbnailImageMorphs sind eigentlich für eben genau das gedacht: Thumbnails. Darum versuchen sie so lange wie möglich ihr Seitenverhältnis beizubehalten (bis zu einem Verhältnis von 3), dann wird doch gestaucht. Das führt dazu, dass ihr, wenn ihr zwei verschieden hohe Reihen habt, die ThumbnailImageMorphs in diesen Reihen auch unterschiedliche Breiten haben werden. === Ein Morph hat einen Rahmen, weil er KeyboardFocus hat; wie bekomme ich diesen weg? === Hierfür gibt es die Morphic Property indicateKeyboardFocus. Mann kann diesen entfernen entweder mit {{{ aMorph setProperty: #indicateKeyboardFocus toValue: #never }}} setzen, oder die Nachricht {{{indicateKeyboardFocus}}} in der benutzerdefinierten Morphklasse muss überschrieben werden (diese muss dann false zurückgeben). === Wie kann ich Text in einem Textmorph zentrieren? === {{{ TextMorph new contents: 'Hallo'; wrapFlag: true; width: 300; centered; lock; openInWorld. }}} === Wie passt man die Schrift in einem TextMorph an? === {{{ "String + TextAttribute = Text" text := 'Hello, World' asText. text addAttribute: TextEmphasis bold. text addAttribute: TextEmphasis bold from: 1 to: 5. text asMorph openInHand. "... bold, italic, colorful, fonts, ..." TextAttribute browseHierarchy. "Fonts" FontImporterTool open. "Install new fonts into Squeak." StrikeFont actualFamilyNames. "Browse and explore" TextStyle fontSizeSummary. "Browse and explore" "Fonts are queriesd by family name, point size, and emphasis code" font := StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 17. font := StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 17 emphasized: TextEmphasis italic emphasisCode. "Note: While emphasis (bold, italic, underlined) can be *in* the font, color is only attached to fonts via text attributes to be used in font-rendering code." "Change/override the font for all characters in the text." text addAttribute: (TextFontReference toFont: font). "Configure text (attributes) through text morphs. Not really recommended." tm := TextMorph new. tm contents: 'Hello, World!'. tm beAllFont: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 17). tm openInHand. }}} === Wie finde ich die Schriftgröße, sodass ein Morph ohne Zeilenumbruch befüllt wird? === {{{ TextMorph beAllFont: aFontFamily scaledToWidth: targetWidth | font | (Interval from: 200 to: 1 by: -1) detect: [:pointSize | font := TTCFont familyName: aFontFamily pointSize: pointSize emphasis: (text emphasisAt: 0). (font widthOfString: text) <= targetWidth]. self beAllFont: font }}} Wenn das obige zu langsam ist, kann auch {{{#findBinary:}}} verwendet werden für ein laufzeitoptimierte Variante. === Unser Spiel basiert auf Stepping und ruckelt!?! Was können wir tun? === ==== Wieso stepping zu langsam sein kann ==== Das Stepping Model von Morphic ist relativ einfach und basiert auf einem Prozess, welcher alle stepping Methoden ausführt, wenn ihre `Morph>>stepTime` abgelaufen ist. Im Umkehrschluss heißt das aber auch je mehr `Morph>>step` Methoden aufgerufen werden, desto mehr Zeit wird zwischen einzelnen Schritten benötigt (da mehr berechnet wird). Die `#stepTime` gibt dabei einen Wunschwert an, nach dem die `#step` Methode wieder aufgerufen werden soll. Nun kann es aber sein, dass die `#step` Methoden soviel zu tun haben (oder andere Prozesse), dass die Überprüfung nur alle 50 ms ausgeführt wird und somit kann eine Zeit von 33 ms nicht garantiert werden. Allerdings wird durch die beschriebene Bewegung eine explizite `#runStepMethods` Message an die Welt gesendet, welcher die Abarbeitung aller steps auslöst. Das heißt der UI Prozess löst bei seiner Reaktivierung eine Neuberechnung aus. ===== Lösungen ===== 1. In Preferences gibt es eine Option "higher performance" diese sorgt für ein häufigeres updaten des UI Prozesses, dass könnte ggf. helfen. 2. Anstatt häufig kleine Schritte auszuführen, kann ggf. eine größere Schrittweite die Animation flüssiger wirken lassen. 3. Die wahrscheinlich schnellste Alternative ist, eine Game Loop zu implementieren (Design Pattern für Spiele), welche abhängig von der verstrichenen Zeit die Animation berechnet und somit immer gewährleistet, dass unabhängig von der Performance die Animation zu einem Zeitpunkt überall gleich weit ist (ruckelnd oder nicht). Dann würdet ihr komplett auf das Stepping Protokoll verzichten. ==== Unser Spiel ruckelt nur auf MacOS!?! Was können wir tun? ==== Das Rendering von Squeak nutzt Heuristiken um zu entscheiden welche Bereiche des Bildschirms neu gezeichnet werden müssen. Diese führen bei Spielen mit wenigen Morphs zu einem Verhalten, dass die MacOS VM nicht gut umsetzen kann. Als Resultat benötigt das Zeichnen eurer Morphs viel mehr Rechenzeit als eigentlich nötig ist. Die dafür Lösung ist es bei jedem Step dem obersten Morph eures Spiels die Nachricht `#changed` zu schicken. Damit drückt ihr aus, dass sich dieser Morph geändert hat und komplett neu gezeichnet werden soll. === Wie werden Transformationen auf einen Morph angewandt? === Transformationen können mit einem !TransformationMorph realisiert werden: {{{ morph := RectangleMorph new openInWorld. transform := morph addFlexShell. "Creates a TransformationMorph" transform openInWorld. transform angle: 45 degreesToRadians. transform scale: 3. }}} === Wie kann man einem !SystemWindow ein Icon geben? === Schau dir mal die "initialization" Kategorie bei den !SystemWindow Methoden an: Dort wirst du die labelArea finden, die u.a. auch für die Buttons und den Titel verantwortlich ist. Der labelArea kannst du auch Morphs hinzufügen. Einfach mal im Explorer (Alt+Shift+I) auf {{{ SystemWindow new openInWorld }}} === In welcher Auflösung soll das fertige Spiel laufen? === Das ist euch generell freigestellt. Wenn ihr das Spiel auf dem XO in Vollbild laufen lassen wollt, dann solltet ihr eine etwas höhere Auflösung einplanen. Der XO-1 hat eine Auflösung von 1200x900 [http://laptop.org/en/laptop/hardware/specs.shtml XO Specs] === Wie ist es möglich die Größe eines Morphs auf die Größe der World zu setzen (selbst wenn diese sich ändert)? === Es gibt aktuell keine Benachrichtigung, wenn sich die Fläche des von Squeak bemalten Bereichs verändert. Morphic's Hauptschleife schaut in jedem Durchlauf explizit nach und passt gegebenenfalls die Dimension der Welt (also: Instanz von PasteUpMorph) an. Siehe {{{WorldState >> #doOneCycleNowFor:}}} und {{{DisplayScreen class >> checkForNewScreenSize}}}. Im Endeffekt landet das in {{{Project >> #displaySizeChanged}}} beziehungsweise {{{PasteUpMorph >> #restoreMorphicDisplay}}}. Allerdings gibt es keinen Weg für euer ein Spiel, dies elegant einzubinden. Daher sollte der Morph steppen und in jedem {{{#step}}} wird die Größe neu gesetzt: {{{ step self isInWorld ifTrue: [ self extent: self world extent] }}} beziehungsweise {{{ step self owner ifNotNil: [:owner | self extent: owner extent] }}} Das ist kein spürbarer Mehraufwand zur Laufzeit, wie ihr in {{{Morph >> #extent:}}} nachschauen könnt. Das allgemeine Konzept, seine eigene Größe automatisch an den Owner anzupassen -- also LayoutPolicy -- wird hier nicht benutzt. PasteUpMorph's haben keine LayoutPolicy. === Wir wollen einen Morph (inklusive Submorphs) als .png Datei abspeichern. === Verwendet dazu `Morph>>exportAsPng`. === Wie kann man einige Farben in einem Array speichern, und diese einer Variablen zuweisen, die für die Hintergrundfarbe eines Morphs zuständig ist? === Das !ColorArray scheint seiner Implementierung zu Folge etwas speicherfreundlicher zu sein als ein normales Array mit Color Objekten. Diese besondere Art kodiert Farben in ihre 32 Bit Repräsentation und speichert diese ab also genau 4 Byte. Beim näheren betrachten der Color Class fällt allerdings auf, dass Farben durchaus mehr Speicher in Anspruch nehmen können (durch cache Mechanismen usw.). Daher kann es in bestimmten Situation sinnvoll sein (wenn viele Farben benötigt werden z.B. in einem Bitmap oder einer Colorform) diese in nur 4 Byte abzulegen. Vermutlich kann hier auch die VM etwas optimieren, wenn grundsätzlich 4 Byte pro Element festgelegt sind. === Gibt es eine gute Möglichkeit in !Squeak/Morphic mit Vektorgraphiken zu arbeiten? Am besten wäre natürlich SVG. === Squeak hat von Haus aus keinen SVG Support. Es gibt aber das [http://map1.squeakfoundation.org/package/7318c055-ce88-4a98-8d4d-2b8cf92e1920/autoversion/4 SVGMorph Package] von Gary Chambers, das ihr euch per Installation der entsprechenden .mcz in des Image holen könnt. Dieses Paket hat bereits für einige Projekte gut funktioniert. === Gibt es in Squeak eine Möglichkeite Bilder in eine Package einzubetten als Ressource? Oder kann man Bilder nur aus Dateien laden? === Die Klasse !SystemWindow besitzt eine Reihe von Klassenmethoden, die allesamt auf *Image enden. (z.B. `SystemWindow>>menuBoxImage`) Diese Methoden geben Arrays zurück, die die Bildinformationen enthalten aus denen dann Form-Objekte erzeugt werden. `Form` ist die "Squeak's Canvas". Eine Bitmap, die Bilder enthalten kann oder auf der gezeichnet werden kann. Diese Objekte können dann in Exemplaren von `ImageMorph` verwendet werden (Der dazu ähnliche `SketchMorph` ist veraltet). Wenn Bilder in Methoden und damit im Image gespeichert werden sollen, können Array-Repräsentationen gewonnen werden, indem das Bild, wie oben beschrieben, in eine Form geladen wird und aus dieser dann das Array (z.B. `Form>>storeOn`). Das Speichern von Bildern im "Quellcode"/Image hat den einfachen Vorteil, dass ihr sie mit allen Teammitgliedern mit Hilfe des Monticello Source Code Repository teilen könnt. Bei externen Dateien muss leider ein anderer Weg (z.B. Dropbox, Git, SVN) genutzt werden. Denkt bitte auch daran einen Unterordner für euer Projekt innerhalb des Squeak Verzeichnisses zu nutzen. Falls ihr sehr viele Bilder habt kann es sein, dass sowohl euer Source Code als auch euer Image zu groß werden um damit sinnvoll umzugehen (bzw. am Ende des Jahres im Image mit allen Spielen wird es bei 18 x 200 MB schon langsam knapp auf Platformen wie dem Rasberry PI). In diesem Fall empfehlen wir euch externe Dateien zu verwenden und diese nur bei Bedarf zu laden und wieder freizugeben sobald sie nicht mehr benötigt werden. Entsprechende Muster behandeln wir in der Vorlesung. Siehe Einträge zu [#platform-path-separator Plattformunabhängige Dateipfade] und [#images-in-squeak Bilder in Squeak]. === Wie formatiert man Text in Squeak? === In Squeak gibt es auch für Text einen bestimmten Morph. Den !TextMorph. Der hat auch weitere Halos, die einem erlauben mögliche Kombinationen aus Schriftarten und Schriftgrößen zu betrachten.. Ein Beispiel für sehr großen Text: {{{ TextMorph new contents: (Text string: 'very important text' attribute: TextEmphasis italic); beAllFont: (StrikeFont familyName: #BitstreamVeraSans pointSize: 120); openInWorld. }}} Anzumerken ist, dass der von Textmorphs enthaltene Text direkt auf diesem verändert werden kann. Dabei ist die Eingabe in !TextMorphs durchaus gewollt. Wenn ihr dies verhindern wollt, müsst ihr sie locken. Weiterhin brechen diese Morphs automatisch den Text um, wenn der Platz (definiert durch die Bounds) nicht ausreichend ist. Das so genannte Word Wrapping kann aber auch deaktiviert werden (siehe `TextMorph>>wrapOnOff`). Wenn in einer Zeile mehr als nur 3-4 Zeichen geschrieben werden sollen, so muss man ihm eben mehr Platz geben also vergrößern. Beide Eigenschaften lassen sich auch mit dem vorgefertigten !TextMorph über Halos und Menüs implementieren. === Wie kann ich das mouseDown-Event an den Owner weiterleiten? === Falls der Owner alle Mouse-Down-Events seiner Kinder abfangen soll, ist `#mouseDownPriority die bessere Lösung. Bei `#rejectsEvent:` müsste jeder mögliche Submorph-Typ modifiziert werden. Dies würde Code-Duplikation bedeuten. === Wir haben mehrere Prozesse in unserem Spiel und beim Rendern treten Artefakte in den Morphs auf. Was passiert und wie können wir das lösen? === Das könnte mit der Synchronisierung zu tun haben. Wenn du Eigenschaften von Morphic-Objekten aus einem anderen Thread heraus veränderst, riskierst du, dass das Drawing der Objekte nur die Hälfte deiner Veränderungen mitbekommt (falls es zufällig "im selben Moment" ausgeführt wird). - Und das kann lustige Effekte haben. Es gibt aber die Möglichkeit, diese Veränderungen synchron im UI-Prozess von Squeak auszuführen. Dafür fügt man eine "Deferred Message" zu einer Liste hinzu, die im UI-Thread dann später abgearbeitet wird und eure Veränderungen in einem Block kapselt. Siehe dazu das folgende [http://osdir.com/ml/lang.smalltalk.squeak.general/2004-12/msg01057.html Beispiel]. Einfacher ist es für euch aber eventuell, wenn ihr eure Game-Logic nicht mithilfe eines neuen Threads schedulen lasst, sondern z.B. das Stepping eines einzigen Morph-Objektes für eure Game-Loop verwendet (d.h. die step-Methode dieses Objekts führt den Rumpf eurer Schleife aus und ihr setzt die stepTime entsprechend). == Sound == === Gibt es eine Möglichkeit Soundeffekte aus Dateien zu laden und abzuspielen? === Die gibt es: {{{ (SampledSound fromWaveFileNamed: 'daffyduck1.wav') play }}} Interessante Klassen sind hier !SoundSystem default (bzw !BaseSoundSystem und die ganze Sound-Synthesis Kathegorie) === Immer wenn wir einen Sound abspielen gibt es davor einen kurzen Lag. Wie können wir den Sound ohne Verzögerung abspielen? === Die Verzögerung liegt daran, dass das Sound-System in Squeak erst einen Prozess zum Befüllen des SoundBuffers startet. Erst wenn dieser Prozess läuft kann euer Sound abgespielt werden. Ihr könnt das lösen, indem ihr kontinuierlich einen "leeren" Sound abspielt, z.B. mit `RepeatingSound>>#repeatForever:`. Falls ihr eine Hintergrundmusik abspielt, hat das den selben Effekt. == Smalltalk als Programmiersprache == === Was ist der Unterschied zwischen Arrays und literal Arrays? === Mit den geschweiften Klammern ist die allgemeinere Art ein Array zu erzeugen. In den Klammern stehen durch Punkte getrennt Statements, also wirklich Code-Teile. Das können direkt Zahlen/Strings/etc sein, aber auch tatsächlich Logik. Array - {{{ {1. 2. 3} }}} zum Beispiel würde {{{ { 1 + 1. 1 + 2.} }}} zu einem Array mit 2 und 3 drin führen. Weil es sich um Code handelt, der ausgeführt werden muss, steht der Inhalt des Arrays erst zur Laufzeit (also wenn der Code ausgeführt wird) fest. Der Code kann deswegen auch andere Objekte enthalten, welche erst zur Laufzeit feststehen, wie Variablen. Zum Beispiel führt {{{ myNumber := 41. { 'foo'. 1 + myNumber} }}} zu einem Array mit 'foo' und 42. Bei literal Arrays kann kein Code übergeben werden, sondern nur Literale. Dadurch steht der Inhalt immer fest, nicht erst zur Laufzeit. Die Werte werden durch Leerzeichen getrennt und werden auch leicht anders erzeugt: {{{ #(1 2 3) }}} Wenn "Text" drin steht ohne als String ('a') oder Symbol (#a) markiert zu sein, wird er automatisch zu Symbolen umgewandelt. So wird {{{ #(hallo welt 42) }}} zu einem Array mit #hallo #welt und 42. Wie Code wird es nicht ausgewertet und Variablen können deshalb auch nicht hineingegeben werden. Deshalb führt {{{ myNumber := 41. #( 1 + myNumber) }}} zu einem Array mit 1 und #+ und #myNumber == Image == === Wie fügt man eine Method zu einer Klasse hinzu? === Hierfür muss zuerst mit dem Browser in die Klasse navigiert werden, in der eine neue Methode erstellt werden soll. Hat diese Klasse bereits Methoden, wird eine neue Methode angelegt, indem man den Methodennamen ändert und dann speichert. Das ist am Anfang etwas ungewöhnlich aber spart einen extra Button. Noch einfach ist es alles zu markieren (ctrl+a) und dann einfach mit dem schreiben der Methode anzufangen. Hat die Klasse noch keine Methoden ist beim Auswählen des Klassenamens ein Methodentemplate zusehen. Dieses muss dann nur noch ausgefüllt werden. === Wie erreiche ich Nebenläufigkeit in Squeak? === Die üblichen Mechanismen Semaphore und Mutex zur Synchronisation mehrerer Prozesse sind in Squeak als Klassen verfügbar. Übliches Vorgehen ist z.B. {{{ | semaphore | semaphore := Semaphore new. [ .... do some work ... semaphore signal ] fork. semaphore wait. }}} Für einfache Nebenläufigkeit ohne Synchronisation existiert `BlockClosure>>fork`. {{{ [50000 factorial] forkAt: ProcessScheduler userBackgroundPriority }}} === Was ist die richtige Art, Klassenvariablen anzulegen? Auf Instanzseite in `classVariableNames:` oder auf Klassenseite in `instanceVariableNames:`? === Dafür ist die instanzseitige Klassenvariable die richtige Wahl. Die klassenseitige Instanzvariable existiert nur auf Grund der Tatsache, dass Squeak ein Metaobjectprotocol besitzt und somit Klassen selbst wieder Instanz einer anderen Klasse sind. Daher besitzen sie auch Instanzvariablen. Allerdings sind die Instanzvariablen auch nur für die Klasse und nicht für Ihre Instanzen einsehbar. Um Klassenvariablen (sowohl Klasse als auch Instanzen der Klasse können in Methoden darauf zugreifen) zu erhalten, sind somit die instanzseitigen Klassenvariablen die richtige Wahl. Uns hatte man damals für den Singletonfall auch geraten, die Instanzseite zu nehmen, um die Singletoninstanz zu speichern. Erklärung nach http://www.faqs.org/faqs/smalltalk-faq/: Q6. What are class instance variables? Ans. Class instance variables are similar to class variables, except that they are created for eachsubclass of the defining class. When a class declares a class instance variable, a new variable is created for each subclass of that class. Each subclass then has its own instance of the variable and retains its own value for the variable, but each subclass has a variable with the same name. Only class methods of a class and its subclasses can refer to class instance variables; instance methods cannot. === Wie kann ich ein gespeichertes Image mit der VM öffnen? === ==== Mac OS X ==== Drag und Drop der Image-Datei auf das Squeak Icon z.B. im Dock. ==== Linux ==== Eintrag in der Datei squeak.sh, z.B.: {{{ IMAGE="$APP/Contents/Resources/Squeak4.4-12327.image" }}} oder die Variable `IMAGE`per command line übergeben, z.B. {{{ squeak.sh IMAGE=./Contents/Resources/Squeak4.4-12327.image }}} ==== Windows ==== Eintrag in Squeak.ini: {{{ ImageFile="Contents\Resources\Squeak4.4-12327.image" }}} Wenn man ein Image auf das Squeak.exe draggt oder als Kommandozeilen-Parameter angibt, wird dieses Image als Code interpretiert. Das passiert genau dann, wenn der Eintrag `ImageFile=` in der Squeak.ini gesetzt ist. Um ein Image per Drag-und-Drop auswählen zu können, musst man demnach den Eintrag entfernen – jedoch wird man dann bei jedem Start der Squeak.exe nach der Image-Datei gefragt. === Hilfe, ich habe hunderte Morphs die alle eine falsche Methode steppen. Was kann ich sie stoppen? === 1.) Wenn es sich um eine einzige Methode handelt, könntest du versuchen diese mit einem "User Interrupt" zu unterbrechen: strg + . (Windows) alt + . (Linux), cmd + . (OS X) 2.) Du kannst in Squeak jeder Klasse `#allInstances` senden und bekommst dann eine Liste, die alle Objekte der Klasse enthält. An diese Liste können dann mit dem [#collection-protocol Collection-Protokoll]. Nachrichten an alle Objekte geschickt werden. Somit kannst du die Objekte bearbeiten oder mittels `#stopStepping` zum Stillstand bringen. === Wie kann ich die Ausführung in meinem Image unterbrechen? === Indem ihr einen Interrupt an das Image sendet. Das geht mit: Strg + . (Windows), Alt + . (Linux), CMD + . (OS X). === Ich habe eine nebenläufige Anwendung programmiert, bei der mir ein Prozess abhanden gekommen ist und der jetzt weiter im Hintergrund läuft. Wie kann ich diesen beenden? === Du kannst im Process-Browser mit Rechtsklick->Explore auf den betreffenden Prozess den Object Explorer öffnen, und in diesem dann "self terminate." ausführen. === Wie bindet man das eigene Projekt in den Objects Explorer ein? === 1.) `ObjectsTool` erstellt die angezeigten Kategorien in dem es einfach alle Morphklassen nach einem bestimmten Selektor durchsucht und nur diese hinzufügt. Man benötigt also einen Morph und zwar den Morph, der das Spiel darstellt (z.B. `BlyPacmanApp`). 2.) Auf der Klassenseite des Morphs wird eine Kategorie "parts bin" benötigt und in dieser eine Methode `#descriptionForPartsBin`. Das ist der Hook für `ObjectsTool`. {{{ descriptionForPartsBin ^ self partName: 'Pacman' categories: #('SWA11') documentation: 'Pretty nice Pacman clone for SWA 09/10.' }}} Dabei kann mit einem vierten Parameter auch ein Beispielbild angegeben werden (siehe z.B. `AudioChatGUIdescriptionForPartsBin`, `self ... sampleImageForm:`). Ansonsten wird ein kleines Icon automatisch erstellt. 3.) Damit sollte der Morph in dem `ObjectsTool` erscheinen. Allerdings kann es sein, dass der Morph nicht richtig initialisiert wird (z.B. das Extent nicht stimmt). Wenn man ein Objekt aus dem `ObjectsTool` zieht, wird der jeweiligen Klasse `#newStandAlone` als Nachricht gesendet. Die Methode dazu sieht wie folgt aus. {{{ newStandAlone ^ self basicNew initializeToStandAlone" }}} Während Morph `#initializeToStandAlone` einfach auf `#initialize` zurückführt, überschreiben andere Morphs (z.B. `PasteUpMorph`) die Methode und setzen z.B. die Größe. Falls das der Fall ist, kann man die `#initializeToStandAlone` einfach überschreiben: {{{ initializeToStandAlone super initializeToStandAlone. self initialize. }}} p.s. Weitere Infos im Squeak-Wiki: [http://wiki.squeak.org/squeak/2998 1] [http://wiki.squeak.org/squeak/uploads/2102/objects_tool.2.html 2] === Welche Keyboard shortcuts gibt es in Squeak? Kann ich selbst welche hinzufügen? === Im Editor und den Unterklassen (vor allem TextEditor) in den initialize* Methoden findet man verschiedene Tabellen um Shortcuts anzupassen. Da diese Tabellen als Zustand auf Klassenseite gehalten werden muss dann die #initialize Methode der Klasse (also z.B. TextEditor initialize) noch einmal aufrufen werden. Allgemein natürlich der Hinweis auf Help > Keyboard Shortcuts für eine allgemeine Übersicht. === Wie stelle ich die letzten Codeänderungen wieder her, nachdem mein Image abgestürzt ist? === In der Liste der Screencasts findest du ein Video ([https://www.hpi.uni-potsdam.de/hirschfeld/trac/SqueakCommunityProjects/wiki/squeak_screencasts#HowtoRecoverChanges How to recover changes]) dazu. Grob machst du Folgendes: Öffne dein Squeak Image, World Menü, changes, recently logged changes... .Wähle in dem aufkommenden Menü dein bevorzugtes Datum aus (meistens das oberste). Danach kommt ein weiteres Tool in dem alle Codefragmente aber auch do its gespeichert sind. Wähle nun diese aus, welche für dich relevant sind (somit vermeidest du den selben Fehler noch einmal mit hinein zuladen). Zum Schluss nur noch auf 'file in'. === Wie finde ich heraus auf welcher Plattform mein Image gerade läuft? === Seht euch die Klasse !SmalltalkImage an. Per {{{ SmalltalkImage current platformName }}} erhaltet ihr informationen über das ausführende Betriebssystem und könnt ggf. eure Keymappings sowie andere plattformabhängige Informationen anpassen. === Wie werden plattformunabhängige Datei-Zugriffe realisiert? === #platform-path-separator Mit der Klasse `FileDirectory` kann im Dateisystem navigiert werden. Weil verschiedene Betriebssysteme verschiedene Pfad-Trennzeichen ("\" auf Windows, "/" bei Linux und OS X) verwenden, sollte die Methode `FileDirectory>>slash` oder `FileDirectory class>>slash` verwendet werden, um Dateipfade dynamisch zusammenzubauen. {{{ | aPath | aPath := aFolderName , FileDirectory slash , anImageName, '.jpg'. ImageMorph new image: (Form fromFileNamed: aPath); openInHand }}} Das Pfad-Problem tritt jedoch nicht bei `FileDirectory-Objekten auf, denn sie übersetzen einen "/" im Pfad bereits in den betriebssystemspezifischen Pfad `/Contents/Resources/folder/anImage.jpg` bzw. `\Contents\Resources\folder\anImage.jpg` auf Windows. {{{ FileDirectory uri: 'folder/anImage.jpg' }}} === Gibt es in Squeak so etwas wie eine !FileDialog Klasse? === Helfen kann euch hier die Klasse `FileList`. === Ich kann in Squeak nur in Fenstern tippen für die ich auch den Mausfokus habe. Wie kann ich das ändern? === Teil der Vorlesung ist es euch darin zu schulen als Programmierer einen Paradigmenwechsel zu durchlaufen. Dazu gehören nicht nur Umstellungen in der Formulierung von Programmcode, sondern auch wie ihr den Code aufschreibt. Die Angelegenheit mit dem Mausfokus ist also schon so gewollt und nach etwas Eingewöhnungszeit auch sehr praktisch, da man sich nämlich den Klick spart (was meist zu erhöhter Produktivität führt). Man muss natürlich aufpassen nicht ungewollt an die Maus zu kommen. Daher ist meine Empfehlung es so zu lassen, wie es ist bzw. prüft, dass euer Benutzerinterface später auch mit der default Einstellung funktioniert wie gewollt. Wenn ihr die Option trotzdem ändern wollt, kann man im World-Menu unter "Preferences" den Preference Browser öffnen. Dort einfach nach "mouse" suchen, und man stößt auf die zwei Optionen "mouseClickForKeyboardFocus" und "mouseOverForKeyboardFocus": Diese einfach umstellen. === Objekte als Text klassenseitig abspeichern === Zum Serialisieren von Objekten gibt es in Squeak die Methoden `#storeOn:` bzw `#readFrom:` Hier der Code dazu: {{{ stream := WriteStream on: (String new). objectToSerialize storeOn: stream. deserializedObject := Object readFrom: stream }}} Überlegt euch aber gut, warum ihr das Serialisieren überhaupt benötigt. Für den Umfang dieser Vorlesung reicht das Speichern des Zustandes im Image meist völlig aus. === Kann man aus dem Debugfenster den Callstack rauskopieren? === Wenn der Debugger geöffnet ist, kann man z.B. im Menü (Rechtsklick o.ä. auf die Titelleiste) "copy text" wählen. Dann ist der dargestellte Callstack in die Zwischenablage kopiert und kann wie gewohnt in allen Anwendungen eingefügt werden. === Frame-Rate messen === Es gibt zum Anzeigen der aktuellen Frame-Rate ein Tool im !PartsBin. Es ist erreichbar über: World Menu (Links-Klick) -> Objects -> Useful -> Herausziehen von "Frame Rate" [[Image(world-menu-objects.jpg)]] [[Image(frame-rate-tool.jpg)]] === Laufzeit einer spezifischen Methode/eines Code-Schnippsels messen === Um die Laufzeit einer hinreichend langsamen Methode und all ihrer Aufrufe zu messen kann, ist der Aufruf`MessageTally>>spyOn: aBlock` geeignet. Außerdem zeigt der Report auch die Auswirkungen auf die automatische Speicherverwaltung. Aber Achtung, damit die Messungen statistisch signifikant werden, muss man sie hinreichend oft wiederholen, z.B: {{{ MessageTally spyOn: [10000 timesRepeat: [1.23 printString]] }}} Beispiel mit tiefem call tree: {{{ MessageTally spyOn: [10000 timesRepeat: [50 factorial]] }}} Erläuterungen zur Ausgabe im Blogpost [http://onsmalltalk.com/profiling-smalltalk]. === Laufzeit aller gerade laufenden Methoden in allen Prozessen messen (Profiling des Images) === In der Menü-Zeile Extras -> Start Profiler wählen. Wenn der Maus-Cursor ganz oben in die Welt bewegt wird, stoppt der Profiler und zeigt den Report an. Erläuterungen zur Ausgabe im Blogpost [http://onsmalltalk.com/profiling-smalltalk]. [[Image(profiler.jpg)]] === Gibt es ein Tool in Squeak, das einem hilft besseren Code zu schreiben? === Wie für viele Programmiersprachen gibt auch in Squeak einen sogenannten Linter, der dabei unterstützen soll schönen Code zu schreiben. Dieser analysiert euren Code und weist auf "unsaubere" Abschnitte hin. Solche Stellen können sowohl mögliche Fehlerquellen sein, als auch Codeabschnitte, die nicht Idiomen entsprechen. SWALint, wie das Tool heißt, wurde über Jahre in SWT Vorlesungen entwickelt und refactored. Ihr findet es im Apps Menü. Eine genauere Erklärung bietet der folgende Screencast (wenn auch für eine etwas ältere Version): https://www.hpi.uni-potsdam.de/hirschfeld/trac/SqueakCommunityProjects/wiki/squeak_screencasts#SwaLintstaticcodeanalysisandmetrics). (Die SwaLint-Tests sind sehr umfangreich und nicht alle immer ein hundertprozentiger Indikator für ein Problem. Ihr könnt sie gerne auch ausführen, um noch Hinweise auf potentielle Probleme zu finden, alle diese Tests zu bestehen sollte aber kein Ziel für euch sein.) === Ich habe meine WorldMainDockingBar kaputt gemacht. Wie kann ich sie zurücksetzen? === Es gibt zwei Wege die MainDockingBar zurückzusetzen: 1. Du kannst das Morph-Menü der Welt mit der rechten Maustaste auf die Welt öffnen und dort den Punkt "show main docking bar" einmal abwählen und wieder auswählen. 2. Du kannst über das World Menü unter "appearance->preferences" den Preference Browser öffnen. Dort kannst du die EInstellung "show world main docking bar" finden die du wieder einmal ab- und wieder auswählen musst. == Seaside == === In Seaside: "clean" URLs (ohne _s und _k) === Schaut euch einmal den Teil des Tutorials an: [http://www.hpi.uni-potsdam.de/hirschfeld/seaside/tutorial?chapter=3D12 Model Overview] Unter dem Punkt static Urls solltet ihr fündig werden. === Wie ist die exakte Funktionsweise der WAFileLibrary Klasse? === Schau dir dafür einmal die Klasse: WAFileLibraryDemo an. Ebenso findest du im Seaside Image unter den Demos den WAUploadFunctionalTest http://localhost:8080/tests/alltests/ Bzw. in unserem Tutorial gibt es auch einen entsprechenden Abschnitt: [http://www.hpi.uni-potsdam.de/hirschfeld/seaside/tutorial?chapter=3D7 Model] Die WAFileLibrary ist nicht dafür gedacht als eine langfristige Lösung zu fungieren. Sie ist eine einfache Art Dateien bereitzustellen. Aber sowohl durch ihre Ablage der Daten im Image als auch der vergleichsweise langsamen Zugriffszeit auf diese Daten sollte sie immer nur temporär benutzt werden. Sie hat aber auch Vorteile, wie z.B. den leichten Austauschen von Dateien über Monticello. Bessere Lösung um auf statische Dateien zuzugreifen findet ihr ebenso im Tutorial (selbes Kapitel weiter unten). Bei deinem Beispiel würde an der Klasse `AClassName` die Methode `#fooBar` aufgerufen werden. Hier dispatcht Seaside ein wenig mit Meta-Programming. Aber dieses Schema ist konstant und kann über den File Upload Dialog soweit umgangen werden, dass ihr die Klasse gar nicht anfassen braucht. Zum Hochladen von Dateien habe ich bereits in einer vergangenen Mail auf der Liste etwas geschrieben, da hier das Tutorial nicht mehr aktuell ist. Durch die begrenzten Möglichkeiten der WAFileLibrary sind Unterordner so weit ich weiß nicht möglich aber ggf. wurde das in der neuen Seaside Version auch geändert. === Wie bringe ich eine Seaside Applikation in einen Deployment modus? Wie schalte ich insbesondere die Toolbar aus? === Programmatisch kann man es wie folgt lösen: {{{ | app deployedApplicationDefaults | app :=3D self setupWCR. deployedApplicationDefaults :=3D WAAdmin applicationDefaults copy removeParent: WADevelopmentConfiguration instance; yourself. app configuration removeParent: WAAdmin applicationDefaults; addParent: deployedApplicationDefaults. }}} Die Methode self setupWCR legt dabei unseren Einstiegspunkt sowie seineKonfiguration fest. Das kannst du z.B. so machen: {{{ | app | app :=3D DeineRootComponentKlasse registerAsApplication: 'test' app preferenceAt: #sessionClass put: DeineSessionClass; "... weitere konfiguration". ^ app }}} ich würde es ganz einfach wie folgt machen. Nimm dein aktuelles Image und -save as... mit einem Namen wie "100710_DigitalSimulatorWeb_Deployment.image". In diesem Image entfernst du einfach die entsprechenden Packages. === Wie kann man mit Seaside E-Mails versenden? === Seaside sollte in eurer Installation Seaside-email mit installiert haben. Damit könnt ihr eurer Applikation im Seaside-Config-Dialog eine Konfiguration (WAEmailConfiguration) hinzufügen, wo ihr Server-parameter einstellen könnt. Ab dann könnt ihr in euer Applikation Instanzen von WAEmailMessage verwenden: {{{ | addr addr2 msg | addr := WAEmailAddress address: 'peter.lustig@example.com' username: 'Peter Lustig'. addr2 := WAEmailAddress address: 'biene.maja@example.com' username: 'Biene Maja'. msg := WAEmailMessage from: addr to: addr2 subject: 'Honey out'. msg body: 'Hello Maja, }}} == !GemStone == === Welche Streams verwendet man zum Dateien schreiben auf !GemStone? === Dateibehandlung findet in !GemStone mit Hilfe der Klasse !GsFile statt. In !GemStone gibt es immer die Unterscheidung, ob etwas auf dem Client (also, !GemTools oder seaside-gem) oder Server (also stone) passiert. Darauf beziehen sich dann die entsprechenden *onClient* und *onServer*- methoden auf Klassenseite von !GsFile. == Lehrveranstaltung == === Wie sind meine Zugangsdaten zu Squeaksource? === Benutzername ist der HPI Benutzername und das Password wurde dir per Mail geschickt. Bitte ändere dein initiales Passwort. === Wo finden die Konsultationen statt? === In der Kommunikationszone C.E. oder online per Zoom. === Welchen Umfang sollten die SWA-Projekte haben? === Die Anforderungen, welche durch die Spiele umgesetzt werden sollen, reflektieren auch den Umfang des Projektes. Alle Projekte wurden so gewählt (oder von uns freigegeben), sodass sie einerseits mit 4 Studierende in der vorhandenen Zeit sehr gut realisierbar sind und andererseits auch ausreichend Raum lassen, um eine sehr gute Architektur zu entwickeln. Eine Zahl entsprechend der Menge an Quelltext lässt sich nur schwer bestimmen, da es von zu vielen Faktoren abhängt (Verwendung von Mustern, Kenntnis der Smalltalk Bibliothek, Benutzte Abstraktionen etc.). Falls es euch hilft: Wir hatten schon sehr gute Projekte, welche nur knapp 1.000 Zeilen geschrieben haben aber auch sehr gute mit mehr als 4.000 Zeilen. Versucht euch am Besten dazwischen einzupendeln und setzt euren Fokus lieber auf Qualität statt Quantität. Uns geht es in dieser Lehrveranstaltung vor allem darum, dass ihr lernt und versteht, was es heißt "schönen" Code zuschreiben. == Monticello == === Wie funktioniert der übliche Workflow mit Monticello? (Dank an Marcel Wendler für die ausführliche Antwort) === ==== Committing ==== 1. Ihr klickt in Squeak auf "Tools" 2. Dann auf "Monticello Browser" 3. Da dann auf "+Package" 4. Gebt den Namen des neuen Packages ein. Der Name muss einer eurer Kategorien im System Browser entsprechen. Diese Kategorie wird dann automatisch in Monticello eingebunden. Genau genommen gehören alle Kategorien, deren erstes Wort dem Packagenamen entspricht zum Package. D.h. zum Package "!MyProject" gehören bspw. folgende Kategorien: !MyProject, !MyProject-Tests, !MyProject-UI, !MyProject-!CoolStuff usw. Man beachte jeweils das Minus! Das heißt ihr müsst nicht mehrere Packages anlegen und getrennt committen, wenn ihr eure Klassen auf mehrere Kategorien verteilen wollt. Beachtet, dass das bedeutet, dass wenn ihr ein Paket "!MyProject" committed und ein anderes "!MyProject-Tests", dann sind *immer* auch alle Tests in "!MyProject" dabei. Nur ohne das "-" würde es wirklich aufgeteilt, wie ihr bei "Collections" und "!CollectionsTests" beobachten könnt. 5. Wählt euer Package aus. Rechts sind alle Repositories aufgelistet, zu denen das Package gehört. Anfangs sollte das leer sein bzw. nur einen Link zu irgendeinem package-cache enthalten (Der package-cache ist ein Ordner auf eurer Platte, in dem Squeak heruntergeladene Monticello-Versionen (*.mcz) zwischenspeichert, damit er eine Version nicht jedes mal wieder herunterladen muss, wenn man darauf klickt.) 6. Jetzt muss da noch euer eigenes Repository hin. Jede Gruppe hat ja eines unter "http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource" bekommen. Dazu klickt ihr (während euer Package ausgewählt ist) im Monticello Browser auf "+repository". 7. Da auf "HTTP" 8. Den Text dort durch folgendes ersetzen (So auch auf eurer jeweiligen Seite bei Squeaksource zu finden): {{{ MCHttpRepository location: 'http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/swa15-team006' user: '.' password: '' }}} Die <> entsprechend anpassen. Achtung: Passwort wird klar angezeigt. Falls um euch rum fiese Spione sitzen also im Nachhinein alle Zeugen beseitigen. Oder besser: Kein "echtes" Passwort von euch verwenden oder das ganze einfach zu Hause erledigen. Es geht hier um das Passwort, dass ihr für hpi.uni-potsdam.de/hirschfeld/squeaksource verwendet. (Ein Initialpasswort haben wir alle per Mail bekommen. Das solltet ihr nach dem ersten Login auf der Seite links über "Edit Account" ändern) 9. Jetzt habt ihr also ein Package und ein Repository, wo das Package rein soll. Fehlt also nur noch die Magie, die beides zusammenbringt. Dafür wählt ihr jetzt rechts euer Repository aus und klickt oben auf "save". 10. Falls ihr schon was in dem Package habt, sollte Squeak jetzt "snapshotten" und "diffen". Dann öffnet sich das commit-Fenster. Wenn ihr brave committer seid, dann packt ihr da also über "=== text below is ignored ===" ein paar Anmerkungen zu dieser Version rein. Achtung: Alles unter "=== text below is ignored ===" wird tatsächlich beim commit ignoriert. Das sind also nur Hinweise für euch, die euch Squeak gibt, damit ihr wisst was hochgeladen wird, was geändert wurde und was gelöscht wird. Erklärung dazu: 1. A XYZ := XYZ wurde neu hinzugefügt (Added) 2. M XYZ := XYZ wurde verändert (Modified) [Wird auch angezeigt, wenn ihr etwas editiert habt, gespeichert, Änderung rückgängig, wieder gespeichert. Zeigt also nur an ob eine Datei zwischendurch neu gespeichert wurde und nicht unbedingt ob wirklich etwas geändert wurde.] 3. D XYZ := XYZ wurde gelöscht (Deleted) Dann auf "Accept" klicken und schon sollten euren Dateien auf dem Server liegen und bereit sein von anderen runtergeladen zu werden. ==== Loading und Merging ==== 1. Eine Versionsverwaltung, in der man nur comitten kann, wäre nicht wirklich praktikabel. Daher noch kurz eine Erklärung zum mergen etc. Dafür einfach die entsprechende Repository im Monticello Browser auswählen und auf "open" klicken. 2. Jetzt werden rechts alle Versionen eures Images angezeigt, die bisher committet wurden. Davon die gewünschte anklicken. 3. Oben stehen euch nun die gewöhnlichen Tools zur Verfügung. Hier sind also vor allem Load (bei Konflikten wird automatisch der neuere Code übernommen, also alter Code überschrieben) und Merge (bei Konflikten wird ein Merge-Fenster geöffnet wo der Benutzer entscheidet, wie zusammengefügt werden soll) interessant. 4. Nach dem Load oder Merge sollte euer Image auf dem neuesten bzw. gewünschten Stand sein. == Patterns und Idiome == === State und Strategy Pattern – Was ist der Unterschied? === Die Strukturen der beiden Muster sind schon sehr ähnlich aber was sie wesentlich unterscheidet ist ihre Intention. Beim State Pattern geht es darum einen Zustand abzubilden und mit diesem verschiedenes Verhalten zu erzeugen. Die Verhaltensvariation unterliegt also der Zustandsklasse und der Client setzt hauptsächlich seinen Zustand und nutzt diesen. Beim Strategy Muster entscheidet der Client aber bewusst ein anderes Verhalten zu benutzen und kennt daher dessen Umsetzung, Vor- und Nachteile. Im GoF Buch gibt es für State das Beispiel der TCP Connection, welche je nach Zustand zwar die selben Nachrichten versteht aber unterschiedlich reagiert. Beim Strategy Muster wurde ein Compositor zur Ausgabe von Text gewählt, hier entscheidet der Client ganz bewusst die Ausgabe in TeX oder normalen Text zu rendern . über die Häufigkeit des Austauschens würde ich jedoch keine Aussage treffen wollen, da dies sehr vom Anwendungsfall abhängt. Der Zustand der Strategy Algorithmen sollte keinen direkten Einfluss auf den Zustand des Clients haben. Beim State kann das jedoch der Fall sein, z.B. ist die TCPConnection abgebrochen und der Client zeigt eine Fehlermeldung durch diesen neuen Zustand. === Wie viel Kommentare sind nötig/erwünscht? === Eine einfache Regel besagt: "So viel wie nötig, so wenig wie möglich". Schaut euch einfach noch einmal die Idiome zum Kommentieren sowohl von Kent Beck als auch Dave Thomas an. Diese findet ihr in den [[https://www.hpi.uni-potsdam.de/intern/studium/materialien Vorlesungsfolien und der begleitenden Literatur zur Lehrveranstaltung]. === Setzt man in Smalltalk nach dem return einen Punkt oder nicht? === Der Punk separiert Ausdrücke und ist deswegen nach dem letzten Ausdruck nicht mehr notwendig. Im Allgemeinen ist es nur wichtig, dass ihr es konsequent überall gleich macht. Wir bevorzugen zwar die Variante ohne Punkt aber dies sollte auf eure Wahl keine Auswirkung haben. == Ohne Kategorie == === Hat Squeak einen Eventhandler für Netzwerkevents? Wie kann man eine Methode aufrufen, sobald Daten angekommen sind? === Das kommt sehr auf eure Netzwerkimplementierung an. Nutz ihr ein Standardprotokoll, so sollte die verwendete Bibliothek im Allgemeinen auch entsprechende Methoden bereitstellen. Häufig sind diese transparent, so dass die Methoden automatisch gerufen werden, sobald ein neuer Request eintrifft. Um Beispiele dafür zu sehen, schaut euch einmal im Image den WAListener an, welcher den Webserver für Seaside implementiert. Dort wird auch gezeigt wie man Prozesse "forked" um mehrere Requests gleichzeitig zu behandeln. Von dem Ansatz die ganze Sache in Morphic und dessen `#step` Methode abzubilden, raten wir schlichtweg ab. Der Grund: die `#step` Methode läuft nicht parallel. Sollte eine Implementierung von `#step` auf `SocketStream>>isDataAvailable` warten, steht das ganze System. Ihr müsste also auf jeden Fall mit mehreren Prozessen arbeiten. Dafür verwendet ihr wie gesagt die `fork`-methode. Um das Warten zu realisieren, schaut euch die Klasse Delay an. Es gibt aber noch weiter Synchronisationsprimitiven, insbesondere wenn ihr Sockets benutzt: Je nachdem ob ihr Socket oder !SocketStream verwendet, gibt es entsprechende Methoden die sich schlafen legen bis Daten verfügbar sind, z.B. `Socket>>waitForData` (siehe Kommentar). Die Netzwerkfähigkeit wäre zwar sicher ein schönes Feature ist aber ohne entsprechende Vorkenntnisse in dem Bereich nicht ganz einfach. Überlegt es euch daher vielleicht, ob ihr dies wirklich implementieren möchtet. === Ist Late Binding nur ein Mittel, um Polymorphie zu erreichen? === Polymorhpie kann auch ohne späte Bindung erreicht werden. Zum Beispiel in C++ stehen polymorphe Methoden schon zur Übersetzungszeit fest, wenn sie nicht mit dem Schlüsselwort "virtual" markiert sind [http://en.wikipedia.org/wiki/Late_binding#Late_binding_in_C.2B.2B Dynamic Dispatch in C++]. Late binding ermöglicht polymorphe Methoden auf Objekten, deren Klasse erst zur Laufzeit bestimmt werden kann. Vererbung und Polymorphie sind nicht notwendig, um objektorientierte Abstraktion zu ermöglichen; Sie werden jedoch aus praktischen Gründen in OO-Sprachen sehr gerne eingesetzt. Die Darstellung von OOP zieht oft fälschlicherweise diese beiden Konzepte als zentral und unabdingbar heran. Sprachen wie z. B. !JavaScript und Self nutzen Delegation an Stelle von Vererbung. Sie sind objektorientiert, kommen aber auch ohne Klassen aus. === Ist in Squeak eine Implementierung der MD5-Verschlüsselung vorhanden? === Das Cryptography package sollte alle notwendigen Funktionen liefern. Dort habt ihr eine MD5 Klasse. Folgt dem Beispiel in der Methode `#hashStream:`. Aber Achtung, [http://de.wikipedia.org/wiki/MD5 MD5] ist sehr unsicher, da sehr leicht Kollisionen gefunden werden können. Seaside nutzt deswegen üblicherweise den SHA Algorithmus. Diesen könnt ihr bereits in eurem Image finden unter der Klasse `SecureHashAlgorithm`. Dort schaut euch einfach die Methoden unter dem 'public' Protokoll an. === Wie konkateniert man Strings? === Das geht mit dem Komma-Operator. Die Konkatenation von Tabs an Strings geht beispielsweise so: {{{ 'abc' , String tab , 'abc' }}} oder auch {{{ 'abc' , Character tab , 'abc' }}} da `String>>,` die Nachricht `#asString` an seinen Parameter sendet. Unterschiedlich sieht das nur aus wenn man es per print it (cmd/strg + p) ausgibt, da tab halt je nach Position in unterschiedlicher Länge gerendert wird. Die Strings sind aber gleich (im Sinne von `#=`). Bedenkt bitte, dass ihr bei sehr komplexen String Konkatenationen besser auf Streams zurückgreifen solltet: {{{ String streamContents: [:stream | stream nextPutAll: 'abc'; tab; nextPutAll: 'abc']. }}} === Was ist der Unterschied zwischen {} und #() bei Arrays und welches sollten wir verwenden? === Die Variante mit #() beschreibt ein Array-Literal, d. h. was innerhalb der Klammern steht, muss beim Kompilieren des Smalltalk-Codes bereits feststehen. Man kann damit also Arrays anlegen, die weitere Literale enthalten, z. B. Characters, Strings, Integers oder Floats, sowie die Konstanten `true`, `false` und `nil`. Andere Wörter werden als Symbole interpretiert. `#(eins zwei drei)` erzeugt also ein Array, welches die Symbole `#eins`, `#zwei` und `#drei` enthält. Man kann mit Array-Literalen jedoch nicht das Ergebnis einer Nachricht in das Array aufnehmen, da dieses zur Kompilierzeit noch nicht feststeht! Im Unterschied zu den Array-Literalen werden die zwischen {} aufgezählten Ausdrücke ausgewertet, wenn der Code ausgeführt wird, und deren Ergebnis im Array gespeichert. Weiterhin müssen die Elemente des Arrays durch einen Punkt getrennt werden (der Punkt am Ende kann genau wie am Ende einer Methode entfallen), was bei Array-Literalen nicht vorgesehen ist. Das Array `{1. #zwei. 'dr', 'ei'. -4 negated}` würde also folgende vier Elemente enthalten: den Integer 1, das Symbol `#zwei`, den String `'drei'` und den Integer 4. Ein weiteres Beispiel: `{self class}` ergibt ein Array, welches die Klasse von self enthält, während `#(self class)` ein Array ergibt, welches die Symbole `#self` und `#class` enthält. Die geschweiften Klammern sind nicht offizieller Teil von Smalltalk, sondern wurden mit Squeak als Erweiterung der Sprache eingeführt. Das ist jedoch kein Grund, sie nicht in den Projekten zu verwenden. Müsste man Smalltalk Code schreiben, der zu VMs anderer Hersteller kompatibel sein soll, müsste man jedoch darauf achten. Die Alternativen in Standard-Smalltalk wären z. B. die Array-Konstruktoren `Array with: firstElement`, `Array with: firstElement with: secondElement`, usw., `Array withAll: aCollection`, sowie das Anlegen eines Arrays mit `Array new: sizeNumber` und anschließendes manuelles Befüllen mit `at:put:`. Dadurch, dass Array-Literale schon zur Kompilierzeit feststehen, ist das Erzeugen von Arrays damit billiger als mit {}. Falls die Werte im Array jedoch nicht konstant sind, sondern sich erst zur Laufzeit ergeben, oder wenn ihr das Array später verändern wollt (dazu gehören auch Nachrichten wie z. B. `sort:`, die eine Collection in-place verändern), nutzt bitte die Schreibweise mit geschweiften Klammern. Andernfalls würdet ihr das in die Methode einkompilierte Array verändern, was zu Problemen führen kann... === Wie bekomme ich plattformunabhängige Tastencodes bei !KeyUp und !KeyDown? === Die mit keyValue bzw. keyCharacter abrufbaren Tastencodes, die dem `KeyboardEvent`, welches mit `keyUp:` und `keyDown:` übergeben wird, wenn man diese Events durch seine Morph-Klasse behandeln lässt, sind von Plattform zu Plattform bzw. von VM zu VM verschieden. Beim `keyStroke:`-Event sind die Codes plattformunabhängig, allerdings bietet einem dieses Event unter Umständen nicht das gewünschte Verhalten. Gruppen, die sowohl mit Windows als auch Mac OS X oder Linux arbeiten und ein zum Beispiel Spiel entwickeln, bei dem man Tasten gedrückt halten soll, könnte das Probleme bereiten. Leider muss man sich darum selbst kümmern, indem man die benötigten Tastencodes für jede Plattform herausfindet und dann anhand der Plattform, auf der die VM gerade läuft, entscheidet, welche Codes berücksichtigt werden sollen. Die Plattform kann man mit `Smalltalk os platformName` ermitteln (die Rückgabewerte sind z. B. `'mac os'`, `'Win32'` und `'unix'`). Eine Möglichkeit wäre, die Tastencodes auf der Klassenseite abzulegen und beim Zugriff darauf anhand der Plattform zu entscheiden, welcher der Codes zurückgegeben werden soll. Hier gibt es mehrere Varianten jeweils mit Vor- und Nachteilen (Plattform-Unterscheidung bei jedem Zugriff? Objekt mit aktuellen Codes anlegen? Wie sicherstellen, dass das richtige verwendet wird, wenn man das Image von einer Plattform zur anderen kopiert?). Überlegt, was sich für euer Projekt am besten eignet. === Iterieren über Collections mit dem Collection-Protokoll === #collection-protocol Wichtig ist, auch für die Klausur, dass ihr nicht ausschließlich `#do:` verwendet, um zu iterieren. Nutzt unbedingt die Möglichkeiten des Collection-Protokolls. {{{ x := #(1 2 3 4 5). b := x allSatisfy: [:a | (a >= 1) & (a <= 4)]. "test if all elements meet condition" b := x anySatisfy: [:a | a > 42]. "test if any element meets condition" y := x select: [:a | a > 2]. "return collection of elements that pass test" y := x reject: [:a | a < 2]. "return collection of elements that fail test" y := x collect: [:a | a + a]. "transform each element for new collection" y := x detect: [:a | a > 3] ifNone: []. "find position of first element that passes test" sum := x inject: 10 into: [:a :c | a + c]. "sum of elements + 10" sum := x fold: [:a :b | a + b ] "sum elements" }}} Beispiele mehrheitlich aus [http://wiki.squeak.org/squeak/5699 Terse Guide to Squeak].