使用GTK创建简易计算器

news2024/9/21 12:41:40

使用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.coloradmin.cn/o/729458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Django之中间件

一、中间件介绍 官方的说法&#xff1a;中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统&#xff0c;用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。 但是由于其影响的是全局&#xff0c;所以需要…

Vivado2020.1 vitis使用:创建hello world项目

前言 之前网上的教程都是基于Vivado2018的&#xff0c;后来接手一个未完工的项目&#xff0c;是使用Vivado2020.1创建的&#xff0c;非常尴尬&#xff0c;只能打开&#xff0c;不能编辑。千辛万苦把2020.1安装好&#xff0c;当然此时不仅仅是Vivado2020.1了&#xff0c;而是vi…

gitee注册以及使用的简单教程

目录 1.gitee是什么&#xff1f; 2. gitee怎么注册? 3.gitee创建仓库 4.gitee怎么提交代码? 5. git的三板斧 1.gitee是什么&#xff1f; 基于Git的代码托管和研发协作平台上面可以托管个人或者公司的代码和开源项目。国外有github&#xff0c;国内有giteegithub经常出现…

(vue)el-table表头、内容居中

(vue)el-table表头、内容居中 效果&#xff1a; 表头、内容都居中 <el-table:data"gridData":header-cell-style"{text-align:center}":cell-style"{text-align:center}" >单个表格的内容居中&#xff1a; el-table-column上加上align‘c…

【C++】-string类的模拟实现

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法\&#x1f384; 如 果 你…

抖音seo源码开发部署技术分享(一)

目录 开发概述 自研开发者介绍 开发要求&#xff1a; 技术开发布局 源码部署及搭建分享 部署环境搭建 代码开发示例 请求样例 响应样例&#xff1a; 代码展示样例 开发概述 开放平台基于开发者诉求和相关平台规则&#xff0c;提供了两种开放模式&#xff1a;能力开放…

前端Vue自定义顶部导航栏navBar 导航栏搜索框searchBar 导航栏右侧菜单按钮button

前端Vue自定义顶部导航栏navBar 导航栏搜索框searchBar 导航栏右侧菜单按钮button&#xff0c;下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13342 效果图如下: # cc-headerSearch #### 使用方法 使用方法 <!-- icon: 右侧菜单…

OpenCV在一个图像上画一个空心绿色的圆和一个实心红色的圆

/*** void cvCircle( CvArr* img, CvPoint center, int radius, CvScalar color, int thickness=1, int line_type=8, int shift=0 );* Opencv画点 其实画的是小圆圈* img:图像。* center:圆心坐标。* radius:圆形的半径。* color:线条的颜色。* thickness:如果是正数,表…

vue筛选框封装

点击对默认查询条件之外的条件进行 增加或删除 在使用的组件或标签加入:filtrateList"filtrateList"传入条件查询数组 当前demo写在xk-page中,就以xk-page组件为例 <xk-upage :filtrateList"filtrateList" :queryArr"queryArr"></xk-…

java项目之母婴用品网站(ssm+mysql+jsp)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的母婴用品网站。 开发环境&#xff1a; 后端&#xff1a; 开发语言&#xff1a;Java 框架&#xff1a;ssm&#xff0c;mybatis JDK版本&am…

EasyCVR非按需定时快照功能的设计与实现方法

EasyCVR基于云边端一体化架构&#xff0c;部署轻快、功能灵活&#xff0c;平台可支持多协议、多类型设备接入&#xff0c;包括&#xff1a;国标GB28181、RTMP、RTSP/Onvif、海康Ehome、海康SDK、大华SDK、宇视SDK等。在视频能力上&#xff0c;可实现视频直播、录像、回放、检索…

Gradio库中的Model3D模块:实时上传和展示3D模型

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

F#奇妙游(9):来一点点画图

F#绘图 如果Python没有Matplotlib&#xff0c;R没有ggplot&#xff0c;Matplab没有plot函数……就像阿珍爱上了阿强&#xff0c;能画图的脚本才是好脚本。当然&#xff0c;F#有画图包&#xff0c;但是F#在数据处理中的地位就像下面&#xff0c;这也是我们没办法的。 ScottPl…

分析openGauss包内集合类型的实现方法

前言 Oracle中集合类型覆盖了Postgresql数组的功能&#xff0c;在Oracle用户中时非常常用的。 尤其是包内定义的集合类型&#xff0c;在SPEC定义后即可直接使用&#xff0c;scope也只在包在生效&#xff0c;使用非常灵活。 开源PG因为有数组没有实现这部分语法&#xff0c;下…

超级广场效果的耳机放大器音响电路设计

用头戴式耳机&#xff0c;尤其是小型耳机听音乐&#xff0c;总感到音乐味不够足&#xff0c;在低频段的效果更差。因此用本机增强耳机的低频特性&#xff0c;并采用立体声反相合成的办法&#xff0c;加上内藏简易矩阵环绕声电路&#xff0c;能获得强劲的低音和在较宽的范围内展…

Windows mingw64 最简易 安装配置

其实挺简单一件事 很多教程都搞复杂了 自己写一个 只需要两步 1. 下载压缩包并解压 2. 配置环境变量 (1). GitHub 下载地址 Releases niXman/mingw-builds-binaries GitHub 如果GitHub下载太慢可以来这里加速 或者用地址2 GitHub Proxy 代理加速 (ghproxy.com) (2). 下…

-Xss1m / -XX:ThreadStackSize=512k

-Xss / -XX:ThreadStackSize指定线程最大栈空间jdk1.4里默认的栈大小是256KBjdk1.5里默认的栈大小为1M配置JVM启动参数&#xff1a;-Xmx20m -Xms20m -Xss1m -XX:PrintCommandLineFlags-XX:ConcGCThreads3 -XX:G1ConcRefinementThreads13 -XX:GCDrainStackTargetSize64 -XX:Init…

学习grpc

Grpc简介&#xff1a; gRPC是一个高性能、通用的开源RPC框架&#xff0c;其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计&#xff0c;基于ProtoBuf(Protocol Buffers)序列化协议开发&#xff0c;且支持众多开发语言&#xff0c;能够基于语言自动生成客户端和服务端…

软负载Nginx详细配置及使用案例

Nginx使用与配置 什么是nginx Nginx 是一个高性能的HTTP和反向代理服务&#xff0c;也是一个IMAP/POP3/SMTP服务。 处理响应请求很快高并发连接低的内存消耗具有很高的可靠性高扩展性热部署 master 管理进程与 worker 工作进程的分离设计&#xff0c;使得 Nginx 具有热部署的…