Я создаю приложение gtkmm. Программа открывается с окном настроек, в котором пользователю предлагается указать некоторую информацию, и когда проверки работоспособности выполнены, это окно должно быть закрыто, и должно открыться главное окно приложения.
Прямо сейчас, открыв главное окно и скрыв окно настроек, полностью закройте приложение. В окнах настроек я делаю:
MainWindow* main_window = new MainWindow();
main_window->show();
this->hide();
Как я могу добиться описанного выше поведения?
Очевидно, вы можете добавлять и удалять окна с Gtk::App. Будет ли он делать то, что я описал, и означает ли это, что мне придется передать своему окну указатель Gtk::App? Спасибо.





Кажется, что правильное решение - передать окну указатель приложения (m_app), добавить к нему новое окно, показать это окно и скрыть текущее. Удаление текущего из приложения позволит функции run () вернуть:
MainWindow* main_window = new MainWindow(m_app);
m_app->add_window(*main_window);
main_window->show();
this->hide();
m_app->remove_window(*this);
delete->this;
Это работает, но это может быть неправильным способом.
В ответ на ваш ответ: delete->this - это чистая синтаксическая ошибка, и даже без -> запись delete this обычно является запахом кода. За исключением этого, то, что вы сделали, похоже, будет работать, если, возможно, не будет настолько интуитивно понятным, насколько могло бы быть.
Однако делать что-то в такой последовательности не всегда возможно. Например, вы можете не знать, каким будет следующий Window. Возможно, какое окно откроется следующим, зависит от ответа HTTP, получение которого может занять некоторое время.
Общее решение - вызвать Application.hold() перед удалением Window. Вызов .hold() увеличивает счетчик использования GApplication, как и добавление окна. Приложение завершает работу, когда его счетчик использования равен нулю. Сказать, что его жизнь контролируется окнами, - это просто быстрый способ приблизиться к объяснению этого (и, очевидно, относится только к GtkApplication, а не к базовому GApplication). Удаление окна уменьшает счетчик использования.
Итак, в этом случае вы теперь перейдете от счетчика использования от 2 к 1, а не от 1 к 0, поэтому удаление 1-го окна больше не приведет к закрытию приложения. Затем, после того, как вы добавите 2-е окно, хотя это произойдет намного позже, вызовите .release(), чтобы удалить дополнительный счетчик использования, чтобы оставшиеся окна теперь снова управляют временем жизни приложения исключительно.
Хотя вопрос довольно старый, я покажу свой подход, который может помочь кому-то другому справиться с этой задачей.
Я использую общий объект приложения, который содержит все объекты окна: MainApplication.cpp
MainApplication::MainApplication(int argc, char **argv)
{
// Creating the main application object as first
mainApp = Gtk::Application::create(argc, argv, APPLICATION_ID);
// Create the entry window
createEntryWindow();
}
int MainApplication::run()
{
if (!isRunning) {
// Set the current window to entry window for startup
currentWindow = entryWindow;
return mainApp->run(*entryWindow);
} else {
return -1;
}
}
void MainApplication::createEntryWindow()
{
// Load the entry_window.glade layout with the Gtk::Builder Api
Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_file("../layout/entry_window.glade");
// Calls the constructor of my own derived widget class which details are specified inside the builder file
builder->get_widget_derived(WND_ENTRY, entryWindow);
// Set this main application object to the new window
entryWindow->setMainApplicationContext(this);
}
MainApplication.h
static const int WS_ENTRY = 100;
static const int WS_SOMETHING = 200;
class MainApplication {
public:
MainApplication(int argc, char* argv[]);
int run();
void switchCurrentWindow(int specifier);
private:
void createEntryWindow();
private:
Glib::RefPtr<Gtk::Application> mainApp;
Gtk::Window* currentWindow = nullptr;
EntryWindow* entryWindow = nullptr;
bool isRunning = false;
};
Объект MainApplication будет создан внутри main (), после чего вызывается run (): main.cpp
int main(int argc, char* argv[])
{
// Create main application object
MainApplication mainApplication(argc, argv);
// Starts the event loop
// No events propagate until this has been called
return mainApplication.run();
}
EntryWindow.cpp выглядит так (простой пример):
EntryWindow::EntryWindow(BaseObjectType* object, const Glib::RefPtr<Gtk::Builder>& refGlade)
: Gtk::Window(object), builder(refGlade)
{
// Set widgets to builder
builder->get_widget(btnName, btn);
// Set on click methods for signal_clicked
btn->signal_clicked().connect(sigc::mem_fun(*this, &EntryWindow::onBtnClicked));
}
void EntryWindow::onBtnClicked()
{
mainApplicationContext->switchCurrentWindow(WS_SOMETHING);
}
void EntryWindow::setMainApplicationContext(MainApplication* mainApplication)
{
this->mainApplicationContext = mainApplication;
}
EntryWindow.h:
class EntryWindow : public Gtk::Window {
public:
EntryWindow(BaseObjectType* object, const Glib::RefPtr<Gtk::Builder>& refGlade);
void setMainApplicationContext(MainApplication* mainApplication);
protected:
void onBtnClicked();
protected:
const Glib::RefPtr<Gtk::Builder> builder;
Gtk::Button* btn;
private:
MainApplication* mainApplicationContext = nullptr;
const Glib::ustring btnName = BTN_NAME;
};
Итак, теперь, когда была нажата кнопка, вы можете переключать окна с помощью следующей функции внутри класса MainApplication:
void MainApplication::switchCurrentWindow(int specifier)
{
// Check if the passed specifier exist
int tmpSpecifier = 0;
switch (specifier) {
case WS_ENTRY:
tmpSpecifier = WS_ENTRY;
break;
case WS_SOMETHING:
tmpSpecifier = WS_SOMETHING;
break;
default:
tmpSpecifier = 0;
}
// If the specifier exist
if (tmpSpecifier != 0) {
// Increase the use counter of the main application object
mainApp->hold();
// Hide the current window
currentWindow->hide();
// Remove the current window
mainApp->remove_window(*currentWindow);
} else {
return;
}
switch (tmpSpecifier) {
case WS_ENTRY:
currentWindow = entryWindow;
break;
case WS_SOMETHING:
currentWindow = somethingWindow;
break;
}
// Add the new current window
mainApp->add_window(*currentWindow);
// Show the new window
currentWindow->show();
// Decrease the use counter of the main application object
mainApp->release();
}
Резюме: Создайте объект, содержащий все окна. Поэтому всякий раз, когда вам нужно новое окно, вы должны создавать его внутри этого объекта. Этот основной объект приложения будет вызываться функцией main (), и там он вызовет run (), когда приложение будет готово к запуску. После этого вы будете определять, какое окно отображается и скрывается только основным объектом приложения.