Mint_21.3 drawing-area和goocanvas的FB笔记(一)

文章介绍了在Linux环境下使用FreeBasic和Goocanvas进行图形界面开发,包括FreeBasic的特点、与C语言的兼容性,以及如何利用Goocanvas进行items操作和界面设计,特别是通过cairo库实现的各种绘图功能,如像素数据渲染、图像表面和自定义绘图函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、关于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的,但用它绘图方式上有些不同,需要另外的方法处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值