使用GTK创建简易计算器

使用GTK创建简易计算器

本文将介绍如何使用GTK(GIMP Toolkit)创建一个简单的计算器应用程序。通过这个例子,你将学习如何构建基本的图形用户界面,并了解GTK的一些常用组件和回调函数的使用。

准备工作

首先,确保你已经安装了GTK和Pango库。你可以通过在终端中运行以下命令来安装它们(适用于Ubuntu和Debian系统):

sudo apt-get install libgtk-3-dev libpango1.0-dev

项目结构

我们的项目将包含以下文件:

  • main.c:包含主函数和回调函数的源代码文件。
  • expression_parser.c:一个包含用于计算表达式的函数的源代码文件。

创建计算器应用程序

下面是一个简单的计算器应用程序的实现代码。你可以将代码保存到一个名为main.c的文件中。

#include <gtk/gtk.h>
#include <pango/pango-font.h>
#include "expression_parser.c"// 回调函数:当按下数字或操作符按钮时调用
void button_clicked(GtkWidget *button, gpointer data);// 回调函数:当按下等号按钮时调用
void equal_button_clicked(GtkWidget *button, gpointer data);// 设置窗口在屏幕上居中显示
void set_window_center(GtkWidget *window);// 创建数字和操作符按钮
GtkWidget* create_button(const gchar *label, GtkWidget *entry);int main(int argc, char *argv[]) {gtk_init(&argc, &argv);// 创建主窗口GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);gtk_container_set_border_width(GTK_CONTAINER(window), 30);gtk_window_set_default_size(GTK_WINDOW(window), 800, 800);set_window_center(window);// 创建垂直布局容器GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 15);gtk_container_add(GTK_CONTAINER(window), vbox);// 创建结果显示文本框GtkWidget *entry = gtk_entry_new();gtk_entry_set_alignment(GTK_ENTRY(entry), 1);PangoFontDescription *font_desc = pango_font_description_from_string("Sans Bold 60");gtk_widget_override_font(entry, font_desc);gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 0);// 创建网格布局容器GtkWidget *grid = gtk_grid_new();gtk_grid_set_row_spacing(GTK_GRID(grid), 5);gtk_grid_set_column_spacing(GTK_GRID(grid), 5);gtk_box_pack_start(GTK_BOX(vbox), grid, TRUE, TRUE, 0);// 数字和操作符按钮的标签数组const gchar *buttons[] = {"7", "8", "9", "/","4", "5", "6", "*","1", "2", "3", "-","0", ".", "%", "+"};int button_index = 0;for (int row = 0; row < 4; row++) {for (int col = 0; col < 4; col++) {// 创建按钮并连接回调函数GtkWidget *button = create_button(buttons[button_index], entry);gtk_grid_attach(GTK_GRID(grid), button, col, row, 1, 1);button_index++;}}// 创建等号按钮并连接回调函数GtkWidget *equal_button = create_button("=", entry);gtk_grid_attach(GTK_GRID(grid), equal_button, 0, 4, 4, 1);g_signal_connect(equal_button, "clicked", G_CALLBACK(equal_button_clicked), entry); // 设置容器的homogeneous属性为TRUE,使子组件均匀分布并随窗口大小进行缩放gtk_box_set_homogeneous(GTK_BOX(vbox), TRUE);// 显示窗口及其所有子组件gtk_widget_show_all(window);// 进入GTK主循环gtk_main();return 0;
}// 按钮点击事件的回调函数
void button_clicked(GtkWidget *button, gpointer data) {const gchar *label = gtk_button_get_label(GTK_BUTTON(button));GtkWidget *entry = GTK_WIDGET(data);const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));gchar *new_text = g_strdup_printf("%s%s", text, label);gtk_entry_set_text(GTK_ENTRY(entry), new_text);g_free(new_text);
}// 等号按钮点击事件的回调函数
void equal_button_clicked(GtkWidget *button, gpointer data) {GtkWidget *entry = GTK_WIDGET(data);const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));// 检查按钮文本是否为等号const gchar *equal_sign = "=";if (strcmp(gtk_button_get_label(GTK_BUTTON(button)), equal_sign) != 0) {// 不是等号按钮,直接将按钮文本追加到文本框中gchar *new_text = g_strdup_printf("%s%s", text, equal_sign);gtk_entry_set_text(GTK_ENTRY(entry), new_text);g_free(new_text);return;}// 执行计算操作double result = eval_expression(text);if (!isnan(result) && !isinf(result)) {gchar *result_str = g_strdup_printf("%g", result);gtk_entry_set_text(GTK_ENTRY(entry), result_str);g_free(result_str);} else {gtk_entry_set_text(GTK_ENTRY(entry), "Error");}
}// 设置窗口在屏幕上居中显示
void set_window_center(GtkWidget *window) {GdkDisplay *display = gdk_display_get_default();GdkMonitor *monitor = gdk_display_get_primary_monitor(display);GdkRectangle monitor_rect;gdk_monitor_get_geometry(monitor, &monitor_rect);gint screen_width = monitor_rect.width;gint screen_height = monitor_rect.height;gint window_width, window_height;gtk_window_get_size(GTK_WINDOW(window), &window_width, &window_height);gint x = (screen_width - window_width) / 2;gint y = (screen_height - window_height) / 2;gtk_window_move(GTK_WINDOW(window), x, y);
}// 创建按钮并连接回调函数
GtkWidget* create_button(const gchar *label, GtkWidget *entry) {GtkWidget *button = gtk_button_new_with_label(label);g_signal_connect(button, "clicked", G_CALLBACK(button_clicked), entry);gtk_widget_set_hexpand(button, TRUE);gtk_widget_set_vexpand(button, TRUE);return button;
}

