当前位置: 首页 > backend >正文

五、xlib绘制按钮控件

文章目录

    • 1.一个简单的按钮
    • 2.鼠标悬停、移出
    • 3.鼠标单击
    • 4.子窗口实现按钮控件
    • 5.总结

在xlib所提供的接口中,没有"控件"这种高级的概念。在Linux系统使用xlib进行GUI应用程序开发,如果我们想要在xlib创建的界面中放置按钮、文本框,我们需要使用xlib提供的接口在界面中自己绘制出控件,并为控件提供鼠标、键盘响应事件。在xlib创建的界面中我们可以采用直接绘制的方式或是子窗口的方式实现这些高级控件。这篇文章中我们将介绍这两种实现"按钮"控件的方式。xlib api提供的绘制文本、绘制矩形、鼠标、键盘这些基本元素,可以认为是七巧板的每一块,我们可以利用七巧板的每一块进行组合形成复杂的图形。

1.一个简单的按钮

首先我们使用直接在主界面中绘制的方式,绘制一个矩形,在矩形中绘制文本。这样在外观上看起来就是一个简单的按钮了。在具体实现代码之前我们先定义一个结构体用于存储按钮的基本信息如下坐标、大小、文本、颜色等。如下

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <string>using namespace ststruct UIButton_s {int x;int y;int width;int height;string text;ulong  textcolor;ulong  borderColor;ulong  backgroundColor;
};

x,y用于表示按钮左上角在窗口中的坐标,width、height表示按钮宽度、高度。text表示按钮的文本,这里直接使用c++的string进行存储,主要不想管理动态内存。textcolor和bordercolor、backgroundColor分别用于存储按钮文本、边框的颜色和背景色。

示例代码如下:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <string>using namespace std;struct UIButton_s {int x;int y;int width;int height;string text;ulong  textcolor;ulong  borderColor;ulong  backgroundColor;
};void DrawButton(struct UIButton_s button,Display *display, Window window ,GC gc) {// 设置边框颜色XSetForeground(display, gc, button.borderColor);// 绘制边框XDrawRectangle(display, window, gc, button.x, button.y, button.width, button.height);// 设置填充颜色XSetForeground(display, gc, button.backgroundColor);// 填充矩形XFillRectangle(display, window, gc, button.x+1, button.y+1, button.width-2, button.height-2);XSetForeground(display,gc, button.textcolor);XDrawString(display,window,gc,  button.x + 30, button.y + 20,button.text.c_str(),button.text.length());
}int main() {Display *display = XOpenDisplay(NULL);Window win = XCreateSimpleWindow(display, DefaultRootWindow(display), 10, 10, 300, 200, 1,BlackPixel(display, DefaultScreen(display)),WhitePixel(display, DefaultScreen(display)));GC gc = XCreateGC(display, win, 0, 0);XSelectInput(display, win, ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);XMapWindow(display, win);struct UIButton_s button{0};button.x = 50;button.y = 40;button.width = 100;button.height = 30;button.text = "Button";button.textcolor = 0;button.borderColor = 0;button.backgroundColor = 0xffececec;while (1) {XEvent e;XNextEvent(display, &e);if (e.type == Expose) {DrawButton(button,display,win,gc);}}XFreeGC(display,gc);XDestroyWindow(display,win);XCloseDisplay(display);return 0;
}

使用g++或是clang++编译运行以上代码。运行效果如下:

在这里插入图片描述

我们的按钮控件显示完成了,但是现在把鼠标移上去,或是点击鼠标没有任何的反应。接下我们为“按钮控件”添加鼠标事件。

2.鼠标悬停、移出

在上面我们绘制出了一个按钮,接下来我们给按钮添加当鼠标移动悬停在按钮之上时,让鼠标的光标显示为手形的光标。xlib提供了加载鼠标光标和设置鼠标光标的方法。分别是XCreateFontCursor和XDefineCursor,两个函数的原型如下

