Hey Leute,
lange nichts mehr von mir gegeben. Habe die letzten Monate so vor mich her programmiert. Zwischenzeitlich hatten wir hier auch einen Praktikanten, der mir bei meinem Java-Projekt weitergeholfen hat. So konnte ich meine etwas überladene Swing-GUI beiseite legen und mit dem Spring Framework eine Seite gestalten (ist bei Applikationen die auf dem Server laufen eh zu empfehlen 🙂 ).
Jetzt nachdem der Praktikant nicht mehr da ist gäbe es zwar noch ein paar Sachen die angepasst werden müssten, aber ich komme klar 😉
Die Tage gab es dann mal wieder eine rege Diskussion zu Matrix (dem Film), was denn möglich ist und was überhaupt mit Matrix gemeint ist (es ist wirklich nur ein Film Leute 😉 ). Allerdings finde ich die Idee mit dem, wie soll man ich sonst nennen, „Matrix-Regen“ gar nicht mal schlecht. Richtig angeordnet und richtig gelesen, könnte er viele Informationen enthalten. Diese Informationen lassen sich dann auch schneller wiedergeben als nur simpler Text.
Ein Programm habe ich ja, fehlt also nur der Regen. Als Basis habe ich mal den Code von Kenai zu Grunde genommen. Hier wird allerdings nur 40 mal der addDrop() ausgeführt und dann so lange selbst aufgerufen bis man das Programm beendet.
Um mit dem Programm zu arbeiten also erst einmal eine Interface-Klasse gebastelt, bei mir sieht diese in etwa so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
import java.awt.*; import java.util.concurrent.CopyOnWriteArrayList; /** * Interface between Monitor and other javaClasses * * @author lkurth on 23.09.2015 * @since 1.2 */ public class MonitorInterface { private static MonitorInterface instance; private CopyOnWriteArrayList<Color[]> dropList = new CopyOnWriteArrayList<>(); /** * Adding a drop to the monitoring service * * @param type the type of the message:<ul> * <li>e - for errors</li> * <li>w - for warnings</li> * <li>i - for information</li> * <li>d - for debug</li> * <li>n - for normal / default</li></ul> */ public void addDrop(char type) { Color color1, color2; switch (type) { case 'e': // errors //color1 = new Color(255, 175, 175); // pink color1 = new Color(255, 0, 0); // brighter red color2 = new Color(144, 0, 0); // darker red break; case 'w': // warnings color1 = new Color(255, 200, 0); // yellow / orange color2 = new Color(255, 80, 0); // orange break; case 'i': // information color1 = new Color(0, 255, 255); // cyan color2 = new Color(0, 0, 255); // blue break; case 'd': // debug color1 = new Color(128, 128, 128); // grey color2 = new Color(78, 78, 78); // darker grey break; case 'n': // normal and default default: color1 = new Color(255, 255, 255); // white color2 = new Color(0, 255, 0); // green } // adding color to the list Color[] ca = {color1, color2}; dropList.add(ca); } /** * Gets the complete {@link #dropList DropList} * * @return the complete {@link #dropList DropList} as {@link java.util.concurrent.CopyOnWriteArrayList CopyOnWriteArrayList}. * Though {@link java.util.ArrayList ArrayList} may produce {@link java.util.ConcurrentModificationException ConcurrentModificationException} while accessing it. * * @see #emptyList * @see java.util.concurrent.CopyOnWriteArrayList * @see java.util.ArrayList */ CopyOnWriteArrayList<Color[]> getList() { return dropList; } /** * Just emptying the {@link #dropList DropList}. */ void emptyList() { dropList.clear(); } /** * @return an instance of the interface */ public static MonitorInterface getInstance() { if (instance == null) instance = new MonitorInterface(); return instance; } } |
Hier wird dann für jeden übergebenen Drop eine Farb-Kombination in einer ArrayList gespeichert und bei Bedarf vom eigentlichen „Monitor-Thread“ ausgelesen und verarbeitet.
Den Monitor (Matrix.java) habe ich dann wie folgt geändert:
- Zwei Variablen für die Farbe deklariert und bei der Interpolation hinterlegt
- Die while Schleife im Matrix-Panel auskommentiert (möglicherweise brauche ich es doch noch) und einen Thread hinzugefügt (zum Aufrufen und Auswerten vom Interface).
- Eine zweite addDrop() Funktion erstellt zum ändern der Farben
Abgeändert sieht er in meinem Fall also so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
import org.pushingpixels.trident.Timeline; import org.pushingpixels.trident.TimelineScenario; import org.pushingpixels.trident.swing.SwingRepaintTimeline; import javax.swing.*; import java.awt.*; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.InputStream; import java.util.*; import java.util.List; import java.util.Timer; import java.util.concurrent.CopyOnWriteArrayList; /** * This class is created in memory of Matrix.java * It will be used to display current processes. * * @author lkurth on 23.09.2015 * @since 1.2 */ public class Monitor { private static Font font; private static Color fc1 = new Color(255, 255, 255); // from this color private static Color fc2 = new Color(0, 255, 0); // into this public static class Letter { private final int x; private final int y; private Color color; private float opacity; private final char c; public Letter(int x, int y, char c) { this.x = x; this.y = y; this.c = c; this.color = Color.white; this.opacity = 0.0f; } @SuppressWarnings("unused") public void setOpacity(float opacity) { this.opacity = opacity; } @SuppressWarnings("unused") public void setColor(Color color) { this.color = color; } public void paint(Graphics g) { if (this.opacity == 0.0f) return; Graphics2D g2d = (Graphics2D) g.create(); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setFont(font); g2d.setColor(this.color); g2d.setComposite(AlphaComposite.SrcOver.derive(this.opacity)); g2d.drawString("" + this.c, this.x, this.y); g2d.dispose(); } } public static class Drop { private final java.util.List<Letter> letters; public Drop() { this.letters = new ArrayList<>(); } public TimelineScenario getScenario(int x) { TimelineScenario result = new TimelineScenario.Parallel(); Random randomizer = new Random(); // how many letters will there be? int totalLetterCount = 5 + randomizer.nextInt(20); // length in general (line) //int totalLetterCount = 30; int initialDelay = randomizer.nextInt(1000); int duration = 1000 + randomizer.nextInt(100); // tail for (int i = 0; i < totalLetterCount; i++) { int y = font.getSize() * i; // choose random katakana letter // int letterIndex = (int) (0x30A0 + Math.random() // * (0x30FF - 0x30A0)); int start = 33; int delta = 95; char c = 0; while (c == 0 || c == ' ' || c == '\u007F') // no spaces in line c = (char) (start + Math.random() * delta); // System.out.print(c + ","); // see whats coming ;-) Letter l = new Letter(x, y, c); this.letters.add(l); Timeline t = new Timeline(l); t.addPropertyToInterpolate("opacity", 1.0f, 0.0f); t.addPropertyToInterpolate("color", fc1, fc2); // white, green t.setDuration(duration); t.setInitialDelay(initialDelay + i * 120); result.addScenarioActor(t); } return result; } // paint the letter public void paint(Graphics g) { for (Letter l : this.letters) l.paint(g); } } /** * This is the main class of the Monitor.java. * The thread reading the drops is placed inside of the {@link java.awt.event.ComponentListener ComponentListener} */ public static class MatrixPanel extends JPanel { private final List<Drop> drops; public MatrixPanel() { try { InputStream is = MxRain.class.getClassLoader() .getResourceAsStream("fonts/katakana.ttf"); Font kf = Font.createFont(Font.TRUETYPE_FONT, is); int fontSize = 14; // Font kf = new Font("monospaced", Font.PLAIN, fontSize); // if katakana.ttf can't be used font = kf.deriveFont(Font.BOLD, fontSize); } catch (Exception exc) { exc.printStackTrace(); } Timeline repaint = new SwingRepaintTimeline(this); repaint.playLoop(Timeline.RepeatBehavior.LOOP); this.drops = new ArrayList<>(); this.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { /* this will add a specific number (40) of repeating drops * / while (drops.size() < 40) addDrop(); /**/ // start a task to check for monitor colors (arrayList) MonitorInterface mii = MonitorInterface.getInstance(); TimerTask monitorTask = new TimerTask() { @Override public void run() { if (mii.getList().size() > 0) { CopyOnWriteArrayList<Color[]> colors = mii.getList(); for (Color[] cl : colors) addDrop(cl[0], cl[1]); mii.emptyList(); } } }; Timer timer = new Timer("Monitor Timer"); timer.scheduleAtFixedRate(monitorTask, 30, 500); // check every x seconds } }); } /** * this synchronized method was used to add its own drop in a loop (matrix like). * now it just adds it once and removes when the callback method is called. */ private synchronized void addDrop() { final Drop drop = new Drop(); TimelineScenario scenario = drop.getScenario(new Random() .nextInt(getWidth())); scenario.addCallback(() -> { synchronized (MatrixPanel.this) { drops.remove(drop); //addDrop(); // not adding again (loop) } }); this.drops.add(drop); scenario.play(); } /** * sets the color * * @param color1 the first color * @param color2 the color to fade into * * @see #addDrop() */ protected void addDrop(Color color1, Color color2) { if (color1 != null && color2 != null) { fc1 = color1; fc2 = color2; } addDrop(); } @Override protected void paintComponent(Graphics g) { g.setColor(Color.black); g.fillRect(0, 0, getWidth(), getHeight()); synchronized (this) { for (Drop drop : this.drops) drop.paint(g); } } } } |
Entgegen der Meldung der IDE (zumindest bei IDEA) werden die beiden Funktionen der Klasse Letter doch verwendet. Wie sie verwendet werden kann die IDE nur nicht wissen/ahnen.
Nun kann über das Interface jedes Mal ein drop hinzugefügt werden. Beispielsweise an allen Stellen, an denen ein Fehler auftritt (try, catch, exceptions). Dazu einfach nur folgenden Code an die beliebige Stelle einfügen:
1 |
monitor.addDrop('e'); |
Bemerkung: Wenn man das Spring Framework verwendet (oder irgendein anderes Framework, welches die Nutzung einer GUI eigentlich überflüssig macht, kann dies mit folgenden Zeilen umgehen:
1 2 3 4 5 6 7 |
// to show a gui we have to be headless ;P System.setProperty("java.awt.headless", "false"); // create Matrix process monitor if (!java.awt.GraphicsEnvironment.isHeadless()) SwingUtilities.invokeLater(() -> { // code here }); |
Ob Nick damit etwas zu tun hat? ;P
Also ich mag den Prozess Monitor. Vielleicht kann ich ihn ja noch etwas ausbauen. Bis dahin…
Viel Spaß