воскресенье, 31 октября 2021 г.

Создаем GTK список (виджет)

 Приведу без комментариев код. 

// gtktable.c
#include <stdio.h>
#include <gtk-3.0/gtk/gtk.h>

GtkWidget* main_window;
GtkWidget* box1;
GtkWidget* scroll_window;

GtkWidget* list_widget;
GtkTreeStore* store_list;
enum{COL1, COL2, COL3, NCOL};
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
GtkTreeSelection* selection;
GtkTreeIter iter1;

gboolean
selection_func (GtkTreeSelection *selection,
                     GtkTreeModel     *model,
                     GtkTreePath      *path,
                     gboolean          path_currently_selected,
                     gpointer          userdata)
{
    g_print ("selection_func is called.\n");
    return TRUE;
}

void
create_gtk_table(){
    store_list = gtk_tree_store_new(NCOL, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
    list_widget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store_list));
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_widget));
    gtk_tree_selection_set_select_function(selection, selection_func, NULL, NULL);

    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list_widget), TRUE);
    gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(list_widget),GTK_TREE_VIEW_GRID_LINES_BOTH);
    gtk_container_add(GTK_CONTAINER(scroll_window), list_widget);

    renderer = gtk_cell_renderer_text_new();
    g_object_set(G_OBJECT(renderer), "background", "grey", NULL);
    g_object_set(G_OBJECT(renderer), "foreground", "black", NULL);

    column = gtk_tree_view_column_new_with_attributes("Title1",renderer, "text", COL1, NULL);
    gtk_tree_view_column_set_clickable(column, TRUE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(list_widget), column);

    column = gtk_tree_view_column_new_with_attributes("Title2",renderer, "text", COL2, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(list_widget), column);

    column = gtk_tree_view_column_new_with_attributes("Title3",renderer, "text", COL3, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(list_widget), column);

    g_object_unref(G_OBJECT(store_list));

    for(gint i = 0 ; i < 100; i++){
      gtk_tree_store_append(store_list, &iter1, NULL);
      gtk_tree_store_set(store_list, &iter1, COL1, i+1, COL2, "string ?", COL3, "the string!", -1);
    }
}

void
activate (GtkApplication *app,
          gpointer        user_data)
{
    main_window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (main_window), "The Table in The Scrolling");
    gtk_window_set_default_size (GTK_WINDOW (main_window), 400, 500);
    gtk_container_set_border_width (GTK_CONTAINER (main_window), 20);

    box1 = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
    gtk_box_set_homogeneous (GTK_BOX(box1), FALSE);
    gtk_container_add (GTK_CONTAINER (main_window), box1);

    scroll_window = gtk_scrolled_window_new(NULL, NULL);
    gtk_widget_set_size_request(scroll_window, 350, 450);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    gtk_box_pack_start(GTK_BOX (box1), scroll_window, FALSE, FALSE, 10);

    create_gtk_table();

    gtk_widget_show_all (main_window);
}

int main(int argc, char **argv) {
    GtkApplication *app;
    int status;
    app = gtk_application_new ("org.gtk.the_sin", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);
    return status;
}

Код, который ответственнен за создание списка находится в функции  create_gtk_table. После выполнения эта программа выводит на дисплей вот такое:

Если для компиляции ипользовать CMake, то необходимо создать файл CMakeLists.txt следующего содержания:

cmake_minimum_required (VERSION 3.18)

project (gtktable)
find_package (PkgConfig REQUIRED)
pkg_check_modules (GTK3 REQUIRED gtk+-3.0)
include_directories (${GTK3_INCLUDE_DIRS})
link_directories (${GTK3_LIBRARY_DIRS})
add_executable(gtktable gtktable.c)
add_definitions (${GTK3_CFLAGS_OTHER})
target_link_libraries (gtktable ${GTK3_LIBRARIES})

Компилировалось и выполнялось на Debian Bullseye. Собственно, это все, что хотел сказать.

четверг, 14 октября 2021 г.

Простейшее испльзования систем CMake и Meson на примере gtk+ приложения

 Напишем простое gtk приложение, которое считает sin от вводимой величины. 

//   calc_sin.c
#include <gtk/gtk.h>
#include <math.h>

GtkWidget* main_window;
GtkWidget* box1;

GtkWidget* edit1;
GtkWidget* button1;
GtkWidget* label1;
GtkWidget* button_exit;

void
calc_sin(){
    double result = sin(atof(gtk_entry_get_text(GTK_ENTRY(edit1))));
    gchar *display = g_strdup_printf("%f", result);   
    gtk_label_set_text (GTK_LABEL(label1), display);
    g_free(display); 
}

void
activate (GtkApplication *app,
          gpointer        user_data)
{
      main_window = gtk_application_window_new (app);
      gtk_window_set_title (GTK_WINDOW (main_window), "The sin");
      gtk_window_set_default_size (GTK_WINDOW (main_window), 200, 400);
      gtk_container_set_border_width (GTK_CONTAINER (main_window), 20);

      box1 = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
      gtk_box_set_homogeneous (GTK_BOX(box1), FALSE);
      gtk_container_add (GTK_CONTAINER (main_window), box1);

      edit1=gtk_entry_new();
      gtk_box_pack_start(GTK_BOX (box1), edit1, FALSE, FALSE, 10);

      button1 = gtk_button_new_with_label("Calculate!");
      g_signal_connect (button1, "clicked", G_CALLBACK (calc_sin), NULL);
      gtk_box_pack_start(GTK_BOX (box1), button1, FALSE, FALSE, 10);

      label1 = gtk_label_new("result");
      gtk_box_pack_start(GTK_BOX (box1), label1, FALSE, FALSE, 10);

      button_exit = gtk_button_new_with_label ("Exit");
      gtk_box_pack_start(GTK_BOX (box1), button_exit, FALSE, FALSE, 10);
      g_signal_connect_swapped (button_exit, "clicked", G_CALLBACK
(gtk_widget_destroy), main_window);

      gtk_widget_show_all (main_window);
}

