Re: Drawing Area issues ( continued, with example )
The Energy Monitor is looking good. Eric ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
Thanks everyone. Once it was confirmed that we can't have multiple cairo contexts happening, I slightly rewrote the graphing to just use one, and it works fine. Screenshot: http://tesla.duckdns.org/images/powercom.png I'm getting close to another release of the above project ... Dan On Sun, Oct 15, 2017 at 1:16 PM, wrote: > > > Hi Dan, > > The graph axis can be made dynamic so that each graph is different. That way > you can show different ranges side by side. I experimented a little with > this by setting a couple of array values that hold the number of tick marks > on each graph. It is in C. I think that I need a big screen for it. Then > maybe I could test a large number of graphs and data points. > > Eric > > https://github.com/cecashon/OrderedSetVelociRaptor/blob/master/Misc/cairo_drawings/four_graphs2.c > ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
Hi Dan, The graph axis can be made dynamic so that each graph is different. That way you can show different ranges side by side. I experimented a little with this by setting a couple of array values that hold the number of tick marks on each graph. It is in C. I think that I need a big screen for it. Then maybe I could test a large number of graphs and data points. Eric https://github.com/cecashon/OrderedSetVelociRaptor/blob/master/Misc/cairo_drawings/four_graphs2.c ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
Hi Emmanuele, Thank you for the answer here. Very helpful. There is a code comment in gdkinternals.h that says struct _GdkWindow { ... struct { /* The temporary surface that we're painting to. This will be composited * back into the window when we call end_paint. This is our poor-man's * way of doing double buffering. */ cairo_surface_t *surface; ... This was how I was thinking about it. It is internal but the widgets GdkWindow surface is used. Right? Or is the only cairo surface being used to draw on is the top level GdkWindow surface by default? By the way, Thanks for all the work you have done putting some of this stuff together. A lot more drawing potential in GTK these days. Eric ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
On 11 October 2017 at 19:34, Eric Cashon via gtk-perl-list wrote: I think there's some confusion, here, about the role of a GdkWindow and rendering… > This is something that I have had some trouble with at the app level. In > GTK3, how drawing is done, has changed a bit between minor versions in GTK3. > In order to get CSS, transparency, OpenGL and modern drawing techniques > working well,,, things have changed. Here is an article about container > windows. I have always considered containers not to have windows to draw on > but that may not be true anymore. > > https://blog.gtk.org/2017/05/24/container-secrets/ Widgets do not need windows to draw any more. It hasn't been true for years, even if the transition happened during the 3.x API series. A GdkWindow itself doesn't mean that a real windowing system surface is used to render its contents; this has been true since the 2.x days. A GtkWidget has a GdkWindow for rendering *only* if Gtk.Widget.set_has_window() is called with `TRUE`; additionally, a GtkWidget can add more GdkWindows for separate rendering and input handling, assuming it calls Gtk.Widget.register_window(), to let the toolkit know. Rendering starts from the top-level, and each widget will be traversed, with the draw() virtual function being called with a Cairo context. The nature of the Cairo context is entirely inconsequential, so you should *never* assume anything about its state. You're only supposed to draw your widget using the allocated size as the size of the surface; if you want to make sure to avoid overdraw, in case you're not using widgets but you're rendering your own objects, you can also query the size of the clip, and avoid rendering things outside of that region. I wrote a blog post about how GTK+ renders last year, for the development blog: https://blog.gtk.org/2016/06/15/drawing-in-gtk/ > Some widgets might use the background window. For example a label or even a > plug inside a socket. You can always try a GtkEventBox if you need a window > to draw on behind a widget if it doesn't have it's own window. You can also use Gtk.DrawingArea and call Gtk.Widget.set_has_window(); the drawing area widget will then create a GdkWindow for you inside its realize() implementation. > A GtkDrawingArea has it's own window to draw on. No, it really doesn't. Of course, unless you call Gtk.Widget.set_has_window(), which is not the default. The default behaviour of GtkDrawingArea, in GTK+ 3.22, is to use the parent's window. In a typical GTK application, this means it'll render on the top-level window, which ideally should be the only native windowing system surface in use for rendering. Incidentally, for GTK+ 4.0 we're cleaning up this mess, and the only windowing system surface for both rendering and input will be the one created by GtkWindow; everything else will render on that, and input will be handled by the GTK widget hierarchy. > If you put a drawing area > over the main window and draw on both with transparency you can see this. This is not really correct. Transparency is done via opacity groups in Cairo and GTK; if a widget has a different opacity than its parent, and its parent also has a different opacity than 1.0, it'll be rendered into an intermediate surface, and then composited at the parent's opacity, to avoid blending errors. > How GTK draws the layout using cairo and the compositor might be a different > argument though. Cairo contexts are the same. Yes, the Cairo context is the same because the Cairo context is propagated through the widget hierarchy, assuming that the surface used as the target is the same. That's how we can implement semi-transparent widgets, or how we can do things like border shadows in CSS. This is also why it's fundamental to always draw using the Cairo context given to you by GTK itself, and never call gdk_cairo_create(). If you want to draw into an intermediate surface you should create your own Cairo surface, render into it, and then use that surface as the source on the Cairo context inside the draw() implementation. Ciao, Emmanuele. -- https://www.bassi.io [@] ebassi [@gmail.com] ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
"Eric Cashon via gtk-perl-list" : > A GtkDrawingArea has it's own window to draw on. You're right, there seems to be a lot of changes going on in this area under the hood. I was basing my statement on the C code in gtk+'s master branch, where drawing areas clearly have no GdkWindow of their own: https://git.gnome.org/browse/gtk+/tree/gtk/gtkdrawingarea.c#n301 But it looks like this is a recent change, because on the gtk-3-22 branch drawing areas do have their own GdkWindow: https://git.gnome.org/browse/gtk+/tree/gtk/gtkdrawingarea.c?h=gtk-3-22#n187 ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
Hi Torsten, This is something that I have had some trouble with at the app level. In GTK3, how drawing is done, has changed a bit between minor versions in GTK3. In order to get CSS, transparency, OpenGL and modern drawing techniques working well,,, things have changed. Here is an article about container windows. I have always considered containers not to have windows to draw on but that may not be true anymore. https://blog.gtk.org/2017/05/24/container-secrets/ Some widgets might use the background window. For example a label or even a plug inside a socket. You can always try a GtkEventBox if you need a window to draw on behind a widget if it doesn't have it's own window. A GtkDrawingArea has it's own window to draw on. If you put a drawing area over the main window and draw on both with transparency you can see this. How GTK draws the layout using cairo and the compositor might be a different argument though. Cairo contexts are the same. The following has two transparent windows that you can draw on. Check out the context and gdkwindow pointer values. Tested on Ubuntu 16.04 and GTK3.18. If you have GTK3.22 there will probably be a warning about is_composited() but it should still work. More changes. Eric #!/usr/bin/perl use strict; use diagnostics; use warnings; use Gtk3 '-init'; use Glib qw(TRUE FALSE); my $window = Gtk3::Window->new(); $window->signal_connect('delete_event' => sub { Gtk3->main_quit; }); $window->set_default_size(400, 400); $window->set_border_width(10); $window->set_title("Layers"); $window->set_app_paintable(TRUE); if ($window->is_composited()) { my $screen = $window->get_screen(); my $visual=$screen->get_rgba_visual(); $window->set_visual($visual); } else { print "Couldn't set transparency.\n" } $window->signal_connect('draw' => \&draw_window); my $da = Gtk3::DrawingArea->new(); $da->set_vexpand(TRUE); $da->set_hexpand(TRUE); $da->signal_connect('draw' => \&draw_da); $window->add($da); $window->show_all(); Gtk3->main; sub draw_da { my ($widget, $cr, $data) = @_; print "da context " . $cr . "\n" . " da GdkWindow " . $widget->get_window() . "\n"; my $width = $widget->get_allocated_width(); my $height = $widget->get_allocated_height(); #Drawing area tranparent background. $cr->set_source_rgba(0, 0, 0, 0); $cr->paint; #Draw a line. $cr->set_source_rgba(0, 1, 1, 0.5); $cr->set_line_width(40); $cr->move_to(0, $height / 2); $cr->line_to($width, $height / 2); $cr->stroke(); return FALSE; } sub draw_window { my ($widget, $cr, $data) = @_; print "window context " . $cr . "\n" . " window GdkWindow " . $widget->get_window() . "\n"; my $width = $widget->get_allocated_width(); my $height = $widget->get_allocated_height(); #Window semi-tranparent background. $cr->set_source_rgba(0, 0, 1, 0.6); $cr->paint; #Draw a line. $cr->set_source_rgba(1, 1, 0, 0.5); $cr->set_line_width(40); $cr->move_to($width / 2, 0); $cr->line_to($width / 2, $height); $cr->stroke(); return FALSE; } ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
"Daniel Kasak" : > Does anyone know ... would this bug be in the bindings, or in gtk+ or cairo? If I understand correctly, this is by design. GtkDrawingArea does not have its own GdkWindow, hence all GtkDrawingAreas within a GtkWindow share the underlying GdkWindow and thus the GdkDrawingContext and thus the cairo context. Prior to invoking each draw callback, the shared cairo context is altered so that any drawing done with it ends up in the right spot. ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
Ah, thankyou :) Yes, indeed, if I avoid constructing further contexts, it works. I can replace this with just calling set_source_rgba() on the 1 context in *most* cases, though I'd have to have *two* passes for the graphs, as I draw a 'highlight' line ( lighter colour ) as well as the 'regular' line that encloses the bars ( and which I call ->fill inside of later ). Still, that's not much more work. Does anyone know ... would this bug be in the bindings, or in gtk+ or cairo? Dan On Wed, Oct 11, 2017 at 1:43 PM, Jeremy Volkening wrote: > On Tue, Oct 10, 2017 at 09:38:03PM +1100, Daniel Kasak wrote: >> >> I've cut down my app to a "relatively small" example which doesn't >> need a database connection or loads of other stuff. It's still 400+ >> lines ( with lots of whitespace ). I know it's asking a lot, but if >> someone can figure out what I'm doing wrong, I'd be very grateful ... > > > Your issues stem from the creation of multiple contexts referencing the > DrawingArea surface within the 'draw' callback, i.e. > > my $surface = $cairo_context->get_target; > # and later.. > my $this_stat_context = Cairo::Context->create( $surface ); > > If you re-write your code without doing this -- just use the context passed > into the 'draw' signal callback along with save() and restore() -- I think > you will find your issues disappear. I haven't completely re-written your > code, but I tested on one small bit and it had the desired effect. > > Why this happens is a little bit hazy to me. If you print the values > passed into the 'draw' callback, you can see that all of the pointers to the > cairo context are the same for the different DAs. Somehow this gets sorted > out if you just use the context as given, but my guess is that when you are > calling get_target() it returns the surface for the first DrawingArea only, > and then your issues arise. > > Regards, > Jeremy > > -- > Civilization is the limitless multiplication of unnecessary necessities. > -- Mark Twain > ___ > gtk-perl-list mailing list > gtk-perl-list@gnome.org > https://mail.gnome.org/mailman/listinfo/gtk-perl-list ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
On Tue, Oct 10, 2017 at 09:38:03PM +1100, Daniel Kasak wrote: I've cut down my app to a "relatively small" example which doesn't need a database connection or loads of other stuff. It's still 400+ lines ( with lots of whitespace ). I know it's asking a lot, but if someone can figure out what I'm doing wrong, I'd be very grateful ... Your issues stem from the creation of multiple contexts referencing the DrawingArea surface within the 'draw' callback, i.e. my $surface = $cairo_context->get_target; # and later.. my $this_stat_context = Cairo::Context->create( $surface ); If you re-write your code without doing this -- just use the context passed into the 'draw' signal callback along with save() and restore() -- I think you will find your issues disappear. I haven't completely re-written your code, but I tested on one small bit and it had the desired effect. Why this happens is a little bit hazy to me. If you print the values passed into the 'draw' callback, you can see that all of the pointers to the cairo context are the same for the different DAs. Somehow this gets sorted out if you just use the context as given, but my guess is that when you are calling get_target() it returns the surface for the first DrawingArea only, and then your issues arise. Regards, Jeremy -- Civilization is the limitless multiplication of unnecessary necessities. -- Mark Twain ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
Hi. Yes, I could do it that way, and I might have to if I can't figure out what's wrong with the multi-drawing-area approach. It shouldn't be required though, and there are complexities to using a single drawing area - for example selecting dates that are quite disperse ( I'd have to project each day's graph to have them all render side-side-side without large gaps in between ). Dan On Wed, Oct 11, 2017 at 8:30 AM, wrote: > > You might be able to just use one drawing area. Then you don't have to worry > about creating and destroying drawing areas, surfaces and contexts. When you > want to redraw, just "paint" the drawing area background and start drawing. > This approach works well for most drawings. > > Test out the following and see if it is close. Just a grid in one or four > panes with one drawing area. > > Eric > > #!/usr/bin/perl > use strict; > use diagnostics; > use warnings; > use Gtk3 '-init'; > use Glib qw(TRUE FALSE); > > my $window = Gtk3::Window->new(); > $window->signal_connect('delete_event' => sub { Gtk3->main_quit; }); > $window->set_default_size(800, 400); > $window->set_border_width(10); > $window->set_title("Graphs"); > > #Number of graphs. Either 1 or 4. Changed by the two buttons. > my $graphs = 1; > > my $da = Gtk3::DrawingArea->new(); > $da->set_vexpand(TRUE); > $da->set_hexpand(TRUE); > $da->signal_connect('draw' => \&draw_graph); > > my $single_button = Gtk3::Button->new( 'graph 1 date' ); > $single_button->signal_connect('clicked' => \&single_button_clicked, $da); > > my $multiple_button = Gtk3::Button->new( 'graph 4 dates' ); > $multiple_button->signal_connect('clicked' => \&multiple_button_clicked, > $da); > > my $grid1 = Gtk3::Grid->new(); > $grid1->set_row_spacing(10); > $grid1->attach($da, 0, 0, 4, 4); > $grid1->attach($single_button, 1, 5, 1, 1); > $grid1->attach($multiple_button, 2, 5, 1, 1); > > $window->add($grid1); > > $window->show_all(); > Gtk3->main; > > sub draw_graph > { > my ($widget, $cr, $data) = @_; > my $width = $widget->get_allocated_width(); > my $height = $widget->get_allocated_height(); > #Some drawing variables. > my $x = 0; > my $y = 0; > my $graph_width = $width / $graphs; > my $vertical_width = $graph_width / 24; > my $horizontal_width = $height / 4; > > #Paint background. > $cr->set_source_rgb(0, 0, 0); > $cr->paint; > > #Vertical lines. > $cr->set_source_rgb(0, 0, 1); > $cr->set_line_width(1); > for (my $i=0; $i < $graphs; $i++) > { > for (my $j = 0; $j < 24; $j++) > { > $x = $i * $graph_width + $j * $vertical_width; > $cr->move_to($x, 0); > $cr->line_to($x, $height); > $cr->stroke(); > } > } > > #24 numbers. > $cr->set_source_rgb(1, 1, 1); > for (my $i=0; $i < $graphs; $i++) > { > for (my $j = 0; $j < 24; $j++) > { > $x = $i * $graph_width + $j * $vertical_width; > $cr->move_to($x + 5, $height - 10); > $cr->show_text ($j); > } > } > > #Horizontal lines. > $cr->set_source_rgb(0, 0, 1); > for (my $i=0; $i < $graphs; $i++) > { > for (my $j = 1; $j < 4; $j++) > { > $y = $j * $horizontal_width; > $cr->move_to(0, $y); > $cr->line_to($width, $y); > $cr->stroke(); > } > } > > #4 numbers. > $cr->set_source_rgb(1, 1, 1); > for (my $i=0; $i < $graphs; $i++) > { > for (my $j = 0; $j < 4; $j++) > { > $y = $j * $horizontal_width; > $cr->move_to(5, $y + 20); > $cr->show_text (2000 - $j * 500); > } > } > > > #Draw graph blocks. > $cr->set_source_rgb(0, 1, 1); > $cr->set_line_width(10); > $cr->rectangle(0, 0, $width, $height); > $cr->stroke(); > $cr->set_line_width(5); > for (my $i=1; $i < $graphs; $i++) > { > $x = $i * $graph_width; > $cr->move_to($x, 0); > $cr->line_to($x, $height); > $cr->stroke(); > } > > return FALSE; > } > sub single_button_clicked > { > my ($widget, $da) = @_; > $graphs = 1; > $da->queue_draw(); > } > sub multiple_button_clicked > { > my ($widget, $da) = @_; > $graphs = 4; > $da->queue_draw(); > } > > ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list
Re: Drawing Area issues ( continued, with example )
You might be able to just use one drawing area. Then you don't have to worry about creating and destroying drawing areas, surfaces and contexts. When you want to redraw, just "paint" the drawing area background and start drawing. This approach works well for most drawings. Test out the following and see if it is close. Just a grid in one or four panes with one drawing area. Eric #!/usr/bin/perl use strict; use diagnostics; use warnings; use Gtk3 '-init'; use Glib qw(TRUE FALSE); my $window = Gtk3::Window->new(); $window->signal_connect('delete_event' => sub { Gtk3->main_quit; }); $window->set_default_size(800, 400); $window->set_border_width(10); $window->set_title("Graphs"); #Number of graphs. Either 1 or 4. Changed by the two buttons. my $graphs = 1; my $da = Gtk3::DrawingArea->new(); $da->set_vexpand(TRUE); $da->set_hexpand(TRUE); $da->signal_connect('draw' => \&draw_graph); my $single_button = Gtk3::Button->new( 'graph 1 date' ); $single_button->signal_connect('clicked' => \&single_button_clicked, $da); my $multiple_button = Gtk3::Button->new( 'graph 4 dates' ); $multiple_button->signal_connect('clicked' => \&multiple_button_clicked, $da); my $grid1 = Gtk3::Grid->new(); $grid1->set_row_spacing(10); $grid1->attach($da, 0, 0, 4, 4); $grid1->attach($single_button, 1, 5, 1, 1); $grid1->attach($multiple_button, 2, 5, 1, 1); $window->add($grid1); $window->show_all(); Gtk3->main; sub draw_graph { my ($widget, $cr, $data) = @_; my $width = $widget->get_allocated_width(); my $height = $widget->get_allocated_height(); #Some drawing variables. my $x = 0; my $y = 0; my $graph_width = $width / $graphs; my $vertical_width = $graph_width / 24; my $horizontal_width = $height / 4; #Paint background. $cr->set_source_rgb(0, 0, 0); $cr->paint; #Vertical lines. $cr->set_source_rgb(0, 0, 1); $cr->set_line_width(1); for (my $i=0; $i < $graphs; $i++) { for (my $j = 0; $j < 24; $j++) { $x = $i * $graph_width + $j * $vertical_width; $cr->move_to($x, 0); $cr->line_to($x, $height); $cr->stroke(); } } #24 numbers. $cr->set_source_rgb(1, 1, 1); for (my $i=0; $i < $graphs; $i++) { for (my $j = 0; $j < 24; $j++) { $x = $i * $graph_width + $j * $vertical_width; $cr->move_to($x + 5, $height - 10); $cr->show_text ($j); } } #Horizontal lines. $cr->set_source_rgb(0, 0, 1); for (my $i=0; $i < $graphs; $i++) { for (my $j = 1; $j < 4; $j++) { $y = $j * $horizontal_width; $cr->move_to(0, $y); $cr->line_to($width, $y); $cr->stroke(); } } #4 numbers. $cr->set_source_rgb(1, 1, 1); for (my $i=0; $i < $graphs; $i++) { for (my $j = 0; $j < 4; $j++) { $y = $j * $horizontal_width; $cr->move_to(5, $y + 20); $cr->show_text (2000 - $j * 500); } } #Draw graph blocks. $cr->set_source_rgb(0, 1, 1); $cr->set_line_width(10); $cr->rectangle(0, 0, $width, $height); $cr->stroke(); $cr->set_line_width(5); for (my $i=1; $i < $graphs; $i++) { $x = $i * $graph_width; $cr->move_to($x, 0); $cr->line_to($x, $height); $cr->stroke(); } return FALSE; } sub single_button_clicked { my ($widget, $da) = @_; $graphs = 1; $da->queue_draw(); } sub multiple_button_clicked { my ($widget, $da) = @_; $graphs = 4; $da->queue_draw(); } ___ gtk-perl-list mailing list gtk-perl-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-perl-list