第133页的gtk+编程例子——编写计算器应用

第133页的gtk+编程例子——编写计算器应用

以下gtk+编程例子是来自书籍《实用技术:开发Linux应用——用GTK+和GDK开发Linux图形用户界面应用》第133页的内容——编写计算器应用
例子程序是在gtk2.0编译的,已经修改许多地方才能在gtk3.0编译通过,尝试在gtk4.0编译,但是报错太多无法修改而成
书中的例子程序源代码可以到以下地址下载,也有有其它章节的例子程序
《Examples from “Developing Linux Applications with GTK+ and GDK” by Eric Harlow》
https://gitlab.com/steshaw/gtk-examples

《gtk计算器》是在gtk2.0编译的有效果图
https://rtoax.blog.csdn.net/article/details/88089639

在openSUSE-Leap-15.5-DVD-x86_64的gnome41.8桌面环境下编译的,其中pkg-config --cflags gtk±3.0两边有反引号括起来的,pkg-config --libs gtk±3.0也是一样两边有反引号括起来的,因为报错undefined reference to symbol 'sqrt@@GLIBC_2.2.5,所以一定要加上-lm选项,> mistake.text 2>&1 ; gedit mistake.text表示将编译过程的错误信息保存到文本文件mistake.text,编译完成后马上打开查看

ruhong@localhost:~/gtk/calculator> gcc -g -Wall -lm pkg-config --cflags gtk+-3.0 -o page133 page133.c pkg-config --libs gtk+-3.0 > mistake.text 2>&1 ; gedit mistake.text
ruhong@localhost:~/gtk/calculator> ./page133

/*
 * Calculator.c
 *
 * Example showing different widgets in use.
 *
*/

#include 
#include 
#include 
#include 
#include 
#include 

static float num1 = 0;
static char lastChar = (char) 0;
static char prevCmd = (char) 0;
#define BUF_SIZE 88

/*
 * --- data structure to keep track of the calculator buttons.
*/
typedef struct {

    char      *szLabel;    /* --- Label display on button --- */
    int       row;         /* --- Row to place the button --- */
    int       col;         /* --- Column to place the button --- */
    GtkWidget *widget;     /* --- Handle to the button --- */

} typCalculatorButton;


/* 
 * --- This is the button list.  Each button is documented here so 
 *     we can access it.
*/
typCalculatorButton buttonList [] = {
    {"C",   1, 0, NULL},      /* --- Clear --- */
    {"CE",  1, 1, NULL},      /* --- Clear --- */
    {"/",   1, 3, NULL},      /* --- Division --- */

    {"7",   2, 0, NULL},      /* --- Digit --- */
    {"8",   2, 1, NULL},      /* --- Digit --- */
    {"9",   2, 2, NULL},      /* --- Digit --- */
    {"*",   2, 3, NULL},      /* --- Multiplication --- */
    {"%",   2, 4, NULL},      /* --- Percent --- */

    {"4",   3, 0, NULL},      /* --- Digit --- */
    {"5",   3, 1, NULL},      /* --- Digit --- */
    {"6",   3, 2, NULL},      /* --- Digit --- */
    {"-",   3, 3, NULL},      /* --- Subtraction --- */
    {"1/x", 3, 4, NULL},      /* --- 1/x --- */

    {"1",   4, 0, NULL},      /* --- Digit --- */
    {"2",   4, 1, NULL},      /* --- Digit --- */
    {"3",   4, 2, NULL},      /* --- Digit --- */
    {"+",   4, 3, NULL},      /* --- Addition --- */
    {"sqrt",4, 4, NULL},      /* --- Square root --- */

    {"+/-", 5, 0, NULL},      /* --- Negate value --- */
    {"0",   5, 1, NULL},      /* --- zero --- */
    {".",   5, 2, NULL},      /* --- Decimal --- */
    {"=",   5, 3, NULL},      /* --- Equals/total --- */
    {"x^2", 5, 4, NULL},      /* --- Squared --- */
};