int
main (int    argc,
      char **argv)
{
    GtkApplication *app;
    int status;
      app = gtk_application_new ("org.gtk.the_sin", G_APPLICATION_FLAGS_NONE);
      g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
      status = g_application_run (G_APPLICATION (app), argc, argv);
      g_object_unref (app);
      return status;
}

 А теперь откомпилируем-отлинкуем этот код разными методами и выполним его. Предполагается, что cmake и meson уже установлены.

Сначала сделаем это "традиционно":

#gcc calc_sin.c `pkg-config  --cflags --libs  gtk+-3.0` -lm -o calc_sin_bin

 После выполнения полученного бинарника мы должны увидеть следующее:

В едитбокс вводим величину в радианах, нажимаем на кнопочку "Calculate!" и вместо слова "result" увидим величину синуса введенного значения.

Теперь соберем нашу программу с помощью CMake.
Создадим файл CMakeLists.txt следующего содержания:

cmake_minimum_required (VERSION 2.6)

project (calc_sin_prj)
find_package (PkgConfig REQUIRED)
pkg_check_modules (GTK3 REQUIRED gtk+-3.0)
include_directories (${GTK3_INCLUDE_DIRS})
link_directories (${GTK3_LIBRARY_DIRS})
add_executable(calc_sin_bin calc_sin.c)
add_definitions (${GTK3_CFLAGS_OTHER})
target_link_libraries (calc_sin_bin ${GTK3_LIBRARIES} m)

Этот файл в данном случае должен быть в директории с нашим исходником. Далее делаем такую последовательность команд:

#mkdir cmakebuilddir
#cd cmakebuilddir
#cmake ..
#cmake --build .

Теперь соберем нашу программу с помощью meson.
Создадим файл meson.build следующего содержания:

project('calc_sin_meson', 'c')
cc = meson.get_compiler('c')
m_dep = cc.find_library('m', required:false)
deps = [dependency('gtk+-3.0'), m_dep]
executable('calc_sin_bin', 'calc_sin.c', dependencies : deps)

 Делаем такую последовательность команд:

#meson setup mesonbuilddir
#cd mesonbuilddir
#meson compile

Во всех трех случаях получаем бинарник  calc_sin_bin с одинаковою функциональностью. Обратите внимание, что этот бинарник создается в случае meson в директории mesonbuilddir , а в случае CMake в директории  cmakebuilddir. Получается что дерево исходников находится отдельно, а все рабочие файлы каждой системной сборки (мезон и симэйк) находятся только в своей поддиректории соответственно. Аккуратно и логично.

суббота, 9 октября 2021 г.

Навигация по документу в vim

    Заезженная тема, но достойная отдельного рассмотрения. Рассмотрим команды перемещения по открытому документу в редакторе vim в нормальном режиме (есть еще режим редактирования, визуальный режим и режим ввода команд после двоеточия). Команды интуитивно сгруппированы в отдельные таблицы и параграфы.
 

аналогия "обычных" клавиш "стрелка вверх" и "стрелка вниз" курсор относительно текста не двигается, текст смещается на одну строку курсор относительно текста не двигается, текст смещается на полэкрана текст смещается на полный экран
вверх Ctrl-p
Ctrl-y Ctrl-u
Ctrl-b
вниз Ctrl-n Ctrl-e Ctrl-d
Ctrl-f

 Ctrl-y и Ctrl-e полезно запомнить, так как эти комбинации имеют полезную функциональность в режиме редактирования: копируют букву в текущей позиции в верхнюю или нижнюю строку соответственно.

текст не перемещаетсяотносительно окна, двигается только курсор
 текст перемещается относительно окна, курсор привязан к позиции в тексте
влево h

вниз j, на дно экрана L
вниз экрана zb
вверх k, вверх экрана H
вверх экрана zt
вправо l

в середину экрана M
в середину экрана zz z.

 

"Глобальные" перемещения:

gg идем в начало;
G
идем в самый конец;
50%
идем в середину всего документа (50% же);
:50
идем на 50-ю строку;

  ctrl-o, ctrl-i перемещаемся вперед-назад по сохраненной истории координат курсора. Не все положения курсора сохраняються в этой истории. Сохраняються координаты, в которые мы попадали в результате перемещения, которое классифицируется как "прыжок". Список сохраненных прыжков можно увидеть командой :jumps. Очень полезная фишка. Вам не нужно бегать по файловому менеджеру в поисках файла, который вы уже когда-то редактировали. Просто войдите в vim в нормальном режиме и понажимайте  ctrl-o, ctrl-i.

Строчные перемещения:

0
переходим в самое начало строки;
^
переходим в на первый непробельный символ строки;
$
переходим в самый конец строки;
-
переходим на начало строки ввех;
+
переходим на начало строки вниз;

Пословные перемещения:

w
переход к началу следующего слова;
W
переход к началу следущего слова;
e
переход к следующему концу слова;
E
переход к следующему концу слова;
b
переход к началу предыдущего слова;
B
переход к началу предыдущего слова;
tx, fx
переход к следующей букве х;
Tх, Fх
переход к предыдущей букве х;
;
повторить предыдущий tfTF-переход;

Ну а про эти команды мне даже как-то неудобно и говорить:
mx установить закладку х;
'x перейти к закладке х;
''
переходим к предыдущему месту, откуда делали последний переход к закладке;
:marks смотрим какие у нас закладки;

Учите клавиатуру. Воздастся. ))