extern Cursor XCreateFontCursor(Display*        /* display */,unsigned int    /* shape */
);extern int XDefineCursor(Display*        /* display */,Window        /* w */,Cursor        /* cursor */
);

加载光标

利用XCreateFontCursor加载一个正常的箭头鼠标光标,一个手形光标。

Cursor  normalCursor = XCreateFontCursor(display,XC_arrow);
Cursor  hoverCursor = XCreateFontCursor(display,XC_hand1);

接下在while循环中我们需要处理鼠标移动事件,当鼠标移动到按钮上时鼠标显示为手形,离开按钮区域时显示为正常形状。

    while (1) {XEvent e;XNextEvent(display, &e);if (e.type == MotionNotify) {if (e.xmotion.x >= button.x && e.xmotion.x<=button.x+button.width && e.xmotion.y>=button.y && e.xmotion.y<=button.y+button.height) {XDefineCursor(display,win,hoverCursor);}else {XDefineCursor(display,win,normalCursor);}}}

完整代码如下:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <string>
#include <X11/cursorfont.h>using namespace std;struct UIButton_s {int x;int y;int width;int height;string text;ulong  textcolor;ulong  borderColor;ulong  backgroundColor;
};void DrawButton(struct UIButton_s button,Display *display, Window window ,GC gc) {// 设置边框颜色XSetForeground(display, gc, button.borderColor);// 绘制边框XDrawRectangle(display, window, gc, button.x, button.y, button.width, button.height);// 设置填充颜色XSetForeground(display, gc, button.backgroundColor);// 填充矩形XFillRectangle(display, window, gc, button.x+1, button.y+1, button.width-2, button.height-2);XSetForeground(display,gc, button.textcolor);XDrawString(display,window,gc,  button.x + 30, button.y + 20,button.text.c_str(),button.text.length());
}int main() {Display *display = XOpenDisplay(NULL);Cursor  normalCursor = XCreateFontCursor(display,XC_arrow);Cursor  hoverCursor = XCreateFontCursor(display,XC_hand1);Window win = XCreateSimpleWindow(display, DefaultRootWindow(display), 10, 10, 300, 200, 1,BlackPixel(display, DefaultScreen(display)),WhitePixel(display, DefaultScreen(display)));GC gc = XCreateGC(display, win, 0, 0);XSelectInput(display, win, ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);XMapWindow(display, win);struct UIButton_s button{0};button.x = 50;button.y = 40;button.width = 100;button.height = 30;button.text = "Button";button.textcolor = 0;button.borderColor = 0;button.backgroundColor = 0xffececec;while (1) {XEvent e;XNextEvent(display, &e);if (e.type == Expose) {DrawButton(button,display,win,gc);}if (e.type == MotionNotify) {if (e.xmotion.x >= button.x && e.xmotion.x<=button.x+button.width && e.xmotion.y>=button.y && e.xmotion.y<=button.y+button.height) {XDefineCursor(display,win,hoverCursor);}else {XDefineCursor(display,win,normalCursor);}}}XFreeCursor(display,normalCursor);XFreeCursor(display,hoverCursor);XFreeGC(display,gc);XDestroyWindow(display,win);XCloseDisplay(display);return 0;
}

编译运行后,当我们把鼠标移动到按钮上时,鼠标显示为手形,在按钮之外显示为正常形状。

改变按钮颜色

