一、关于freebasic和goocanvas
Linux下的FreeBasic是C的一种实现,有指针、类、线程,正则表达式,可内嵌asm和其它语言c等,c的h库几乎都能改写后使用(不能直接用,它的.bi可从h近乎自动转换),老的Quick Basic语句也能使用,屏幕方式增加了 screenres x, y 按屏幕尺寸像素定义大小, 比如 screenres 1920, 1080等, 其它语言写成的.so动态库可以类似.h一样写.bi, 然后即可调用,最后编译成本地二制执行文件,调用sqlite, mysql, postgreSQL也行。它free, 功能比较全,稳定性比较好。c能用的界面库它都能用,比较典型的是gtk库,基于cairo的goocanvas也比较有趣。下面的图一是freeBasic操作goocanvas实现的items分组、变换、和模拟运动。
(图一、goocanvas表格图)
下图的上部分是cairo渲染的png图,下部分是goocanvas画线、矩形和文字显示。
(图二、cairo png和goo canvas画图)
下图的上部分是cairo渲染的pixbuf像素数据,下部分是goocanvas画线、矩形和文字显示。
(图三、pixbuf作为cairo的surface渲染)
下图的上部分是cairo渲染的语句绘图,下部分是goocanvas画线、矩形和文字显示。
二、界面设计笔记
界面垂直分成四个部分,上部cairo绘图区、label显示区、goocanvas绘图区、操作区。设计时可用glade作界面设计, box1留一个空位并置入一个gtxbox, ID为canvasbox,在编程时将goocanvas作为一个gtkwidget放入其中,大小也是程序中直接写,glade只需留出带gtkbox的空位即可。设计好后存成文件 glade_newui.glade, 程序中gtk_builder会取出widget属性和信号,程序中也可改写。
将widget作为GObject,定义其指针; 建gtkbuilder; 将glade中的widget与定义的指针对应起来,然后定义一个goocanvas并将写放入到预留的canbox中,最后将界面最大化。
DIM AS STRING GUISTR
DIM SHARED AS GtkBuilder PTR XML
DIM SHARED AS GObject PTR window1
DIM SHARED AS GObject PTR box1
DIM SHARED AS GObject PTR canbox
DIM SHARED AS GObject PTR button1
DIM SHARED AS GObject PTR button2
DIM SHARED AS GObject PTR button3
DIM SHARED AS GObject PTR button4
DIM SHARED AS GObject PTR label1
DIM SHARED AS GObject PTR drawarea1
DIM SHARED AS Gerror PTR errptr
Dim shared as GtkWidget PTR canvas
Dim shared as GooCanvasItem PTR root
XML = gtk_builder_new()
GUISTR="glade_newui.glade"
window1 = gtk_builder_get_object(XML, @"window")
box1 = gtk_builder_get_object(XML, @"box1")
canbox = gtk_builder_get_object(XML, @"canvasbox")
drawarea1 = gtk_builder_get_object(XML, @"drawarea1")
button1 = gtk_builder_get_object(XML, @"button1")
button2 = gtk_builder_get_object(XML, @"button2")
button3 = gtk_builder_get_object(XML, @"button3")
button4 = gtk_builder_get_object(XML, @"button4")
label1 = gtk_builder_get_object(XML, @"label1")
canvas = goo_canvas_new ()
root = goo_canvas_get_root_item (GOO_CANVAS (canvas))
gtk_widget_set_size_request (canvas, 600, 250)
gtk_widget_show (canvas)
gtk_container_add (GTK_CONTAINER (canbox), canvas)
gtk_window_maximize(GTK_WINDOW(window1))
三、cairo绘图笔记
glade设计时上部分是 drawingarea1 ,它有个 draw 信号(gtk3),后面写个回调函数的名称。
下面是回调函数,draw信号带来widget指针,cairo-context, 和用户数据指针(如果在glade设计时指定的话),接下来是取得drawingarea1的宽和高(如果界面是sizeable可变的,需要在使用时先取尺寸),然后画个白色的矩形,接着是判断自定义DrawfunctionFlag值,并根据值调用相应的绘图function函数。
FUNCTION on_drawarea1_draw CDECL ALIAS "on_drawarea1_draw" ( _
BYVAL widget AS GtkWidget PTR, _
BYVAL cr AS cairo_t PTR, _
BYVAL user_data AS gpointer) AS gboolean EXPORT
'get drawingarea width and height
VAR w = gtk_widget_get_allocated_width(widget)
VAR h = gtk_widget_get_allocated_height(widget)
'clear drawingarea surface
DIM AS GdkRGBA col
col.red = 1: col.green = 1: col.blue = 1: col.alpha = 1
gdk_cairo_set_source_rgba(cr, @col)
cairo_rectangle(cr, 0, 0, w, h)
cairo_fill(cr)
cairo_stroke(cr)
'do as per DrawfuctionFlag value
If DrawfunctionFlag = 0 then
on_drawarea1_draw1(widget, cr, user_data)
elseif DrawfunctionFlag = 1 then
on_drawarea1_draw2(widget, cr, user_data)
elseif DrawfunctionFlag = 2 then
on_drawarea1_draw3(widget, cr, user_data)
End if
'return a value as per function definition requirement
RETURN DrawfunctionFlag
END FUNCTION
功能一:用语句直接画个大圆,stroke 到界面上去。
FUNCTION on_drawarea1_draw1 CDECL ALIAS "on_drawarea1_draw1" ( _
BYVAL widget AS GtkWidget PTR, _
BYVAL cr AS cairo_t PTR, _
BYVAL user_data AS gpointer) AS gboolean EXPORT
VAR w = gtk_widget_get_allocated_width(widget)
VAR h = gtk_widget_get_allocated_height(widget)
DIM AS GdkRGBA col
col.red = 1: col.green = 1: col.blue = 1: col.alpha = 1
gdk_cairo_set_source_rgba(cr, @col)
cairo_rectangle(cr, 0, 0, w, h)
cairo_fill(cr)
col.red = 1: col.green = 0: col.blue = 0: col.alpha = 1
gdk_cairo_set_source_rgba(cr, @col)
cairo_arc(cr, w/2, h/2, MIN(w/2, h/2), 0, 2 * G_PI)
gdk_cairo_set_source_rgba(cr, @col) 'Equal to ---> cairo_set_source_rgba (cr, 1 , 0 , 0 , 1)
cairo_fill(cr)
cairo_stroke(cr)
RETURN FALSE
END FUNCTION
功能二:用pixbuf数据创建一个surface, 把创建的surface给 draw 信号的cr进行渲染,pixbuf 像素数据就用像素显示到界面上了。
Function on_drawarea1_draw2 cdecl( byval widget as GtkWidget ptr, _
BYVAL cr AS cairo_t PTR, _
byval userdata as gpointer) as gboolean
Dim surf as cairo_surface_t PTR
surf = cairo_image_surface_create_for_data (@rgbbuf(0), CAIRO_FORMAT_ARGB32, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_WIDTH*4)
cairo_set_source_surface(cr, surf, 50, 100)
cairo_paint (cr)
cairo_surface_destroy(surf)
return TRUE
End function
pixbuf像素数据是个byte数组,一个彩色的像素点如果用rgb表示的话,需要r、g、b三个数据,每个数据一个byte,即8bit,三个数据是24位。数据表示为 Dim shared rgbbuf(IMAGE_WIDTH * IMAGE_HEIGHT * 3) as byte,是全部像素的字节数。
rgb格式加上alpha是rgba格式,因为多了个alpha, 所以Dim shared rgbbuf(IMAGE_WIDTH * IMAGE_HEIGHT * 4) as byte, 为全部像素的字节数。
函数cairo_image_surface_create_for_data的最后一个参数是一行像素的字节数,这样它就知道了断行显示,否则数据连在一起就分不开了。程序定义的是个数组,然后循环赋值。
#define IMAGE_WIDTH 256
#define IMAGE_HEIGHT 256
Dim shared rgbbuf(IMAGE_WIDTH * IMAGE_HEIGHT * 4) as byte
''create a 32-bit RGB image by adding alpha
for y = 0 to IMAGE_HEIGHT-1
for x = 0 to IMAGE_WIDTH-1
rgbbuf(i+0) = y - (y and 31) '' B
rgbbuf(i+1) = (x \ 32) * 4 + y - (y and 31) '' G
rgbbuf(i+2) = x - (x and 31) '' R
rgbbuf(i+3) = &HFF '' A
i += 4
next
next
上面的数据画出来的图是下面的格子图
功能三:调用png图片文件作为surface渲染
surf = cairo_image_surface_create_from_png("fblogo.png")
其它不用改动。
Function on_drawarea1_draw3 cdecl( byval widget as GtkWidget ptr, _
BYVAL cr AS cairo_t PTR, _
byval userdata as gpointer) as gboolean
Dim surf as cairo_surface_t PTR
surf = cairo_image_surface_create_from_png("fblogo.png")
cairo_set_source_surface(cr, surf, 200, 100)
cairo_paint (cr)
cairo_surface_destroy(surf)
return TRUE
End function
这三个功能通过DrawFunctionFlag的值来选择,公共变量DrawFunctionFlag的值是由操作区的button press动作赋值的。代码写在了函数里,先是改变它的值,然后取得button click信号带过来的user_data这个widget的尺寸,在glade设计时带过来的drawingarea1,所以这里的user_data就是drawingarea1, 下面的一句是驱动drawingarea的draw信号动作的关键一句:
gtk_widget_queue_draw_area (GTK_WIDGET(user_data), 0, 0, w, h)
它是个需要重绘的矩形区,这里是全部区域,也可以是部分需要更新的区域,比如画实时曲线时的很小一部分变化,不过图小的时候全绘也不会有速度问题的。全绘的话,也可以去掉后面的_area,就直接是全绘了,后面的参数也省了。
Function setDrawfunctionFlag CDECL ALIAS "setDrawfunctionFlag" ( _
BYVAL widget AS GtkWidget PTR, _
BYVAL user_data AS gpointer) AS gboolean EXPORT
if DrawfunctionFlag + 1 >2 then
DrawfunctionFlag = 0
else
DrawfunctionFlag += 1
end if
VAR w = gtk_widget_get_allocated_width(GTK_WIDGET(user_data))
VAR h = gtk_widget_get_allocated_height(GTK_WIDGET(user_data))
gtk_widget_queue_draw_area (GTK_WIDGET(user_data), 0, 0, w, h)
Return TRUE
End Function
surface可以通过pixbuf产生,cairo-context可以由surface产生,可以操作cr在surface上绘图。如果释放掉surface产生的cr, 将surface作为界面送来cr的源surface,数据的图就渲染到界面上去了,类似内存考贝后叠加成新图片。goocanvas是基于cairo的,但用它绘图方式上有些不同,需要另外的方法处理。