I'm a fairly experienced programmer, but new to GUI programming. I'm trying to port a plotting library I wrote for DFL to gtkD, and I can't get drawings to show up. The following code produces a blank window for me. Can someone please tell me what's wrong with it, and/or post minimal example code for getting a few lines onto a DrawingArea and displaying the results in a MainWindow?
import gtk.DrawingArea, gtk.Main, gtk.MainWindow, gdk.GC, gdk.Drawable,
gdk.Color;
void main(string[] args) {
Main.init(args);
auto win = new MainWindow("Hello, world");
win.setDefaultSize(800, 600);
auto drawingArea = new DrawingArea(800, 600);
win.add(drawingArea);
drawingArea.realize();
auto drawable = drawingArea.getWindow();
auto gc = new GC(drawable);
gc.setForeground(new Color(255, 0, 0));
gc.setBackground(new Color(255, 255, 255));
drawable.drawLine(gc, 0, 0, 100, 100);
drawingArea.showAll();
drawingArea.queueDraw();
win.showAll();
Main.run();
}
I have no experience whatsoever in D, but lots in GTK, so with the help of the gtkD tutorial I managed to hack up a minimal example:
import gtk.DrawingArea, gtk.Main, gtk.MainWindow, gdk.GC, gdk.Drawable,
gdk.Color, gtk.Widget;
class DrawingTest : MainWindow
{
this()
{
super("Hello, world");
setDefaultSize(800, 600);
auto drawingArea = new DrawingArea(800, 600);
add(drawingArea);
drawingArea.addOnExpose(&drawStuff);
showAll();
}
bool drawStuff(GdkEventExpose *event, Widget self)
{
auto drawable = self.getWindow();
auto gc = new GC(drawable);
gc.setForeground(new Color(cast(ubyte)255, cast(ubyte)0, cast(ubyte)0));
gc.setBackground(new Color(cast(ubyte)255, cast(ubyte)255, cast(ubyte)255));
drawable.drawLine(gc, 0, 0, 100, 100);
return true;
}
}
void main(string[] args) {
Main.init(args);
new DrawingTest();
Main.run();
}
In GTK, a DrawingArea is actually just a blank widget for you to paint on, and painting on widgets must always be done in the expose-event handler. (Although I understand this will change in GTK 3!)
I understand you can't connect functions as signal callbacks, only delegates, so that's the reason for the DrawingTest class.
Related
I'm developing a GUI for Arduino mega 2560 using Processing (control p5 library).
My board senses analog pin A0 and continuously displays its value as string in console. If a specific digital pin goes high then It sends the error string to processing console and waits for reset to be pressed.
Ex: A1-B1 error press reset
If A1-B1 is error then I want my GUI to fill the rectangle with red along with displaying string
" A1-B1 error press reset"
How to I do this?
Here's my processing code
import java.util.*;
import at.mukprojects.console.*;
Console console;
import processing.serial.*;
Serial port;
import controlP5.*;
ControlP5 cp5;
int myColorBackground = color(0, 0, 0);
float k,l;
String val;
int i;
char a;
void setup() {
size(800,600);
frame.setResizable(true);
smooth();
noStroke();
printArray(Serial.list());
port = new Serial(this,Serial.list()[0],9600);
port.bufferUntil(10);
cp5 = new ControlP5(this); //init gui lib
console = new Console(this); //init console
console.start();
}
void draw() {
background(myColorBackground);
fill(250, 131, 3); //text color
console.draw();
k= (width*0.75);
l=(0.25*height)-50;
fill(0);
stroke(250, 131, 1);
rect(k+20, l+20, 12,12);
fill(250, 131, 3);
textFont(font, 16);
text("A1-B1", k+100, l+20);
}
void serialEvent(Serial myPort) {
while(port.available()>0){
val = port.readStringUntil(10);
}
if (val!=null)
{
println(val);
}
}
The best advice we can give you is to break your problem down into smaller steps and take those pieces on one at a time.
For example, can you create a simple sketch that displays a message after the mouse has been clicked? Forget about the Arduino for a minute and just get this working by itself. It might look something like this:
boolean mouseWasPressed = false;
void draw(){
if(mouseWasPressed){
background(255, 0 , 0);
}
}
void mousePressed(){
mouseWasPressed = true;
}
Separately from that, get a sketch working that just shows the Arduino message in the console. It sounds like you might already have a lot of that done, but try to isolate it in a small example program.
When you have both of those working separately, then you can start thinking about combining them into one program. And if you get stuck, you can post a MCVE showing exactly which step you're stuck on. Good luck.
I am trying to use g4p-controls library in Processing to create a button that executes a drawing command in another window. In this library, a child window is created by the code
GWindow window = GWindow.getWindow(this, "Main", 100, 50, 500, 500, JAVA2D);
where this is the main applet, and the other arguments specify the name, position, and renderer.
GWindow is a subclass of PApplet, so I should feasibly be able to call a drawing command, for example window.background(0);, from anywhere in my code to paint that window black. However, this does not work, and I cannot figure out why. After all, the same code works when I put it in a handler function and add it to the window:
window.addDrawHandler(this, "windowDraw");
where the windowDraw method is
public void windowDraw(PApplet app, GWinData data) {
app.background(0);
}
Looking into the source code, the draw handler method windowDraw is invoked by the GWindow object with the first argument this, which is exactly the object referred to when I attempted window.background(0);. So window should be the object whose background() method is called to paint the canvas black.
If I am misunderstanding something fundamental about event-driven programming, please let me know. It does seem like the handlers take the relevant applet as an argument for a reason, but I really cannot see what's different about invocation inside and outside the handler functions.
Some additional notes: calling window.background(0); works if it's inside the main draw() function. It does not work if it's in the setup() function, and unfortunately for me, it doesn't work if it's in a button handler method:
public void handleButtonEvents(GButton button, GEvent event) {
if (this.button == button) {
if (event == GEvent.PRESSED) {
window.background(0);
}
}
}
Obviously, I have made sure this code actually runs when I press the button.
Even more strangely, if I substitute the above drawing call to window with something like window.strokeWeight(10), the change actually takes place, and subsequent lines in that canvas are drawn thicker. It only fails to actually draw things. I am just at a loss.
In the future, please try to post a MCVE instead of a bunch of disconnected code snippets. Here's an example:
import g4p_controls.*;
GWindow window;
void setup(){
window = GWindow.getWindow(this, "Main", 100, 50, 500, 500, JAVA2D);
}
void draw(){
background(255, 0, 0);
window.ellipse(mouseX, mouseY, 25, 25);
window.draw();
}
void mousePressed(){
window.background(0, 255, 0);
}
I would expect this code to draw circles in the second window, and to draw green in the second window when I press the mouse in the first window. However, it seems to only draw those things very sporadically.
In fact, here's the same type of program, in "pure Processing" without using the G4P library:
SecondApplet sa;
void setup() {
String[] args = {"TwoFrameTest"};
sa = new SecondApplet();
PApplet.runSketch(args, sa);
}
void settings() {
size(200, 200);
}
void draw() {
background(0);
sa.ellipse(mouseX, mouseY, 25, 25);
}
void mousePressed() {
sa.background(255, 0, 0);
}
public class SecondApplet extends PApplet {
public void settings() {
size(200, 200);
}
void draw() {
}
}
I would also expect this to work, but we see a similar gray window for the second sketch. I've filed a bug here to get feedback from the Processing devs about whether this is expected behavior.
In the meantime, you'll have better luck if you do something like this:
SecondApplet sa;
float drawMouseX;
float drawMouseY;
color drawBackground = #0000ff;
void setup() {
String[] args = {"TwoFrameTest"};
sa = new SecondApplet();
PApplet.runSketch(args, sa);
}
void settings() {
size(200, 200);
}
void draw() {
background(0);
drawMouseX = mouseX;
drawMouseY = mouseY;
}
void mousePressed() {
drawBackground = #00ff00;
}
public class SecondApplet extends PApplet {
public void settings() {
size(200, 200);
}
void draw() {
background(drawBackground);
ellipse(drawMouseX, drawMouseY, 25, 25);
}
}
Instead of calling the drawing functions directly, we're now setting some variables that are then used in the second applet's draw() function. There are a bunch of ways to do this, but the idea is the same: just don't call the draw functions directly, and call them from the draw() function instead.
Update: I heard back from Ben Fry (a founder of Processing) and codeanticode (a developer of Processing) on the bug I filed on GitHub, and I understand better now why this doesn't work.
The reason it doesn't work is because each sketch has its own UI thread, which is responsible for drawing and handling events. You can't draw to a sketch from a different thread, otherwise weird things happen. But you're trying to draw to a second sketch from the first sketche's event thread, which is not the second sketch's drawing thread, which is why it doesn't work.
See the bug for a discussion on alternative approaches, but honestly your best bet is probably to go with the approach I outlined of just sharing variables between the sketches.
In PySide you can override the paintEvent() method of a QWidget to control how the widget is painted on the screen. Is there an equivalent for Node in JavaFX?
In context: I'm in need of a way to display a custom image format on the screen. Constantly converting my format and JavaFX's Image so I can display it in an ImageView is too slow for me, in addition to being messier.
I've taken a look at ImageView.java and Canvas.java, but no luck. ImageView is using css, and Canvas appears to be doing something with the deprecated impl_ methods, for which I've found no documentation on.
Thanks!
Generally, the paint mechanisms in JavaFX changed towards a more event-based approach. To follow the JavaFX way, you should probably look at Timeline or AnimationTimer and only update the display when the actual image data changes.
However, you could use the old Swing way in JavaFX, if you like:
public class MyPane extends Pane {
private final Canvas canvas;
public MyPane() {
canvas = new Canvas(getWidth(), getHeight());
getChildren().add(canvas);
widthProperty().addListener(e -> canvas.setWidth(getWidth()));
heightProperty().addListener(e -> canvas.setHeight(getHeight()));
}
#Override
protected void layoutChildren() {
super.layoutChildren();
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.clearRect(0, 0, getWidth(), getHeight());
gc.setFill(Color.RED);
gc.fillRect(10, 10, getWidth() - 20, getHeight() - 20);
// Paint your custom image here:
gc.drawImage(someImage, 0, 0);
}
}
The above code would be the equivalence of this Swing code:
public class MyPanel extends JPanel {
private static final long serialVersionUID = -969772195113348076L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.clearRect(0, 0, getWidth(), getHeight());
g.setColor(java.awt.Color.RED);
g.fillRect(10, 10, getWidth() - 20, getHeight() - 20);
// Paint your custom image here:
g.drawImage(someImage, 0, 0, null);
}
}
How to create more than one window of a single sketch in Processing?
Actually I want to detect and track a particular color (through webcam) in one window and display the detected co-ordinates as a point in another window.Till now I'm able to display the points in the same window where detecting it.But I want to split it into two different windows.
You need to create a new frame and a new PApplet... here's a sample sketch:
import javax.swing.*;
SecondApplet s;
void setup() {
size(640, 480);
PFrame f = new PFrame(width, height);
frame.setTitle("first window");
f.setTitle("second window");
fill(0);
}
void draw() {
background(255);
ellipse(mouseX, mouseY, 10, 10);
s.setGhostCursor(mouseX, mouseY);
}
public class PFrame extends JFrame {
public PFrame(int width, int height) {
setBounds(100, 100, width, height);
s = new SecondApplet();
add(s);
s.init();
show();
}
}
public class SecondApplet extends PApplet {
int ghostX, ghostY;
public void setup() {
background(0);
noStroke();
}
public void draw() {
background(50);
fill(255);
ellipse(mouseX, mouseY, 10, 10);
fill(0);
ellipse(ghostX, ghostY, 10, 10);
}
public void setGhostCursor(int ghostX, int ghostY) {
this.ghostX = ghostX;
this.ghostY = ghostY;
}
}
One option might be to create a sketch twice the size of your original window and just offset the detected coordinates by half the sketch's size.
Here's a very rough code snippet (assumming blob will be a detected color blob):
int camWidth = 320;
int camHeight = 240;
Capture cam;
void setup(){
size(camWidth * 2,camHeight);
//init cam/opencv/etc.
}
void draw(){
//update cam and get data
image(cam,0,0);
//draw
rect(camWidth+blob.x,blob.y,blob.width,blob.height);
}
To be honest, it might be easier to overlay the tracked information. For example, if you're doing color tracking, just display the outlines of the bounding box of the tracked area.
If you really really want to display another window, you can use a JPanel.
Have a look at this answer for a running code example.
I would recommend using G4P, a GUI library for Processing that has some functionality built in for handling multiple windows. I have used this before with a webcam and it worked well. It comes with a GWindow object that will spawn a window easily. There is a short tutorial on the website that explains the basics.
I've included some old code that I have that will show you the basic idea. What is happening in the code is that I make two GWindows and send them each a PImage to display: one gets a webcam image and the other an effected image. The way you do this is to augment the GWinData object to also include the data you would like to pass to the windows. Instead of making one specific object for each window I just made one object with the two PImages in it. Each GWindow gets its own draw loop (at the bottom of the example) where it loads the PImage from the overridden GWinData object and displays it. In the main draw loop I read the webcam and then process it to create the two images and then store them into the GWinData object.
Hopefully that gives you enough to get started.
import guicomponents.*;
import processing.video.*;
private GWindow window;
private GWindow window2;
Capture video;
PImage sorted;
PImage imgdif; // image with pixel thresholding
MyWinData data;
void setup(){
size(640, 480,P2D); // Change size to 320 x 240 if too slow at 640 x 480
// Uses the default video input, see the reference if this causes an error
video = new Capture(this, 640, 480, 24);
numPixels = video.width * video.height;
data = new MyWinData();
window = new GWindow(this, "TEST", 0,0, 640,480, true, P2D);
window.isAlwaysOnTop();
window.addData(data);
window.addDrawHandler(this, "Window1draw");
window2 = new GWindow(this, "TEST", 640,0 , 640,480, true, P2D);
window2.isAlwaysOnTop();
window2.addData(data);
window2.addDrawHandler(this, "Window2draw");
loadColors("64rev.csv");
colorlength = mycolors.length;
distances = new float[colorlength];
noCursor();
}
void draw()
{
if (video.available())
{
background(0);
video.read();
image(video,0,0);
loadPixels();
imgdif = get(); // clones the last image drawn to the screen v1.1
sorted = get();
/// Removed a lot of code here that did the processing
// hand data to our data class to pass to other windows
data.sortedimage = sorted;
data.difimage = imgdif;
}
}
class MyWinData extends GWinData {
public PImage sortedimage;
public PImage difimage;
MyWinData(){
sortedimage = createImage(640,480,RGB);
difimage = createImage(640,480,RGB);
}
}
public void Window1draw(GWinApplet a, GWinData d){
MyWinData data = (MyWinData) d;
a.image(data.sortedimage, 0,0);
}
public void Window2draw(GWinApplet a, GWinData d){
MyWinData data = (MyWinData) d;
a.image(data.difimage,0,0);
}
I wanna make an application with "vertical entries" and I want that the backgroung keep static while you go up or down seeing the entries, I use that code:
VerticalFieldManager BGVFM = new VerticalFieldManager(VerticalFieldManager.USE_ALL_WIDTH | VerticalFieldManager.USE_ALL_HEIGHT) {
public void paint(Graphics graphics) {
graphics.drawBitmap(0, 0, Display.getWidth(), Display.getHeight(), fondo, 0, 0);
super.paint(graphics);
}
};
...
add(BGVFM)
But if I scroll, the image. It's possible to do what I want to do?