/*
 * --- Number of buttons in the data structure.  
*/
int nButtons = sizeof (buttonList) / 
               sizeof (typCalculatorButton);

/* --- This is the LCD panel - the results --- */
GtkWidget *label;


/*
 * CloseAppWindow
 *
 * The window is closing down, end the gtk loop
*/
gint CloseAppWindow (GtkWidget *widget, gpointer data)
{
    gtk_main_quit ();

    return (FALSE);
}


/*
 * TrimTrailingZeros
 *
 * Get rid of trailing zeros 
 * Takes the string and removes the trailing zeros.
*/
void TrimTrailingZeros (char *szDigits)
{
    int nIndex;
    int bDecimal = FALSE;
    int nPos = -1;

    /* --- Loop through the string. --- */
    for (nIndex = 0; nIndex < strlen (szDigits); nIndex++) {

        /* --- Is this a decimal? --- */
        if (szDigits[nIndex] == '.') {
             bDecimal = TRUE;
        }

        /* --- If we're on the right side of the decimal... --- */
        if (bDecimal) {

            /* --- A zero?  Hmm... from this point on? --- */
            if (szDigits[nIndex] == '0') {

               /* --- If we don't have a point yet... --- */
               if (nPos < 0) {
 
                   /* --- Save this as a point. --- */
                   nPos = nIndex;
               }
            } else {

               /* --- Clear it.  Bad point. --- */
               nPos = -1;
            }
        }
    }

    /* --- Truncate the field. --- */
    if (nPos > 0) {
        szDigits[nPos] = (char) 0;
    }
}


/*
 * TrimLeadingZeros
 *
 * Trim the leading zeros.
 * 
 * Converts numbers like "0000012" to "12"
*/
void TrimLeadingZeros (char *szDigits)
{
    int nPos;

    if (szDigits == NULL) return;

    /* --- While we have a combination a digit in front --- */
    for (nPos = 0; (szDigits[nPos] && szDigits[nPos] == '0'); nPos++) {

        /* --- If the digit is a zero and next char is a digit --- */
        if (isdigit (szDigits[nPos+1])) {

            /* --- Blank the field. --- */  
            szDigits[nPos] = ' ';
        } 
    }
}


/*
 * Command
 *
 * Returns true if the character is a two digit command.
*/
int Command (char ch)
{
    switch (ch) {
        case '+':
        case '-':
        case '/':
        case '*':
        case '=':
            return (TRUE);
    }
    return (FALSE);
}

/*
 * FloatingPointChar
 *
 * Returns true if the character is any of [0123456789.]
*/
int FloatingPointChar (char ch)
{

    return (isdigit (ch) || ch == '.');
}


/*
 * key_press
 *
 * Handle the button "key_press" event.
 *
 * Function looks for the keystroke in the calculator 
 * data structure and (if a match is found) presses the 
 * button that matches the keystroke for the user.  It
 * keeps our code small since we only have to handle the
 * button_clicked events.
*/
void key_press (GtkWidget *widget, 
                GdkEventKey *event, 
                gpointer data)
{
    int nIndex;

    /* --- Search through the buttons --- */
    for (nIndex = 0; nIndex < nButtons; nIndex++) {

        /* --- If the keystroke is the first character of a button AND --- */
        /* --- the button label length is one. --- */      
        if (event->keyval == buttonList[nIndex].szLabel[0] && 
            buttonList[nIndex].szLabel[1] == (char) 0) {

            /* --- Set focus to that button --- */
            gtk_widget_grab_focus (buttonList[nIndex].widget);

            /* --- Make like the button was clicked to do processing. --- */
            gtk_button_clicked (GTK_BUTTON (buttonList[nIndex].widget));
            return;
        }
    }
}