在上面的代码中,我们使用了GTK提供的一些函数和宏来创建窗口、布局容器、文本框和按钮等组件。其中,button_clicked函数用于处理数字和操作符按钮的点击事件,equal_button_clicked函数用于处理等号按钮的点击事件。

实现表达式解析器

下面是一个简单的表达式解析器的实现代码,可以计算包含四则运算的数学表达式,你可以将代码保存到一个名为expression_parser.c的文件中。

首先,遍历表达式字符串的每个字符,根据字符的类型进行相应的处理。如果是空格,则忽略;如果是数字,则将连续的数字字符解析为一个操作数,并将其入栈;如果是运算符,则根据其优先级与栈顶的运算符进行比较,如果栈顶的运算符优先级较高或相等,则从栈中弹出运算符和操作数进行计算,并将计算结果入栈。

最后,当所有字符处理完毕后,还可能剩余一些运算符和操作数没有处理,此时需要对它们进行计算。循环从栈中取出运算符和操作数,执行相应的计算,并将结果入栈,直到栈中只剩下一个元素,即为最终的计算结果。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>double eval_expression(const char* expression);// 获取运算符的优先级
int get_operator_priority(char op) {switch (op) {case '+':case '-':return 1;case '*':case '/':case '%':return 2;default:return 0;}
}// 执行二元运算
double perform_operation(double left_operand, char op, double right_operand) {switch (op) {case '+':return left_operand + right_operand;case '-':return left_operand - right_operand;case '*':return left_operand * right_operand;case '/':return left_operand / right_operand;case '%':return fmod(left_operand, right_operand);default:return 0.0;}
}// 检查字符是否为数字
bool is_digit(char ch) {return (ch >= '0' && ch <= '9');
}// 计算表达式的结果
double eval_expression(const char* expression) {int length = strlen(expression);double operands[length]; // 操作数栈char operators[length]; // 运算符栈int operand_top = -1; // 操作数栈顶指针int operator_top = -1; // 运算符栈顶指针for (int i = 0; i < length; i++) {char current = expression[i];if (current == ' ') {continue; // 忽略空格} else if (is_digit(current)) {// 解析数字double operand = 0.0;int decimal_places = 0;while (is_digit(expression[i]) || expression[i] == '.') {if (expression[i] == '.') {decimal_places = 1;} else {if (decimal_places > 0) {operand += (expression[i] - '0') / (10.0 * decimal_places);decimal_places *= 10;} else {operand = operand * 10 + (expression[i] - '0');}}i++;}operands[++operand_top] = operand;i--; // 回退一个字符,因为 for 循环会再自增} else {// 解析运算符while (operator_top >= 0 && get_operator_priority(operators[operator_top]) >= get_operator_priority(current)) {double right_operand = operands[operand_top--];double left_operand = operands[operand_top--];char op = operators[operator_top--];double result = perform_operation(left_operand, op, right_operand);operands[++operand_top] = result;}operators[++operator_top] = current;}}// 处理剩余的运算符和操作数while (operator_top >= 0) {double right_operand = operands[operand_top--];double left_operand = operands[operand_top--];char op = operators[operator_top--];double result = perform_operation(left_operand, op, right_operand);operands[++operand_top] = result;}return operands[0];
}

代码中的eval_expression函数接受一个字符串参数expression,该字符串表示一个数学表达式。函数通过遍历字符串的每个字符,将表达式中的操作数和运算符进行解析和计算,最终返回表达式的结果。

代码中的get_operator_priority函数用于获取运算符的优先级,根据运算符的不同,返回不同的优先级值。优先级用于确定运算符的计算顺序。

perform_operation函数用于执行两个操作数之间的二元运算,根据运算符的不同,执行相应的加法、减法、乘法或除法运算,并返回运算结果。

is_digit函数用于检查一个字符是否是数字,通过判断字符的ASCII码值是否在数字字符的范围内来确定。

eval_expression函数中,代码使用两个栈来解析和计算表达式。operands数组作为操作数栈,存储解析得到的操作数;operators数组作为运算符栈,存储解析得到的运算符。

编译和运行

要编译该程序,你可以使用以下命令:

gcc main.c -o calculator `pkg-config --cflags --libs gtk+-3.0` -lm

然后,在终端中运行可执行文件:

./calculator

这将启动计算器应用程序并显示一个简单的界面,你可以通过点击按钮进行数字输入和运算。
在这里插入图片描述

