Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 8a47618

Browse files
Merge branch 'seminars/add-materials'
2 parents 658aa46 + 119d2f9 commit 8a47618

File tree

26 files changed

+1179
-2
lines changed

26 files changed

+1179
-2
lines changed

‎ch16/graphical_user_interfaces.tex‎

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
% !TEX encoding = UTF8
2+
% !TEX spellcheck = ru_RU
3+
% !TEX root = ../seminars.tex
4+
5+
%%===============================================
6+
\chapter{Графические пользовательские интерфейсы}
7+
%%===============================================
8+
9+
%%====================================
10+
\section{Окно с кнопкой \texttt{Quit}}
11+
%%====================================
12+
Выполним упражнение~1 из~\textbookref{главы~16} учебника. Для~этого пронаследуем \code{My\_window} от~класса \code{Simple\_window} из~библиотеки \code{Graph\_lib}. Затем добавим по~аналогии с~кнопкой \code{Next} кнопку \code{Quit}, как показано на~рисунке~\ref{fig:mywindow}.
13+
14+
\begin{figure}[ht]
15+
{\centering
16+
\includegraphics[width=0.6\textwidth]{images/my_window.png}
17+
18+
}
19+
\caption{Простое окно \code{My\_window} с кнопкой \code{Quit}}
20+
\label{fig:mywindow}
21+
\end{figure}
22+
23+
Из~документации к~библиотеке \name{FLTK} известно, что если все окна становятся скрытыми, то цикл обработки событий прекращает работу. То есть для~реализации функции \code{quit()} можно воспользоваться методом \code{hide()}.
24+
25+
\cppfile[firstline=11, lastline=21]{projects/ch16/chessboard/board.h}
26+
27+
Конструктор нашего окна принимает те же аргументы, что и \code{Simple\_window}. Мы добавляем инициализацию кнопки \code{Quit}. Нужно сместить её вниз на~высоту кнопки \code{Next}, которую добавляет базовый класс, а также связать её с~функцией-обработчиком \code{cb\_quit()}:
28+
29+
\cppfile[firstline=7, lastline=12]{projects/ch16/chessboard/board.cpp}
30+
31+
\textbf{NB!} Обратим внимание на одну деталь, которую нам пришлось изменить в~изначальной версии кода библиотеки \code{Graph\_lib}:
32+
33+
\cppfile[firstline=8, lastline=14]{projects/lib/Graph_lib/GUI.cpp}
34+
35+
\noindent В~строке~12 мы передаём указатель на~текущий объект класса (\code{this}), то есть указатель на~кнопку (\code{Button}), а не~указатель на~окно, как это ожидается в~примерах обработчиков, показаных в~\textbookref{главе~16} учебника.
36+
37+
Отметим, что здесь мы можем передавать, вообще говоря, любые данные. Позже библиотека \name{FLTK} вернёт нам указатель на~эти данные в~функцию обратного вызова:
38+
39+
\cppfile[firstline=14, lastline=18]{projects/ch16/chessboard/board.cpp}
40+
41+
\noindent Параметр \code{widget} как раз и есть тот самый адрес кнопки, который мы передаём при~вызове функции \code{Button::attach()}, когда связываем кнопку с~окном:
42+
43+
\cppfile[firstline=11, lastline=11]{projects/ch16/chessboard/board.cpp}
44+
45+
Оказывается, во многих задачах удобнее иметь указатель на~наш виджет. А, так как он хранит адрес окна, с~которым связан, мы можем получить доступ к~нашему окну и, в~конце концов, вызвать метод \code{quit()}:
46+
47+
\cppfile[firstline=17, lastline=17]{projects/ch16/chessboard/board.cpp}
48+
49+
\noindent Здесь использован оператор \code{dynamic\_cast}, который выполняет приведение типа от~ссылки на~объект базового класса к~ссылке на~объект производного класса (\textenglish{down cast}). Такое преобразование в~общем случае небезопасно. Данный оператор выполняет проверку во~время выполнения программы и возбуждает исключение \code{std::bad\_cast} в~случае ошибки.
50+
51+
52+
53+
%%=======================
54+
\section{Шахматная доска}
55+
%%=======================
56+
Покажем, как можно создать клеточное поле и взаимодействовать с~ним на~примере упражнения~2 из~\textbookref{главы~16}. Эти идеи можно использовать для~создания практически любой игры с~клеточным полем: шашек, шахмат, сапёра, морского боя, пять в ряд и других.
57+
58+
Учитывая последующую доработку, код удобно сразу распределить между~несколькими файлами, как это предлагается в~таблице~\ref{tab:chessboard}.
59+
60+
\begin{table}[ht]
61+
{\centering\begin{tabular}{ll}
62+
\toprule
63+
\code{board.h} & класс \code{Chessboard} для~шахматной доски, а также \code{My\_window} \\
64+
\code{board.cpp} & \\[0.5em]
65+
66+
\code{cell.h} & класс \code{Cell} для~шахматной клетки \\
67+
\code{cell.cpp} & \\[0.5em]
68+
69+
\code{main.cpp} & \\
70+
\bottomrule
71+
\end{tabular}
72+
73+
}
74+
\medskip
75+
\caption{Распределение кода <<шахматной доски>> между файлами}
76+
\label{tab:chessboard}
77+
\end{table}
78+
79+
Размеры клеток и, соответственно, окна зафиксируем. Впоследствии, такое поведение можно изменить. Клетки представим квадратными кнопками и будем хранить их в~\code{Vector\_ref}. Метки строк и столбцов доски можно нарисовать при~помощи объектов \code{Marks}. Окно, согласно заданию, пронаследуем от~\code{My\_window} из~предыдущего упражнения.
80+
81+
\begin{figure}[ht]
82+
{\centering
83+
\includegraphics[width=0.5\textwidth]{images/chessboard.png}
84+
85+
}
86+
\caption{Окно с шахматной доской \(4\times 4\)}
87+
\label{fig:chessboard}
88+
\end{figure}
89+
90+
Итак, давайте разбираться по~порядку.
91+
92+
93+
94+
%%===========================
95+
\paragraph{Шахматная клетка.}
96+
%%===========================
97+
Прежде всего, создадим шахматную клетку "--- класс \code{Cell}. Клетки на~шахматной доске могут быть только белыми или чёрными. Это свойство мы выразим, используя перечисление \code{Cell::Type}. Размер клетки зафиксируем на~этапе компиляции. Тогда разумно сделать его константой класса (\code{static}), чтобы обращаться из~любой точки программы без~указания объекта \code{Cell::size}.
98+
99+
Нам придётся перекрыть метод \code{attach()}. Почему?
100+
101+
Также добавим пару методов \code{activate()}/\code{deactivate()} для~управления подсветкой активной клетки (клетка~\code{b2} на~рисунке~\ref{fig:chessboard}), то есть той клетки, которую выбрал мышкой пользователь.
102+
103+
В~заголовочном файле \code{cell.h} разместим определение класса:
104+
105+
\cppfile[firstline=8, lastline=32]{projects/ch16/chessboard/cell.h}
106+
107+
Реализация методов проста и не~требует дополнительных пояснений, кроме того, что \code{pw} "--- это защищённый член класса \code{Graph\_lib::Widget}, связанный с~реальным графическим виджетом из~\name{FLTK}.
108+
109+
Код разместим в~файле \code{cell.cpp}:
110+
111+
\cppfile[firstline=5, lastline=32]{projects/ch16/chessboard/cell.cpp}
112+
113+
114+
115+
%%==========================
116+
\paragraph{Шахматная доска.}
117+
%%==========================
118+
Основные моменты обозначены выше. Отметим здесь, что размер доски (\(N\times N\)) фиксируем на~этапе компиляции. Введём дополнительную константу \(N_{max}\), чтобы пользователь класса не~превышал определённый размер. Дело в~том, что позже мы будем добавлять подписи клеток и пока рассчитываем дизайн максимально на~8--9 клеток по~высоте. Дальше придётся что-то придумывать или каким-то образом выравнивать текстовые метки, состоящие из~двух цифр. (Это оставляем на~подумать.)
119+
120+
В~заголовочный файл \code{board.h} добавим определение класса:
121+
122+
\cppfile[firstline=23, lastline=51]{projects/ch16/chessboard/board.h}
123+
124+
Реализацию методов класса добавим в~файл \code{board.cpp}.
125+
126+
Конструктор задаёт размеры окна, создаёт клетки, располагая их в~соответствующих позициях, и рисует метки строк и столбцов доски. Зафиксируем размеры окна, чтобы пользователь не~смог его растягивать или сжимать. Сделать это позволяет функция \code{size\_range()} "--- метод окна \name{FLTK}.
127+
128+
\cppfile[firstline=48, lastline=67]{projects/ch16/chessboard/board.cpp}
129+
\cpp/ .../
130+
\cppfile[firstline=79, lastline=79]{projects/ch16/chessboard/board.cpp}
131+
132+
Цвет клетки (или тип) вычисляется на~основе номеров строки и столбца, определяющих её положение на~доске. Клетка в~левом нижнем углу имеет чёрный цвет. Обратим внимание, что клетки добавлялись в~линейный массив по~порядку в~соответствии с~направлением снизу вверх и слева направо, как принято в~шахматах.
133+
134+
\cppfile[firstline=20, lastline=26]{projects/ch16/chessboard/board.cpp}
135+
136+
137+
138+
%%=========================
139+
\paragraph{Подписи клеток.}
140+
%%=========================
141+
Метки \code{Marks} допускают всего лишь один символ, поэтому мы используем ограничение \code{N\_max} для~максимального размера доски. Если потребуется вывести двузначные номера клеток, придётся разработать новый класс или добавить метки при~помощи неименованных объектов \code{Text}.
142+
143+
А пока воспользуемся простой реализацией (добавляем код в конструктор):
144+
145+
\cppfile[firstline=67, lastline=78]{projects/ch16/chessboard/board.cpp}
146+
147+
\noindent и парой внешних вспомогательных функций:
148+
149+
\cppfile[firstline=28, lastline=46]{projects/ch16/chessboard/board.cpp}
150+
151+
\noindent Таким образом, получается вид, как на~рисунке~\ref{fig:chessboard}.
152+
153+
154+
155+
%%=========================================
156+
\paragraph{Взаимодействие с пользователем.}
157+
%%=========================================
158+
В~функцию-обработчик нажатия на~кнопку-клетку добавим простую логику самой шахматной доски. Можно:
159+
\begin{itemize}
160+
\item выделить любую клетку;
161+
\item переместить выделение, выбрав другую клетку;
162+
\item снять выделение, нажав на~выделенную клетку повторно.
163+
\end{itemize}
164+
165+
\noindent Реализация относительно проста, однако, здесь удобно использовать переменную-указатель \code{selected}. (Указатели будут обсуждаться в~\textbookref{главе~17} учебника.) Мы запоминаем адрес <<активной>> клетки в~переменной \code{selected}. Значение \code{nullptr} говорит, что <<активная>> клетка не~выбрана.
166+
167+
\cppfile[firstline=81, lastline=103]{projects/ch16/chessboard/board.cpp}
168+
169+
\noindent\textbf{NB!} Если в~отображении виджета что-то изменилось, то, скорее всего, потребуется перерисовать его вручную. Для~этого используйте \code{Fl::redraw()}.
170+
171+
Теперь настало время собрать и запустить программу целиком. Добавьте необходимые заголовки, функцию \code{main()}. Создайте окно \code{Chessboard} и запустите цикл обработки событий:
172+
173+
\cppfile[firstline=17, lastline=18]{projects/ch16/chessboard/main.cpp}
174+
175+
176+
177+
%%===============================
178+
\section{Добавление фигур. Шашки}
179+
%%===============================
180+
181+
\begin{figure}[ht]
182+
{\centering
183+
\includegraphics[width=0.5\textwidth]{images/checkers.png}
184+
185+
}
186+
\caption{Шахматная доска \(4\times 4\) с~шашками}
187+
\label{fig:checkers}
188+
\end{figure}
189+
190+
\todo{Описание будет добавлено позже.}
191+
192+
193+
194+
%%================
195+
\WhatToReadSection
196+
%%================
197+
\textcite{Stroustrup:2016:ru}: \textbf{глава~17}
198+
199+
200+
201+
%%===============
202+
\ExercisesSection
203+
%%===============
204+
\begin{exercise}
205+
\item Выполните упражнения из~\textbookref{главы~16} учебника.
206+
207+
\end{exercise}