/*
 * HandleDigit
 *
 * Digit button was pressed, deal with it.  How it
 * is dealt with depends on the situation.
*/
void HandleDigit (char *str, char ch)
{
    char buffer[BUF_SIZE];
    int  len;

    /* --- And they just did a command --- */
    if (Command (lastChar)) {

        /* --- Clear the digit field --- */
        gtk_label_set_text (GTK_LABEL (label), "");

        /* --- If they did a computation --- */
        if (lastChar == '=') {

            /* --- Clear out the command --- */
            lastChar = (char) 0;
            prevCmd = (char) 0;
        }
    }

    /* --- Get the buffer in the led --- */
    strcpy (buffer, gtk_label_get_text (GTK_LABEL (label)));

    /* --- Add the new character on it. --- */
    len = strlen (buffer);
    buffer[len] = (gchar) ch;
    buffer[len+1] = (gchar) 0;
   
    /* --- Trim leading zeros. --- */
    TrimLeadingZeros (buffer);

    /* --- Add digit to field. --- */
    gtk_label_set_text (GTK_LABEL (label), (char *) buffer);
}


/*
 * MaybeUnary
 *
 * str
 * 
 * Check to see if the user hit a unary operator button - 
 * like %, sqrt, 1/x, etc that should be dealt with NOW
 * not later.
*/
void MaybeUnaryOperation (char *str)
{
    char buffer[BUF_SIZE];
    float num2;

    /* --- Get number in the field. --- */
    num2 = atof (gtk_label_get_text (GTK_LABEL (label)));

    /* --- Percentage? --- */
    if (strcmp (str, "%") == 0) {
        num2 = num2 / 100;

    /* --- Trying for 1/x? --- */
    } else if (strcmp (str, "1/x") == 0) {

        /* --- Can't divide by zero. --- */
        if (num2 == 0) {
            /*Error (); */
            return;
        }
        num2 = 1 / num2;

    /* --- Calculate sqrt --- */
    } else if (strcmp (str, "sqrt") == 0) {
        num2 = sqrt ((double) num2);

    /* --- Calculate square --- */
    } else if (strcmp (str, "x^2") == 0) {
        num2 = num2 * num2;
    }

    /* --- Put the number back. --- */
    sprintf (buffer, "%f", (float) num2);
    TrimTrailingZeros (buffer);
    TrimLeadingZeros (buffer);
    gtk_label_set_text (GTK_LABEL (label), buffer);
}


void HandleBinaryOperation ()
{
    char buffer[BUF_SIZE];
    float num2;

    /* --- Get number in the field. --- */
    num2 = atof (gtk_label_get_text (GTK_LABEL (label)));

    /* --- Calculate based on previous command. --- */
    switch (prevCmd) {
        case '+':
            num1 = num1 + num2;               
            break;

        case '-':
            num1 = num1 - num2;               
            break;

        case '*':
            num1 = num1 * num2;               
            break;

        case '/':
            num1 = num1 / num2;               
            break;

        case '=':
            num1 = num2;
            break;

        default:
            num1 = num2;
            break;
    }

    /* --- Put the number back. --- */
    sprintf (buffer, "%f", (float) num1);
    TrimTrailingZeros (buffer);
    TrimLeadingZeros (buffer);
    gtk_label_set_text (GTK_LABEL (label), buffer);
}


/*
 * button_clicked
 *
 * widget - button pressed.
 * data - button label.
 *
 * Button was pressed, handle it.
*/
void button_clicked (GtkWidget *widget, gpointer data)
{
    char ch = *((char *) data);
    char *str;
    
    /* --- Get the button label --- */
    str = (char *) data;

    /* --- Entering a number... --- */
    if (FloatingPointChar (ch) && strlen (str) == 1) {

        HandleDigit (str, ch);

    } else {

        /* --- Clear? --- */
        if (strcmp (str, "CE") == 0) {
            gtk_label_set_text (GTK_LABEL (label), "0");
            return;

        /* --- BIG clear? --- */
        } else if (strcmp (str, "C") == 0) {
            prevCmd = (char) 0;
            lastChar = (char) 0;
            gtk_label_set_text (GTK_LABEL (label), "0");
            return;

        } else {

            /* --- Maybe it's a unary operator? --- */
            MaybeUnaryOperation (str);
        }

        /* --- See if there's a binary operation to do --- */
        HandleBinaryOperation ();

        prevCmd = ch;
    }
    lastChar = ch;
}