总结

通过这个简单的计算器示例,你学习了如何使用GTK创建基本的图形用户界面。GTK提供了丰富的功能和组件,可用于开发各种类型的应用程序。希望这个例子能够帮助你入门GTK开发,并为你构建更复杂的应用程序打下基础。

你可以在GTK官方文档中找到更多关于GTK的详细信息和示例代码。祝你在GTK项目中取得成功!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/13424.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis如何实现持久化

RDB快照 RDB是一种快照存储持久化方式&#xff0c;具体就是将Redis某一时刻的内存数据保存到硬盘的文件当中&#xff0c;默认保存的文件名为dump.rdb&#xff0c;而在Redis服务器启动时&#xff0c;会重新加载dump.rdb文件的数据到内存当中恢复数据。 开启RDB持久化方式 开启…

【CEEMDAN-WOA-LSTM】完备集合经验模态分解-鲸鱼优化-长短时记忆神经网络研究(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

MATLAB 之 可视化图形用户界面设计

这里写目录标题 一、可视化图形用户界面设计1. 图形用户界面设计窗口1.1 图形用户界面设计模板1.2 图形用户界面设计窗口 2. 可视化图形用户界面设计工具1.1 对象属性检查器2.2 菜单编辑器2.3 工具栏编辑器2.4 对齐对象工具2.5 对象浏览器2.6 Tab 键顺序编辑器 3. 可视化图形用…

[毕业设计baseline]tkinter+flask的毕业设计开发baseline

一.前言 最近开发了一个结合了tkinter和flask框架的GUI页面服务器。目前可以想到的开发方向有。 1.基于python的局域网聊天系统。 2.服务器管理系统。 3.网络安全防御系统。 接下来就来介绍一下这个框架以及开发方向的详细思路。如果计算机专业的本科毕业生感兴趣可以用pyt…

基于单片机的蓝牙音乐喷泉的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;通过HM-18蓝牙音频模块进行无线传输&#xff1b; 通过LM386功放模块对音频信号进行放大&#xff1b;手机端可以直接控制音频播放&#xff0c;并且最远距离可达20米&#xff1b;手机端可以进行任意音乐切换&#xff0c;播报、暂停&a…

记一次阿里云被挖矿处理记录

摘要 莫名其妙的服务器就被攻击了&#xff0c;又被薅了羊毛&#xff0c;当做免费的挖矿劳动力了。 一、起因 上班&#xff08;摸鱼&#xff09;好好的&#xff0c;突然收到一条阿里云的推送短信&#xff0c;不看不知道&#xff0c;两台服务器被拉去作为苦力&#xff0c;挖矿去…

具备捕获 Web2 用户能力的 PoseiSwap,治理通证$POSE再度涨超 360%

Nautilus Chain 是行业内首个模块化 Layer3 架构链&#xff0c;开发者能够基于模块化进行定制化开发&#xff0c;并有望进一步推动 Web3 应用向隐私、合规等方向发展。当然&#xff0c;Nautilus Chain 的特殊之处还在于为生态用户带来丰厚的空投预期&#xff0c;据悉上线 Nauti…

android Surface(1, 2)

android Surface(1, 2) android的Surface相关内容从底层依次往上分别是&#xff1a; 1.frameBuffer&#xff0c;简称fb&#xff0c;对于同一个android系统&#xff0c;可以同时存在多个frameBuffer&#xff0c;本机是fb0&#xff0c;依次外接时&#xff0c;fb1, fb2, ……fbn…

安全漏洞的检测利用

安全漏洞的检测&利用 一、安全漏洞的基本概念1.1、什么是漏洞1.2、漏洞的简单理解1.3、微软的RPC漏洞与蠕虫病毒1.4、微软经典的蓝屏漏洞1.5、Heartbleed&#xff08;心脏滴血&#xff09;漏洞1.6、破壳漏洞CVE-2014-62711.7、漏洞的危害1.8、漏洞的成因1.9、漏洞的信息的组…

electron+vue3全家桶+vite项目搭建【21】自定义窗口拖拽移动

引入 如果你尝试过透明窗口&#xff0c;并控制透明部分事件击穿&#xff0c;就会发现使用 drag属性样式去控制窗口拖拽会导致点击事件失效&#xff0c;并且带drag属性的窗口移动到另一个窗口的透明部分会有窗口乱动的各种BUG&#xff0c;于是&#xff0c;这便需要我们自己去实…

mac上 如何批量在文件名后加相同的文字?

mac上如何批量在文件名后加相同的文字&#xff1f;不管你是使用windows电脑还是使用mac电脑&#xff0c;很多小伙伴都会在电脑上进行文件批量重命名的操作&#xff0c;不过这项操作对于使用windows系统电脑的小伙伴来说会简单一些&#xff0c;因为在网上可以搜索到很多这样的教…

软考:中级软件设计师:计算机体系结构,CISC和RISC,Flynn分类,指令流水线,吞吐率,效率

软考&#xff1a;中级软件设计师:计算机体系结构 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是需要细心准备的…