‎images/checkers.png‎

10.7 KB
Loading[フレーム]

‎images/chessboard.png‎

10.1 KB
Loading[フレーム]

‎images/my_window.png‎

5.78 KB
Loading[フレーム]

‎projects/CMakeLists.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ add_subdirectory(ch13/hexagon_tile)
2525
add_subdirectory(ch14/logic_shapes)
2626

2727
add_subdirectory(ch15/least_squares)
28+
29+
add_subdirectory(ch16/chessboard)
30+
add_subdirectory(ch16/checkers)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
3+
set(TARGET "checkers")
4+
5+
set(HEADERS
6+
board.h
7+
cell.h
8+
checkers.h
9+
figure.h
10+
utils.h
11+
${LIB_DIR}/Graph_lib/fltk.h
12+
${LIB_DIR}/Graph_lib/Graph.h
13+
${LIB_DIR}/Graph_lib/GUI.h
14+
${LIB_DIR}/Graph_lib/Point.h
15+
${LIB_DIR}/Graph_lib/Simple_window.h
16+
${LIB_DIR}/Graph_lib/Window.h
17+
)
18+
set(SOURCES
19+
main.cpp
20+
board.cpp
21+
cell.cpp
22+
checkers.cpp
23+
figure.cpp
24+
${LIB_DIR}/Graph_lib/Graph.cpp
25+
${LIB_DIR}/Graph_lib/GUI.cpp
26+
${LIB_DIR}/Graph_lib/Window.cpp
27+
)
28+
29+
project(${TARGET} CXX)
30+
31+
set(FLTK_SKIP_FLUID True)
32+
set(FLTK_SKIP_FORMS True)
33+
34+
find_package(FLTK 1.3.8 EXACT REQUIRED)
35+
find_package(OpenGL REQUIRED)
36+
37+
include_directories(SYSTEM ${FLTK_INCLUDE_DIR})
38+
link_directories(${FLTK_INCLUDE_DIR}/../lib)
39+
40+
add_executable(${TARGET} ${HEADERS} ${SOURCES})
41+
42+
target_link_libraries(${TARGET} ${FLTK_LIBRARIES} ${OPENGL_LIBRARIES})
43+
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
44+
target_link_libraries(${TARGET} fltk_jpeg fltk_png fltk_z)
45+
endif()
46+
47+
install(TARGETS ${TARGET})

