In Windows, I can blit a bitmap to a DC picked up by GetDC at any time without calling InvalidateRect issuing a WM_PAINT message. How do I port this behavior to GTK/cairo?
In a background thread, I want to
Scale a given bitmap to "best-fit" inside a window (Need to know the width and height of in pixels)
Blit the scaled image to the screen
It should not be done through the expose event (the render loop is fast so the window will be drawn in time anyway). Full logic is
Create a window
Get context of the window
Start message loop
User presses a "Start" button
A new program thread is started that checks for a stop flag. If it is false, it issues commands that update the contents of the window regularly without user interaction. Meanwhile, the message loop checks for user input.
When user presses a "Stop" button the stop flag raises causing the rendering thread to exit
When rendering thread is not running, the expose event should call the same code as the render thread.
I have tried to catch the cairo_t* cairo_obj after creating a GtkDrawingArea (I get one)
the animation thread then calls image draw repeatedly issuing
cairo_set_source_rgb(cairo_obj, 1, 0, 0);
cairo_select_font_face(cairo_obj, "Sans", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cairo_obj, 40.0);
cairo_move_to(cairo_obj, 10.0, 50.0);
cairo_show_text(cairo_obj, "Disziplin ist Macht.");
This works when doing event passing but not otherwise. Are there any other calls that has to be done at the end of the drawing code swapping buffers?
If this cannot be done with GTK/cairo, are there any other toolkit that lets me do this? Is it possible under X at all?
Related
GTK+ version: 3.18.9
I created a drawing area (GtkWidget) with following code
content = gtk_drawing_area_new();
gtk_widget_set_can_focus(content, TRUE);
gtk_widget_add_events(content, GDK_ALL_EVENTS_MASK);
g_signal_connect(content, "draw", G_CALLBACK(&drawCallback), ctx);
gtk_widget_realize(content);
// Add a filter for interception
gdk_window_add_filter(content, gtk_widget_get_window(content),
OnXEvent, NULL);
Problem is that when I clicked the wideget, I got sequence of XEvents as follows:
LeaveNotify
FocusIn
EnterNotify
ButtonPress
FocusOut // Lost focus!
ButtonRelease
Above FocusOut indicated we lost focus right away after clicking.
It implies we can't receive any keyboard events since they are available only within focus.
Is this limitation of GTK? If not, is there any tools/methodlogy to find out which widget/widow trigger the FocusOut?
References:
gdk_window_add_filter
As of GTK+ version 3.18.9, it uses an extra X window for toplevel window and any keyboard events are captured by the window. Thus a filter need to be added as a global one, i.e.
gdk_window_add_filter(NULL, OnXEvent, NULL)
Note that the API was removed in this commit in GTK+ 4.x.
I have a multi-form application in which a child form is positioned on the second monitor on startup, at which time its BoundsRect is saved.
When the computer's display configuration changes, Windows moves the form to the first (primary) monitor. I can catch this change with WM_DISPLAYCHANGE:
procedure WMDisplayChange(var msg: TWMDisplayChange); message WM_DISPLAYCHANGE;
What I'm interested in doing is moving the child form back to the second monitor when it reappears in the configuration (i.e. Screen.MonitorCount goes from 1 to 2), e.g.:
childForm.BoundsRect := childForm.m_WorkingBounds;
// (or)
childForm.BoundsRect := Screen.Monitors[Screen.MonitorCount-1].BoundsRect;
However this assignment is have no affect -- the child form stays on monitor 0.
I've tried other approaches, such as SetWindowPos(), with no success ...
Root of your problem is in the fact that Delphi VCL does not refresh its internal list of monitors when they actually change. You have to force that refresh yourself.
Monitors are refreshed with TScreen.GetMonitors method that is unfortunately private method so you cannot call it directly.
However, TApplication.WndProc(var Message: TMessage) processes WM_WTSSESSION_CHANGE and upon receiving that message it calls Screen.GetMonitors - this is most benign way to achieve your goal.
When you receive notifications that monitors are changed just send it to Application:
SendMessage(Application.Handle, WM_WTSSESSION_CHANGE, 0, 0);
I tested this with old version Delphi5 and it worked easy just to:
Screen.Free;
Screen := TScreen.Create(Nil);
The screen handling has changed in later versions of Delphi, however a similar approach may work.
I'm using GtkHScale (which is a slider) to perform seeking in a media player. I connected the "value_changed" signal of the widget to a callback so that the user can seek. I also set a timer, that will update the widget every 250ms so that it will show the current position in track. Unfortunately GTK fires the "value_changed" signal regardless of whether the slider was clicked by the user or updated by the timer. I tried seeking in the "clicked" signal, and the callback was fired, but it always sought to the beginning
You can keep track of whether the user is moving the slider by connecting to the button-press-event and button-release-event signals. When the user presses the mouse button you want to block the updates from the player so they don't move the slider when the user is trying to seek. In the button release handler you can unblock the updates.
One technique is to surround calls to gtk_range_set_value() for playback updates with g_signal_handler_block/unblock [1] calls. This allows the value and UI of the slider to be updated without triggering specific value changed handlers.
Python GTK+ 3 example showing on_value_changed is only called with interactive scrubbing:
from gi.repository import Gtk, GLib
def on_value_changed(hscale):
print('value-changed: %s' % hscale.get_value())
def on_timeout(hscale, handler_id):
# Block value-changed handler during timed playback updates.
with hscale.handler_block(handler_id):
hscale.set_value(hscale.get_value() + 1)
return True
hscale = Gtk.HScale()
hscale.set_range(0, 100)
handler_id = hscale.connect('value-changed', on_value_changed)
window = Gtk.Window()
window.set_size_request(500, 50)
window.add(hscale)
window.connect('destroy', Gtk.main_quit)
window.show_all()
GLib.timeout_add(100, on_timeout, hscale, handler_id)
Gtk.main()
[1] https://developer.gnome.org/gobject/stable/gobject-Signals.html#g-signal-handler-block
For example I need to disable two buttons in runtime. After I disabled first button it bacame gray, the second - it also became gray. But I do not know how to make the repainting simultaneous!
I need something like that:
freeze the Form (disable repainting)
disable first button
disable second button
Enable Form repainting
How to implement that?
Look at the Win32 API WM_SETREDRAW message. For example:
SendMessage(Handle, WM_SETREDRAW, False, 0);
Button1.Enabled := False;
Button2.Enabled := False;
SendMessage(Handle, WM_SETREDRAW, True, 0);
InvalidateRect(Handle, nil, True);
Messages cannot be processed until your application re-enters a message loop, so any attempt to modify/update control state that relies on message processing will not work within a single sequence of code that does not "pump" messages.
Fortunately the VCL controls typically provide a means for force repainting without waiting for messages to be processed, via the Update method:
Button1.Enabled := False;
Button2.Enabled := False;
Button1.Update;
Button2.Update;
This works independently of having to disable form repainting. The form will not repaint until your application goes into a message loop anyway, so disabling form painting and re-enabling within a single procedure that does not itself cause message processing is a waste of time.
This may not be exactly simultaneous repainting of the two buttons, but truly simultaneous painting of two separate control is impossible without getting into multithreaded GUI painting code which I think is way beyond the scope of this problem. Calling Update on two buttons in this way will be as near simultaneous in effect as you need however.
To Elias551:
LockWindowUpdate is probably not the best way to handle this since it is intended for drag and drop operations and can introduce subtle bugs when misused.
See http://blogs.msdn.com/b/oldnewthing/archive/2007/02/22/1742084.aspx
Instead use SendMessage(hwnd, WM_SETREDRAW, FALSE, 0)
The above decision with WM_SETREDRAW does not update child windows.
Instead, i recommend RedrawWindow:
RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_ALLCHILDREN);
This could help: the API LockWindowUpdate(Handle: HWND) locks drawing to the handle and children.
ex:
procedure TForm1.ColorButtons();
begin
LockWindowUpdate(Self.Handle);
// Make some stuff
LockWindowUpdate(0);
end;
Once the locked handle is reset, the component is repainted
I have a uiscrollview within three subUIViews 1, 2, 3. I load content for subUIView1, 2,3 dynamically. Here are steps
I create scrollview with these 3
subUIview and with a default size
for each subUIView
I create an operation for each subUIView, let us say Operation 1,2,3. add to operation queue
every Operation (operation 1) calculate the content that will be later draw in related subUIView (subview 1). after calculate completed, create CGLayer and attach it with UISubview1. reset all UIsubviews frame and bound and layout scrollview subviews and setNeedDisplay for related subUIview1. then move to next operation
in SubUIView drawRect method, i call CGLayerDrawInRect.
then occasionly i
get this error Invalid Context.
sometimes, it work fine, no error sometime it happened
Any idea about this error will be welcome.
.
Take a very long time to debug and finally find what is the problem and resolved. Here is the thing
When operation 1 setNeedDisplay sent out, drawRect have not called yet
Once drawRect is called, operation 2 is already running, during drawRect running,operation 2 somehow change subview1,2,3, layout and frame at the same time.
So, the bad thing happened, context will become invalid that time because frame are changing, and also worst, you may receive exe_bad_access false message
Resolve solution, i set a singal to make sure operation 1 drawRect is done then let operation 2 start run.
I have to say XCode is terrible development tools compare what MS studio tools. It gives message like invalid Context or exe_bad_access randomly. where is the productivity for our poor developer. However, Iphone OSX is much better than MS windows mobile. that is why i am working hard to use XCode creatively.