Основы интерфейса немедленного режима
Поймите, как egui выполняет отрисовку.
«Основы интерфейса немедленного режима» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 4 уроков всего.
Что означает непосредственный режим
egui — это библиотека GUI с непосредственным режимом. В отличие от наборов инструментов с сохранением состояния (GTK, Qt), где Вы создаёте постоянное дерево виджетов, egui заново строит весь интерфейс с нуля в каждом кадре.
Ваш код выполняется сверху вниз в каждом кадре, создавая виджеты вызовами функций. Сохранённого графа узлов нет — интерфейс является лишь побочным эффектом выполнения Вашего замыкания.
Цикл обновления
Приложение egui реализует eframe::App, метод update которого вызывается один раз за кадр, обычно 60 раз в секунду.
Внутри update Вы описываете, как интерфейс должен выглядеть прямо сейчас, исходя из текущего состояния. egui ничего не сравнивает — он просто заново создаёт всё содержимое.
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _f: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.label("Hello, egui!");
});
}
}Контекст и Ui
Два основных типа встречаются повсюду. egui::Context — это общий, недорого клонируемый дескриптор всего состояния интерфейса в текущем кадре.
Ui — это область, в которую Вы добавляете виджеты. Панели и окна передают Вам &mut Ui внутри своего замыкания; в нём Вы вызываете методы вроде ui.label или ui.button.
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Dashboard");
ui.separator();
ui.label("Widgets go here.");
});Виджеты возвращают ответы
Поскольку интерфейс перестраивается в каждом кадре, Вы не можете регистрировать обратные вызовы так, как это делают наборы инструментов с сохранением состояния. Вместо этого каждый вызов виджета сразу возвращает объект Response.
Вы проверяете Response в том же кадре: .clicked(), .hovered(), .changed(). Благодаря этому обработка событий выполняется непосредственно рядом с описанием размещения.
if ui.button("Save").clicked() {
self.save();
}
let resp = ui.text_edit_singleline(&mut self.name);
if resp.changed() {
self.dirty = true;
}Состояние хранится в Вашем коде
egui почти не хранит данные приложения. Значения виджетов находятся в self структуры App, а в виджеты передаются изменяемые ссылки.
Текстовое поле принимает &mut String, а флажок — &mut bool. egui записывает изменения обратно по ссылке, поэтому Ваша структура является единственным источником истины.
struct MyApp {
name: String,
enabled: bool,
}
// inside update:
ui.text_edit_singleline(&mut self.name);
ui.checkbox(&mut self.enabled, "Enabled");Настройка eframe
eframe — официальный фреймворк, размещающий egui на компьютере и в веб-среде. Он управляет нативным окном, поверхностью GPU и циклом событий.
Запуск выполняется с помощью eframe::run_native: передайте заголовок окна, нативные параметры и замыкание, создающее Ваш App.
fn main() -> eframe::Result<()> {
let opts = eframe::NativeOptions::default();
eframe::run_native(
"My App",
opts,
Box::new(|_cc| Ok(Box::new(MyApp::default()))),
)
}Перерисовка по событию ввода
По умолчанию egui перерисовывает интерфейс только при каком-либо событии: перемещении мыши, нажатии клавиши или явном запросе. Это экономит CPU в бездействующих приложениях.
Для анимаций или актуальных данных вызовите ctx.request_repaint(), чтобы запланировать ещё один кадр, либо request_repaint_after с интервалом для обновлений по времени.
ui.label(format!("Tick: {}", self.counter));
self.counter += 1;
ctx.request_repaint(); // keep animatingПочти никаких сохраняемых идентификаторов виджетов
egui выводит идентификатор виджета из порядка и положения вызовов, автоматически создавая Id. Это важно для таких элементов, как сворачиваемые заголовки, которым нужна стабильная память между кадрами.
Если два виджета могут столкнуться — например, кнопки с одинаковой подписью в цикле, — оберните их в ui.push_id(i, |ui| ...), чтобы различать их.
for (i, item) in self.items.iter().enumerate() {
ui.push_id(i, |ui| {
if ui.button(&item.label).clicked() {
println!("clicked {}", i);
}
});
}Компромиссы непосредственного режима
Непосредственный режим делает код интерфейса естественно управляемым данными: if показывает или скрывает раздел, а for отображает список. Не требуется вручную добавлять или удалять узлы, и модель с представлением не рассинхронизируются.
Цена этого подхода в том, что весь интерфейс выполняется заново в каждом кадре, поэтому не усложняйте работу на кадр и избегайте тяжёлых вычислений внутри update.
if self.logged_in {
ui.label(format!("Welcome, {}", self.user));
} else {
ui.label("Please sign in.");
}Минимальное полноценное приложение
Соберём всё вместе: структура хранит состояние, update отображает его, а main запускает окно. Это весь каркас любой настольной программы на egui.
Всё остальное — размещение, оформление, упаковка — строится на этом цикле.
#[derive(Default)]
struct App { count: i32 }
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
if ui.button("+1").clicked() { self.count += 1; }
ui.label(format!("Count: {}", self.count));
});
}
}Почему Rust отлично подходит для egui
Дизайн egui использует сильные стороны Rust. Передача &mut self в один вызов update означает, что проверка заимствований гарантирует отсутствие гонок данных между виджетами и Вашей моделью.
Нет обратных вызовов, захватывающих общее изменяемое состояние, и нет графов Rc<RefCell>, навязанных деревом с сохранением состояния, — только обычное владение и один изменяемый проход за кадр.
Быстрая проверка
Проверьте, насколько хорошо Вы поняли непосредственный режим.
Итоги
egui работает в непосредственном режиме: весь интерфейс перестраивается в каждом кадре внутри eframe::App::update. Виджеты являются вызовами функций, сразу возвращающими объект Response, который Вы проверяете.
Ваши данные хранятся в self и передаются по изменяемой ссылке, поэтому проверка заимствований поддерживает синхронность модели и представления. eframe::run_native управляет окном и циклом событий. Далее Вы изучите набор виджетов и систему размещения.
Часто задаваемые вопросы
Урок «Основы интерфейса немедленного режима» бесплатный?
Да — полный текст урока «Основы интерфейса немедленного режима» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 4 уроков всего.
Чему я научусь в уроке «Основы интерфейса немедленного режима»?
Поймите, как egui выполняет отрисовку. Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Learn Rust Coding?
Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Основы интерфейса немедленного режима»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Learn Rust Coding?
Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы интерфейса немедленного режима
- Виджеты и компоновка
- Управление состоянием приложения
- Сборка настольного приложения