‎projects/ch16/checkers/Makefile‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
TARGET = checkers
2+
3+
LIB_DIR = ../../../lib
4+
5+
SOURCES = $(wildcard ${LIB_DIR}/Graph_lib/*.cpp) \
6+
board.cpp \
7+
cell.cpp \
8+
checkers.cpp \
9+
main.cpp
10+
11+
OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES))
12+
13+
14+
CXX ?= g++
15+
CXXFLAGS := -std=c++17 -pedantic -Wall -Wextra -I${LIB_DIR} \
16+
$(patsubst -I%,-isystem%,$(shell fltk-config --use-images --cxxflags))
17+
LDFLAGS := $(shell fltk-config --use-images --ldflags)
18+
19+
20+
.DEFAULT_GOAL := all
21+
.PHONY: all clean clean-all
22+
23+
24+
$(TARGET): $(OBJECTS)
25+
$(CXX) -o $@ $(OBJECTS) $(LDFLAGS)
26+
27+
%.o: %.cpp %.h
28+
$(CXX) -c $(CXXFLAGS) -o $@ $<
29+
30+
31+
all: $(TARGET)
32+
33+
34+
clean:
35+
rm -f $(OBJECTS)
36+
37+
clean-all: clean
38+
rm -f $(TARGET)
39+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /