unable to draw correctly in Gtk+3 - gtk

I can draw in a single Gtk.DrawingArea, but when i try to do it for many, for example 50, i got some errors in drawing.
Here is the code you need to check out:
def aggiorna(self, args=()):
import random
import time
while True:
for i in self.indirizzi_ip:
self.r=random.randint(0,10)/10.0
self.g=random.randint(0,10)/10.0
self.b=random.randint(0,10)/10.0
self.cpu_info[i]['drawing_area'].show() #the drawing happens here
time.sleep(1)
def __draw(self, widget, context): #connected to Gtk.DrawingArea.show()
context.set_source_rgb(self.r, self.g, self.b) #random
context.rectangle(0, 0, widget.get_allocated_width(), widget.get_allocated_height())
context.fill()
1) why do i get errors in drawing?
2) why do Gtk.DrawingArea(s) change color ONLY when i update the window (for example i switch from a program to Gtk.DrawingArea window)?
3) why don't i get random colors for each Gtk.DrawingArea?

cant help you on this
because it only changes color when Gtk.DrawingArea repainted itself ("draw" signal)
the r,g,b should be inside "draw" function. you did construct r,g,b but since it's outside the draw function, it did not change while area repainted.
why the sleep?
** edited **
sample code:
....
win = Gtk.Window ()
box = Gtk.Box ()
self.square_list = []
for square in range (10):
self.darea = Gtk.DrawingArea ()
self.set_size_request (50,50)
self.square_list.append (self.darea)
box.add (self.darea)
#for each darea connect to separate "draw" signal
self.darea.connect ("draw", self,_draw)
aggiorna_button = Gtk.Button ('Aggiorna!!!') #sorry i use button
box.add (aggiorna_button)
aggiorna.button.connect ("clicked", self.aggiorna)
def _draw (self, widget, cr):
r = random.randint (0,10)/10.0
g = random.randint (0,10)/10.0
b = random.randint (0,10)/10.0
cr.set_source_rgb (r,g,b)
cr.rectangle (0,0, widget.get_allocated_width(), widget.get_allocated_height())
cr.fill ()
def aggiorna (self, widget):
for darea in self.square_list:
darea.queue_draw()

Related

windows.forms and redrawing bitmaps

I'm implementing a gameboy emulator as so many before me.
I'm trying to implement the PPU and to do this I'm using a class representing the screen.
// needed because VS can't find it as dependency
#r "nuget: System.Windows.Forms"
open System
open System.Windows.Forms
open System.Drawing
type Screen(title, width : int, height : int) as screen =
inherit Form()
let mutable canvas = new Bitmap(width, height)
do
// set some attributes of the screen
screen.Size <- new Size(width, height)
screen.Text <- title
interface IDisposable with
member S.Dispose() = (canvas :> IDisposable).Dispose()
override S.OnPaint e =
e.Graphics.DrawImage(canvas,0,0) // here
base.OnPaint(e)
member S.Item
with get (w, h) = canvas.GetPixel(w,h)
and set (w,h) pixel = canvas.SetPixel(w,h,pixel)
But I can't get it to update the screen after I have redrawing the bitmap, it does not show the redrawn image.
the redraw
let main () =
let screen = new Screen("gameboy",800,600)
Application.Run(screen)
// test example
for i in 0 .. 300 do
screen.[i,i] <- Drawing.Color.Black
(screen :> Form).Refresh()
i.e. How do I make it to redraw after update of the bitmap?
You can't do anything graphical after Application.Run has been called, because it doesn't finish until the user closes the main form. Instead, you can create an event handler that is called once the main form is loaded, like this:
let main argv =
let screen = new Screen("gameboy",800,600)
screen.Load.Add(fun _ ->
for i in 0 .. 300 do
screen.[i,i] <- Drawing.Color.Black)
Application.Run(screen)
0

ScalaFX canvas poor performance when drawing image with animation timer

I plan to make a rhythm game by using ScalaFX with canvas,
When I try to run the code, I found that it consumes a lot of GPU, and sometimes the frame rate drop at 30 fps, even I only draw one image on the canvas without drawing any animated note, dancer, process gauge, etc.
Below is my code
import scalafx.animation.AnimationTimer
import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.canvas.{Canvas, GraphicsContext}
import scalafx.scene.image.Image
import scalafx.scene.layout.Pane
import scalafx.scene.paint.Color.Green
object MainApp extends JFXApp{
var MainScene: Scene = new Scene {
fill = Green
}
var MainStage: JFXApp.PrimaryStage = new JFXApp.PrimaryStage {
scene = MainScene
height = 720
width = 1280
}
var gameCanvas:Canvas = new Canvas(){
layoutY=0
layoutX=0
height=720
width=1280
}
var gameImage:Image = new Image("notebar.png")
var gc:GraphicsContext = gameCanvas.graphicsContext2D
MainScene.root = new Pane(){
children=List(gameCanvas)
}
var a:Long = 0
val animateTimer = AnimationTimer(t => {
val nt:Long = t/1000000
val frameRate:Long = 1000/ (if((nt-a)==0) 1 else nt-a)
//check the frame rate
println(frameRate)
a = nt
gc.clearRect(0,0,1280,720)
gc.drawImage(gameImage,0,0,951,160)
})
animateTimer.start()
}
how can I improve the performance or is there any better ways to do the same thing without using canvas?
There are a few factors that are potentially slowing down the frame rate:
You are outputting the frame rate to the console every frame. This is a very slow operation, and also can be expected to slow down the frame rate. (This is probably the biggest performance hit.)
You are calculating the frame rate during frame rendering. Philosophically, a good example of Heisenberg's uncertainty principle, since by measuring the frame rate, you're interfering with it and slowing it down... ;-)
You are clearing the entire canvas each time you want to redraw your image, rather than just that part of it taken up the image. (This initially proved not to be a huge factor in my version of your code, but when I disabled JavaFX's speed limit—see update below—it turned out to make a big difference.)
Regarding the frame rate, in the version below, I record the time (in nanoseconds) of the first frame, and count the number of frames drawn. When the application exits, it reports the average frame rate. This is a simpler calculation that doesn't interfere too much with the operations inside the animation handler, and is a good measure of overall performance. (There's going to be considerable variability in the timing of each frame, due to garbage collection, other processes, JIT compilation improvements, etc. We'll try to skip over all of that by looking at the average rate.)
I also changed the code to clear only the region occupied by the image.
I've also simplified your code a little, to make it slightly more conventional in its use of ScalaFX (using the stage member of the main class, for instance, as well as making more use of type inference):
import scalafx.animation.AnimationTimer
import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.canvas.Canvas
import scalafx.scene.image.Image
import scalafx.scene.layout.Pane
import scalafx.scene.paint.Color.Green
object MainApp
extends JFXApp {
// Nanoseconds per second.
val NanoPerSec = 1.0e9
// Height & width of canvas. Change in a single place.
val canvasHeight = 720
val canvasWidth = 1280
// Fixed canvas size.
val gameCanvas = new Canvas(canvasWidth, canvasHeight)
// The image.
val gameImage = new Image("notebar.png")
val gc = gameCanvas.graphicsContext2D
stage = new JFXApp.PrimaryStage {
height = canvasHeight
width = canvasWidth
scene = new Scene {
fill = Green
root = new Pane {
children=List(gameCanvas)
}
}
}
// Class representing an initial frame time, last frame time and number of frames
// drawn. The first frame is not counted.
//
// (Ideally, this would be declared in its own source file. I'm putting it here for
// convenience.)
final case class FrameRate(initTime: Long, lastTime: Long = 0L, frames: Long = 0L) {
// Convert to time in seconds
def totalTime: Double = if(frames == 0L) 1.0 else (lastTime - initTime) / NanoPerSec
def mean: Double = frames / totalTime
def update(time: Long): FrameRate = copy(lastTime = time, frames = frames + 1)
}
// Current frame rate.
private var frames: Option[FrameRate] = None
val animateTimer = AnimationTimer {t =>
// Update frame rate.
frames = Some(frames.fold(FrameRate(t))(_.update(t)))
// Send information to console. Comment out to determine impact on frame rate.
//println(s"Frame rate: ${frames.fold("Undefined")(_.mean.toString)}")
// Clear region of canvas.
//
// First clears entire canvas, second only image. Comment one out.
//gc.clearRect(0, 0, canvasWidth, canvasHeight)
gc.clearRect(0, 0, gameImage.width.value, gameImage.height.value)
// Redraw the image. This version doesn't need to know the size of the image.
gc.drawImage(gameImage, 0, 0)
}
animateTimer.start()
// When the application terminates, output the mean frame rate.
override def stopApp(): Unit = {
println(s"Mean frame rate: ${frames.fold("Undefined")(_.mean.toString)}")
}
}
(BTW: avoid use of var statements in Scala whenever possible. Shared mutable state is unavoidable when using JavaFX/ScalaFX, but Propertys provide much better mechanisms for dealing with it. Try to get into the habit of using val element declarations unless they really, really do need to be vars. And if you do need to use vars, they should nearly always be declared private to prevent uncontrolled external access and modification.)
Benchmarking Java programs is an art form, but clearly, the longer you run each version, the better the average frame rate is going to be. On my machine (with an image of my own) I achieved the following, rather unscientific, results after running the application for 5 minutes:
Clearing entire canvas and writing to console: 39.69 fps
Clearing entire canvas, no output to console: 59.85 fps
Clearing only image, no output to console: 59.86 fps
Clearing just the image, rather than the whole canvas appears to have little effect, and surprised me a little. However, outputting to the console had a huge effect on the frame rate.
Aside from using a canvas, another possibility is to simply position an image within a scene group, and then move it around by changing its co-ordinates. The code to do that is below (using properties to indirectly move the image):
import scalafx.animation.AnimationTimer
import scalafx.application.JFXApp
import scalafx.beans.property.DoubleProperty
import scalafx.scene.{Group, Scene}
import scalafx.scene.image.ImageView
import scalafx.scene.layout.Pane
import scalafx.scene.paint.Color.Green
import scalafx.scene.shape.Rectangle
object MainApp
extends JFXApp {
// Height & width of app. Change in a single place.
val canvasHeight = 720
val canvasWidth = 1280
// Nanoseconds per second.
val NanoPerSec = 1.0e9
// Center of the circle about which the image will move.
val cX = 200.0
val cY = 200.0
// Radius about which we'll move the image.
val radius = 100.0
// Properties for positioning the image (might be initial jump).
val imX = DoubleProperty(cX + radius)
val imY = DoubleProperty(cY)
// Image view. It's co-ordinates are bound to the above properties. As the properties
// change, so does the image's position.
val imageView = new ImageView("notebar.png") {
x <== imX // Bind to property
y <== imY // Bind to property
}
stage = new JFXApp.PrimaryStage {
height = canvasHeight
width = canvasWidth
scene = new Scene {thisScene => // thisScene is a self reference
fill = Green
root = new Group {
children=Seq(
new Rectangle { // Background
width <== thisScene.width // Bind to scene/stage width
height <== thisScene.height // Bind to scene/stage height
fill = Green
},
imageView
)
}
}
}
// Class representing an initial frame time, last frame time and number of frames
// drawn. The first frame is not counted.
//
// (Ideally, this would be declared in its own source file. I'm putting it here for
// convenience.)
final case class FrameRate(initTime: Long, lastTime: Long = 0L, frames: Long = 0L) {
// Convert to time in seconds
def totalTime: Double = if(frames == 0L) 1.0 else (lastTime - initTime) / NanoPerSec
def mean: Double = frames / totalTime
def update(time: Long) = copy(lastTime = time, frames = frames + 1)
}
// Current frame rate.
var frames: Option[FrameRate] = None
val animateTimer = AnimationTimer {t =>
// Update frame rate.
frames = Some(frames.fold(FrameRate(t))(_.update(t)))
// Change the position of the image. We'll make the image move around a circle
// clockwise, doing 1 revolution every 10 seconds. The center of the circle will be
// (cX, cY). The angle is therefore the modulus of the time in seconds divided by 10
// as a proportion of 2 pi radians.
val angle = (frames.get.totalTime % 10.0) * 2.0 * Math.PI / 10.0
// Update X and Y co-ordinates related to the center and angle.
imX.value = cX + radius * Math.cos(angle)
imY.value = cY + radius * Math.sin(angle)
}
animateTimer.start()
// When the application terminates, output the mean frame rate.
override def stopApp(): Unit = {
println(s"Mean frame rate: ${frames.fold("Undefined")(_.mean.toString)}")
}
}
This produces a mean frame rate for me, after 5 minutes of running, of 59.86 fps—almost exactly the same as using a canvas.
In this example, the motion is a little jerky, which could well be caused by garbage collection cycles. Maybe try experimenting with different GC's?
BTW, I move the image around in this version to force something to happen. If the properties don't change, then I suspected that the image would not be updated that frame. Indeed, if I just set the properties to the same value each time, the frame rate becomes: 62.05 fps.
Using the canvas means that you have to determine what is drawn, and how to redraw it. But using the JavaFX scene graph (as in the last example) means that JavaFX takes care of figuring out whether the frame even needs to be redrawn. It doesn't make a big difference in this particular case, but it might speed things up if there are few content differences between successive frames. Something to bear in mind.
Is that fast enough? BTW, there's a lot of overhead in relation to content in this particular example. I wouldn't be at all surprised if adding other elements to the animation had very little impact upon the frame rate. It would probably be best to try it and see. Over to you...
(For another possibility regarding animation, refer to the ColorfulCircles demo that comes with the ScalaFX sources.)
UPDATE: I mentioned this in a comment, but it's perhaps worth highlighting in the main answer: JavaFX has a default speed limit of 60 fps, which also impacts the benchmarking above—and which also explains why the CPU and GPU are not better utilized.
To enable your application to run at the highest possible frame rate (possibly not a great idea if you're looking to maximize battery charge on a laptop, or to improve overall application performance), enable the following property when running your application:
-Djavafx.animation.fullspeed=true
Note that this property is undocumented and unsupported, meaning that it may go away in a future version of JavaFX.
I re-ran the benchmarks with this property set, and observed these results:
Using a canvas:
Clearing entire canvas and writing to console: 64.72 fps
Clearing entire canvas, no output to console: 144.74 fps
Clearing only image, no output to console: 159.48 fps
Animating a scene graph:
No output to console: 217.68 fps
These results change my original conclusions significantly:
Rendering images—and even animating them—using the scene graph is far more efficient (36% better in terms of frame rate) than the best result obtained when drawing the image on the canvas. This is not unexpected, given that the scene graph is optimized to improve performance.
If using the canvas, clearing only the region occupied by the image has a roughly 10% (for this example) better frame rate than clearing the entire canvas.
Refer to this answer for further details about the javafx.animation.fullspeed property.

ImageJ: how to get Image inside GenericDialog to repaint?

In my ImageJ plugin I display a GenericDialog which has a bunch of images attached to it, like this:
// global:
ColorProcessor cp = new ColorProcessor(50, 50); // new ColorProcessor
ImagePlus ip;
public void run(ImageProcessor ip) {
GenericDialog gdiag = new GenericDialog("Foo"); // new Dialolg
gdiag.addDialogListener(this); // adding Listener
gdiag.addMessage("Lorem Ipsum"); // adding Message
gdiag.addSlider("Bar", 1, 360, 1); // adding Slider
Color c = new Color(r, g, b);
cp.setColor(tarColor);
cp.fill();
ip = new ImagePlus("fooimg", cp);
gdiag.addImage(ip);
gdiag.showDialog();
}
I keep a reference to the Colorprocessor and the ImagePlus. When the slider gets moved on the GenericDialog, my the dialogItemChanged() event fires. Here I change the Color on the Image:
public boolean dialogItemChanged(GenericDialog gd, AWTEvent event) {
float fooVal = (float) ((Scrollbar)(gd.getSliders().get(0))).getValue();
// calculating color based on fooVal ...
Color selColor = new Color(r, g, b);
cp.setColor(selColor);
cp.fill();
}
Now when I run this, the Color in the Image does not update. Only when I change the size of the dialog and move the border over the image, the color displays correctly.
How can I force the dialog to repaint?
I tried so many different updates & repaints, I am out of options.
Wayne Rasband added this capability in the 1.51b12 daily build of ImageJ; see his response on the ImageJ mailing list, where this question was cross-posted.

How to select a region with QRubberBand on a QLabel like in KSnapshot?

I am writing a screenshot utility with PyQt, and the idea is take a screenshot of the whole desktop, then display it in a QLabel, make the window full screen and user selects a region by mouse.
Is it possible to do this efficiently with a QLabel? I want the rubber band to stay on screen and it still can be tweaked. In this case, would I have to use QGraphicsScene?
Desired effect:
http://gfycat.com/SkinnyObeseAquaticleech
here is what I have so far
import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import Qt, QPoint, QRect, QSize
from PyQt4.QtGui import QPixmap, QApplication, QLabel, QRubberBand
class MyLabel(QLabel):
def __init__(self, parent=None):
QLabel.__init__(self, parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.origin = QPoint()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = QPoint(event.pos())
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rubberBand.show()
def mouseMoveEvent(self, event):
if not self.origin.isNull():
self.rubberBand.setGeometry(
QRect(self.origin, event.pos()).normalized())
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.rubberBand.hide()
class mainUI(QtGui.QWidget):
def __init__(self):
super(mainUI, self).__init__()
self.initUI()
def initUI(self):
layout = QtGui.QVBoxLayout(self)
label = MyLabel(self)
pixmap = QPixmap.grabWindow(app.desktop().winId())
label.setPixmap(pixmap)
layout.addWidget(label)
self.setLayout(layout)
geometry = app.desktop().availableGeometry()
self.setFixedSize(geometry.width(), geometry.height())
# self.setWindowFlags( self.windowFlags() | Qt.FramelessWindowHint)
self.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = mainUI()
sys.exit(app.exec_())
Your approach is already quite far and I think it's possible to achieve what you want with a QLabel. I extended your example so that the rubber band stays on screen even after the mouse is released and you can drag the upper left and lower right corners of it.
You could extend it even more to drag the other corners and the sides and showing a label with the size in the middle.
In the picture you see the selection staying without the mouse pressed.
from PyQt4 import QtGui, QtCore
class RubberbandEnhancedLabel(QtGui.QLabel):
def __init__(self, parent=None):
QtGui.QLabel.__init__(self, parent)
self.selection = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle, self)
def mousePressEvent(self, event):
'''
Mouse is pressed. If selection is visible either set dragging mode (if close to border) or hide selection.
If selection is not visible make it visible and start at this point.
'''
if event.button() == QtCore.Qt.LeftButton:
position = QtCore.QPoint(event.pos())
if self.selection.isVisible():
# visible selection
if (self.upper_left - position).manhattanLength() < 20:
# close to upper left corner, drag it
self.mode = "drag_upper_left"
elif (self.lower_right - position).manhattanLength() < 20:
# close to lower right corner, drag it
self.mode = "drag_lower_right"
else:
# clicked somewhere else, hide selection
self.selection.hide()
else:
# no visible selection, start new selection
self.upper_left = position
self.lower_right = position
self.mode = "drag_lower_right"
self.selection.show()
def mouseMoveEvent(self, event):
'''
Mouse moved. If selection is visible, drag it according to drag mode.
'''
if self.selection.isVisible():
# visible selection
if self.mode is "drag_lower_right":
self.lower_right = QtCore.QPoint(event.pos())
elif self.mode is "drag_upper_left":
self.upper_left = QtCore.QPoint(event.pos())
# update geometry
self.selection.setGeometry(QtCore.QRect(self.upper_left, self.lower_right).normalized())
app = QtGui.QApplication([])
screen_pixmap = QtGui.QPixmap.grabWindow(app.desktop().winId())
window = QtGui.QWidget()
layout = QtGui.QVBoxLayout(window)
label = RubberbandEnhancedLabel()
label.setPixmap(screen_pixmap)
layout.addWidget(label)
geometry = app.desktop().availableGeometry()
window.setFixedSize(geometry.width(), geometry.height())
window.show()
app.exec_()

Gtk3 loading PixbufAnimation inside DrawingArea?

For the purpose of mine gstreamer application I tought about simple loader before I give a handle of DrawingArea widget to sink element.The basic idea was to load an animated .gif inside Gtk.DrawingArea but I run on the problem with documentation.I found out about PixbufAnimation and I used it with Gtk.Image widget but the same logic doesn't work for Gtk.DrawingArea and since it doesn't have add method I don't know what to do so as my last resort I came here to get a help.
This is what I did with Gtk.Image:
from gi.repository import Gdk,Gtk,GdkPixbuf
class animatedWin(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self,width_request=640,height_request=480)
self.canvas=Gtk.Image()
self.add(self.canvas)
self.load_file()
self.connect("delete-event",self.Gtk.main_quit)
def load_file(self):
self.loader=GdkPixbuf.PixbufAnimation.new_from_file("loader.gif")
self.canvas.set_from_animation(self.loader)
app=animatedWin()
app.show_all()
Gtk.main()
is it possible to achieve the same thing with DrawingArea ?
DrawingArea like most widgets in gtk3 uses cairo for drawing on them. Cairo draws on surfaces using context. You can convert pixbuf into surface by
public Surface Gdk.cairo_surface_create_from_pixbuf (Pixbuf pixbuf, int scale, Window? for_window)
And back by
public Pixbuf? Gdk.pixbuf_get_from_surface (Surface surface, int src_x, int src_y, int width, int height)
(taken from valadoc.org)
Example code snippet from my drawing app (I'm learning Vala while I writing it, so it may not be best implementation):
private void on_scale (Gtk.Button button) { // on button press
var my_pixbuf = Gdk.pixbuf_get_from_surface (this.buf_surface, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
var tmp_surface = Gdk.cairo_surface_create_from_pixbuf (my_pixbuf, 2, null);
var ctx = this.ccc; //this.ccc is context of drawing surface
ctx.set_source_surface (tmp_surface, 0, 0);
ctx.paint();
drawing_area.queue_draw(); // ask drawing_area to redraw, on redraw I have function/method that will paint drawing_area widget surface with drawing surface
}
PS. see http://valadoc.org/#!api=cairo/Cairo for more info on cairo. As I see it, cairo used for vector graphics and pixbuf for raster.