/*
 * CreateButton
 *
 * Create a button, assign event handlers, and attach the button to the
 * grid in the proper place.
*/
GtkWidget *CreateButton (GtkWidget *grid, char *szLabel, int row, int column)
{
    GtkWidget *button;

    /* --- Create the button --- */
    button = gtk_button_new_with_label (szLabel);

    /* --- We care if the button is clicked --- */
    g_signal_connect (G_OBJECT (button), "clicked",
                        G_CALLBACK (button_clicked), szLabel);

    /* --- Put the button in the grid in the right place. --- */
    	 gtk_grid_attach (GTK_GRID (grid), button, column, row, 1, 1);
    	 
    /* --- Make the button visible --- */
    gtk_widget_show (button);

    /* --- return the button. --- */
    return (button);
}



/*
 * CreateCalculatorButtons
 *
 * Create the buttons on the calculator from the grid we defined at the
 * beginning of this program.  The button pointers (handles) are stored
 * back in the grid so they can be referenced later.
*/
void CreateCalculatorButtons (GtkWidget *grid)
{
    int nIndex;

    /* --- Run through the list of buttons. --- */
    for (nIndex = 0; nIndex < nButtons; nIndex++) {

        /* --- Create a button --- */
        buttonList[nIndex].widget = 
                CreateButton (grid, 
                              buttonList[nIndex].szLabel, 
                              buttonList[nIndex].row, 
                              buttonList[nIndex].col);
    }
}

/*
 * main
 *
 * Program begins here
*/
int main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *grid;

    /* --- GTK initialization --- */
    gtk_init (&argc, &argv);

    /* --- Create the calculator window --- */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    /* --- Give the window a title. --- */
    gtk_window_set_title (GTK_WINDOW (window), "Calculator");

    /* --- Set the window size. --- */
    gtk_widget_set_size_request (window, 200, 200);

    /* --- We care if a key is pressed --- */
    g_signal_connect (G_OBJECT (window), "key_press_event",
                        G_CALLBACK (key_press), NULL);

    /* --- You should always remember to connect the delete event
     *     to the main window. --- */
    g_signal_connect (G_OBJECT (window), "delete_event",
                        G_CALLBACK (CloseAppWindow), NULL);

    /* --- Create a 5x5 grid for the items in the calculator. --- */
    grid = gtk_grid_new (); 
	 gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE);
	 gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);

    /* --- Create the calculator buttons. --- */
    CreateCalculatorButtons (grid);

    /* --- Create the calculator LED --- */
    label = gtk_label_new ("0");
	 gtk_label_set_xalign (GTK_LABEL (label), 1);
	 /*--- 水平方向X居右,垂直方面Y居中 ---*/
	 gtk_label_set_yalign (GTK_LABEL (label), 0.5);

    /* --- Add label to the grid --- */
    gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 4, 1);
    
    gtk_widget_show (label);
  
    /* --- Make them visible --- */
    gtk_container_add (GTK_CONTAINER (window), grid);
    gtk_widget_show (grid);
    gtk_widget_show (window);

    /* --- Grab focus for the keystrokes --- */
    //gtk_widget_grab_focus (buttonList[0].widget);

    gtk_main ();
    return (0);
}

效果图如下
第133页的gtk+编程例子——编写计算器应用_第1张图片第133页的gtk+编程例子——编写计算器应用_第2张图片

参考文章:
《GTK的计算器》
https://blog.csdn.net/zhouzhouzf/article/details/17097999

《编译错误“ undefined reference to ‘sqrt‘ ”解决方法小结》
https://blog.csdn.net/wangqingchuan92/article/details/115261070

你可能感兴趣的:(gtk+,linux,opensuse,gtk)