当鼠标悬停在按钮之上时,我们可以改变按钮的背景颜色、边框和文本颜色。来增强用户交互体验效果。

        if (e.type == MotionNotify) {if (e.xmotion.x >= button.x && e.xmotion.x<=button.x+button.width && e.xmotion.y>=button.y && e.xmotion.y<=button.y+button.height) {XDefineCursor(display,win,hoverCursor);button.borderColor = 0x555555;     //鼠标悬停,改变边框,背景色button.backgroundColor = 0xfff6f6f6;DrawButton(button,display,win,gc);}else {XDefineCursor(display,win,normalCursor);button.borderColor = 0;button.backgroundColor = 0xffececec;DrawButton(button,display,win,gc);}}

事件处理函数中的代码越来越多,对代码进行重构提取出DrawNormalButton和DrawHoverButton两个函数用于绘制正常情况情况下的按钮和鼠标悬停时的按钮。完整可运行代码如下

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <string>
#include <X11/cursorfont.h>using namespace std;struct UIButton_s {int x;int y;int width;int height;string text;ulong  textcolor;ulong  borderColor;ulong  backgroundColor;
};void DrawButton(struct UIButton_s button,Display *display, Window window ,GC gc) {// 设置边框颜色XSetForeground(display, gc, button.borderColor);// 绘制边框XDrawRectangle(display, window, gc, button.x, button.y, button.width, button.height);// 设置填充颜色XSetForeground(display, gc, button.backgroundColor);// 填充矩形XFillRectangle(display, window, gc, button.x+1, button.y+1, button.width-2, button.height-2);XSetForeground(display,gc, button.textcolor);XDrawString(display,window,gc,  button.x + 30, button.y + 20,button.text.c_str(),button.text.length());
}static void DrawNormalButton(UIButton_s button, Display *display, Window window,GC gc) {button.borderColor = 0;button.backgroundColor = 0xffececec;button.textcolor = 0;DrawButton(button,display,window,gc);
}static void DrawHoverButton(UIButton_s button,Display *display, Window window,GC gc) {button.borderColor = 0x555555;     //鼠标悬停,改变边框,背景色button.backgroundColor = 0xfff6f6f6;button.textcolor = 0;DrawButton(button,display,window,gc);
}int main() {Display *display = XOpenDisplay(NULL);Cursor  normalCursor = XCreateFontCursor(display,XC_arrow);Cursor  hoverCursor = XCreateFontCursor(display,XC_hand1);Window win = XCreateSimpleWindow(display, DefaultRootWindow(display), 10, 10, 300, 200, 1,BlackPixel(display, DefaultScreen(display)),WhitePixel(display, DefaultScreen(display)));GC gc = XCreateGC(display, win, 0, 0);XSelectInput(display, win, ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);XMapWindow(display, win);struct UIButton_s button{0};button.x = 50;button.y = 40;button.width = 100;button.height = 30;button.text = "Button";button.textcolor = 0;button.borderColor = 0;button.backgroundColor = 0xffececec;while (1) {XEvent e;XNextEvent(display, &e);if (e.type == Expose) {DrawNormalButton(button,display,win,gc);}if (e.type == MotionNotify) {if (e.xmotion.x >= button.x && e.xmotion.x<=button.x+button.width && e.xmotion.y>=button.y && e.xmotion.y<=button.y+button.height) {XDefineCursor(display,win,hoverCursor);DrawHoverButton(button,display,win,gc);}else {XDefineCursor(display,win,normalCursor);DrawNormalButton(button,display,win,gc);}}}XFreeCursor(display,normalCursor);XFreeCursor(display,hoverCursor);XFreeGC(display,gc);XDestroyWindow(display,win);XCloseDisplay(display);return 0;
}

编译运行以上代码,随着鼠标移入、移出按钮区域,鼠标的光标我按钮的颜色跟着发生改变。

3.鼠标单击

当鼠标在按钮区域按下左键时,我们可以再次改变按钮的显示颜色。来进一步加强用户交互效果。同时当鼠标在按钮区域松开按钮时,我们应该让按钮恢复到悬停状态。这里需要处理鼠标左键单击和松开事件。处理逻辑如下

        if (e.type == ButtonPress && e.xbutton.button == Button1) {if (e.xmotion.x >= button.x && e.xmotion.x<=button.x+button.width && e.xmotion.y>=button.y && e.xmotion.y<=button.y+button.height) {//鼠标单击事件}}if (e.type == ButtonRelease && e.xbutton.button == Button1) {if (e.xmotion.x >= button.x && e.xmotion.x<=button.x+button.width && e.xmotion.y>=button.y && e.xmotion.y<=button.y+button.height) {//鼠标单击后松开事件}}

完整代码如下:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <string>
#include <X11/cursorfont.h>using namespace std;struct UIButton_s {int x;int y;int width;int height;string text;ulong  textcolor;ulong  borderColor;ulong  backgroundColor;
};void DrawButton(struct UIButton_s button,Display *display, Window window ,GC gc) {// 设置边框颜色XSetForeground(display, gc, button.borderColor);// 绘制边框XDrawRectangle(display, window, gc, button.x, button.y, button.width, button.height);// 设置填充颜色XSetForeground(display, gc, button.backgroundColor);// 填充矩形XFillRectangle(display, window, gc, button.x+1, button.y+1, button.width-2, button.height-2);XSetForeground(display,gc, button.textcolor);XDrawString(display,window,gc,  button.x + 30, button.y + 20,button.text.c_str(),button.text.length());
}static void DrawNormalButton(UIButton_s button, Display *display, Window window,GC gc) {button.borderColor = 0;button.backgroundColor = 0xffececec;button.textcolor = 0;DrawButton(button,display,window,gc);
}static void DrawHoverButton(UIButton_s button,Display *display, Window window,GC gc) {button.borderColor = 0x555555;     //鼠标悬停,改变边框,背景色button.backgroundColor = 0xfff6f6f6;button.textcolor = 0;DrawButton(button,display,window,gc);
}static void DrawPushedButton(UIButton_s button,Display *display,Window window, GC gc) {button.borderColor = 0xcccccc;button.backgroundColor = 0xffffffff;button.textcolor = 0;DrawButton(button,display,window,gc);
}int main() {Display *display = XOpenDisplay(NULL);Cursor  normalCursor = XCreateFontCursor(display,XC_arrow);Cursor  hoverCursor = XCreateFontCursor(display,XC_hand1);Window win = XCreateSimpleWindow(display, DefaultRootWindow(display), 10, 10, 300, 200, 1,BlackPixel(display, DefaultScreen(display)),WhitePixel(display, DefaultScreen(display)));GC gc = XCreateGC(display, win, 0, 0);XSelectInput(display, win, ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);XMapWindow(display, win);struct UIButton_s button{0};button.x = 50;button.y = 40;button.width = 100;button.height = 30;button.text = "Button";button.textcolor = 0;button.borderColor = 0;button.backgroundColor = 0xffececec;while (1) {XEvent e;XNextEvent(display, &e);if (e.type == Expose) {DrawNormalButton(button,display,win,gc);}if (e.type == MotionNotify) {if (e.xmotion.x >= button.x && e.xmotion.x<=button.x+button.width && e.xmotion.y>=button.y && e.xmotion.y<=button.y+button.height) {XDefineCursor(display,win,hoverCursor);DrawHoverButton(button,display,win,gc);}else {XDefineCursor(display,win,normalCursor);DrawNormalButton(button,display,win,gc);}}if (e.type == ButtonPress && e.xbutton.button == Button1) {if (e.xmotion.x >= button.x && e.xmotion.x<=button.x+button.width && e.xmotion.y>=button.y && e.xmotion.y<=button.y+button.height) {DrawPushedButton(button,display,win,gc);}}if (e.type == ButtonRelease && e.xbutton.button == Button1) {if (e.xmotion.x >= button.x && e.xmotion.x<=button.x+button.width && e.xmotion.y>=button.y && e.xmotion.y<=button.y+button.height) {DrawHoverButton(button,display,win,gc);}}}XFreeCursor(display,normalCursor);XFreeCursor(display,hoverCursor);XFreeGC(display,gc);XDestroyWindow(display,win);XCloseDisplay(display);return 0;
}

使用g++编译运行以上代码。运行结果如下:

在这里插入图片描述

4.子窗口实现按钮控件

上面几步我们通过直接在主界面上绘制,并处理主界面的鼠标事件实现按钮控件。我们也可以使用子窗口的方式实现按钮控件。

在主窗口上创建一个按钮子窗口的代码逻辑如下:

Window CreateButtonWindow(Display *display, Window parent, UIButton_s button) {// 创建子窗口作为按钮Window buttonWindow = XCreateSimpleWindow(display, parent, button.x, button.y, button.width+1, button.height+1, 0,BlackPixel(display, DefaultScreen(display)),WhitePixel(display, DefaultScreen(display)));XSelectInput(display, buttonWindow, ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);return buttonWindow;
}

最后关于事件的处理逻辑,前面的示例中我们所有的鼠标事件处理逻辑都是在主窗口中实现的。那时只有一个窗口,但是现在有两窗口,我们需要判断鼠标的移动、单击操作是由哪个窗口触发的。通过子窗口和直接绘制在实现逻辑上没有太大区别,在主窗口直接绘制的方式中我们需要根据当前鼠标的坐标位置来判断鼠标的操作是作用中按钮矩形区域内,而采用子窗口的方式,当我们按下按钮时,由X Server产生子窗口的鼠标事件。子窗口实现代码逻辑如下

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <string>
#include <X11/cursorfont.h>using namespace std;struct UIButton_s {int x;int y;int width;int height;string text;ulong  textcolor;ulong  borderColor;ulong  backgroundColor;
};void DrawButton(struct UIButton_s button,Display *display, Window window ,GC gc) {// 设置边框颜色XSetForeground(display, gc, button.borderColor);// 绘制边框XDrawRectangle(display, window, gc, 0, 0, button.width, button.height);// 设置填充颜色XSetForeground(display, gc, button.backgroundColor);// 填充矩形XFillRectangle(display, window, gc, 1, 1, button.width-2, button.height-2);XSetForeground(display,gc, button.textcolor);XDrawString(display,window,gc,  30, 20,button.text.c_str(),button.text.length());
}static void DrawNormalButton(UIButton_s button, Display *display, Window window) {button.borderColor = 0;button.backgroundColor = 0xffececec;button.textcolor = 0;GC gc = XCreateGC(display,window,0,nullptr);DrawButton(button,display,window,gc);XFreeGC(display,gc);
}static void DrawHoverButton(UIButton_s button,Display *display, Window window) {button.borderColor = 0x555555;     //鼠标悬停,改变边框,背景色button.backgroundColor = 0xfff6f6f6;button.textcolor = 0;GC gc = XCreateGC(display,window,0,nullptr);DrawButton(button,display,window,gc);XFreeGC(display,gc);
}static void DrawPushedButton(UIButton_s button,Display *display,Window window) {button.borderColor = 0xcccccc;button.backgroundColor = 0xffffffff;button.textcolor = 0;GC gc = XCreateGC(display,window,0,nullptr);DrawButton(button,display,window,gc);XFreeGC(display,gc);
}Window CreateButtonWindow(Display *display, Window parent, UIButton_s button) {// 创建子窗口作为按钮Window buttonWindow = XCreateSimpleWindow(display, parent, button.x, button.y, button.width+1, button.height+1, 0,BlackPixel(display, DefaultScreen(display)),WhitePixel(display, DefaultScreen(display)));XSelectInput(display, buttonWindow, ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);return buttonWindow;
}int main() {Display *display = XOpenDisplay(NULL);Cursor  normalCursor = XCreateFontCursor(display,XC_arrow);Cursor  hoverCursor = XCreateFontCursor(display,XC_hand1);Window win = XCreateSimpleWindow(display, DefaultRootWindow(display), 10, 10, 300, 200, 1,BlackPixel(display, DefaultScreen(display)),WhitePixel(display, DefaultScreen(display)));XSelectInput(display, win, ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);XMapWindow(display, win);struct UIButton_s button{0};button.x = 50;button.y = 40;button.width = 100;button.height = 30;button.text = "Button";button.textcolor = 0;button.borderColor = 0;button.backgroundColor = 0xffececec;Window buttonWindow = CreateButtonWindow(display,win,button);XMapWindow(display,buttonWindow);while (1) {XEvent e;XNextEvent(display, &e);if (e.type == Expose && e.xexpose.window == win) {DrawNormalButton(button,display,buttonWindow);}if (e.type == MotionNotify) {if (e.xmotion.window == buttonWindow) {XDefineCursor(display,win,hoverCursor);DrawHoverButton(button,display,buttonWindow);}else if (e.xmotion.window == win){XDefineCursor(display,win,normalCursor);DrawNormalButton(button,display,buttonWindow);}}if (e.type == ButtonPress && e.xbutton.button == Button1 && e.xbutton.window == buttonWindow) {DrawPushedButton(button,display,buttonWindow);}if (e.type == ButtonRelease && e.xbutton.button == Button1 && e.xbutton.window == buttonWindow) {DrawHoverButton(button,display,buttonWindow);}}XFreeCursor(display,normalCursor);XFreeCursor(display,hoverCursor);XDestroyWindow(display,buttonWindow);XDestroyWindow(display,win);XCloseDisplay(display);return 0;
}

使用g++编译以上代码,运行结果与直接在主窗口绘制没有本质区别。

5.总结

如果我们把while(1)循环中的代码提取出来放到一个函数或类中,就实现了一个窗体程序的事件主循环功能,这个功能就是gtk中gtk_main,Qt中QApplication::exec()所完成的工作。把所有绘制Button、处理Button事件的代码进行封装放到c++的类中,定义一个虚函数OnClick,在while循环中当检测到按钮事件时,调用button的OnClick函数,这样可以实现一个可复用的按钮类,这也是Qt中QPushButton的基本原理。

http://www.xdnf.cn/news/6895.html

相关文章:

  • DeepSeek-R1 Supervised finetuning and reinforcement learning (SFT + RL)
  • 怎么在excel单元格1-5行中在原来内容前面加上固定一个字?
  • NVMe简介6之PCIe事务层
  • HTTP与HTTPS协议的核心区别
  • Linux调试生成核心存储文件
  • React Hooks 必须在组件最顶层调用的原因解析
  • Linux517 rsync同步 rsync借xinetd托管 配置yum源回顾
  • 【typenum】 8 常量文件(consts.rs)
  • 第三十五节:特征检测与描述-ORB 特征
  • SummaryWriter 记录和保存训练日志
  • 阿里云服务器跑模型教程
  • 关键词长度为何重要:2025年SEO优化策略
  • 【typenum】 9 与常量泛型桥接(generic_const_mappings.rs)
  • aksharetools:大模型智能体框架agno可直接获取A股金融数据
  • BUUCTF——Nmap
  • 数据库原理及其应用 第六次作业
  • 计网| 网际控制报文协议(ICMP)
  • ecmascript 第6版特性 ECMA-262 ES6
  • 全端同步!ZKmall开源商城如何用B2B2C模板让消费者跨设备购物体验无缝衔接?
  • 第八节第四部分:认识泛型、泛型类、泛型接口
  • 如何深入学习MATLAB的高级应用?
  • java的面向对象思想
  • VASP+机器学习快速收敛AIMD
  • PyTorch分布式训练深度解析与实战案例
  • 互联网大厂Java面试:从Spring到微服务的全面探讨
  • Maven 插件扩展点与自定义生命周期
  • Linux的静态库 共享库 进程 主函数的参数
  • 【C语言练习】046. 编写插入排序算法
  • JSP与JSTL:EL表达式与MVC分层模式的完美结合
  • 环形缓冲区 ring buffer 概述