1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29
30 // XXX: must be included before Qt headers because of `slot' redefine
31 #include "mainwindow/pythonconsole/pythonconsolewidget.h"
32
33 // Interface header.
34 #include "mainwindow.h"
35
36 // UI definition header.
37 #include "ui_mainwindow.h"
38
39 // appleseed.studio headers.
40 #include "help/about/aboutwindow.h"
41 #include "mainwindow/logwidget.h"
42 #include "mainwindow/minimizebutton.h"
43 #include "mainwindow/project/attributeeditor.h"
44 #include "mainwindow/project/projectexplorer.h"
45 #include "mainwindow/rendering/lightpathstab.h"
46 #include "mainwindow/rendering/materialdrophandler.h"
47 #include "mainwindow/rendering/renderwidget.h"
48 #include "utility/interop.h"
49 #include "utility/miscellaneous.h"
50 #include "utility/settingskeys.h"
51
52 // appleseed.shared headers.
53 #include "application/application.h"
54
55 // appleseed.renderer headers.
56 #include "renderer/api/aov.h"
57 #include "renderer/api/frame.h"
58 #include "renderer/api/lighting.h"
59 #include "renderer/api/log.h"
60 #include "renderer/api/postprocessing.h"
61 #include "renderer/api/project.h"
62 #include "renderer/api/rendering.h"
63 #include "renderer/api/surfaceshader.h"
64
65 // appleseed.foundation headers.
66 #include "foundation/core/appleseed.h"
67 #include "foundation/math/aabb.h"
68 #include "foundation/math/vector.h"
69 #include "foundation/platform/compiler.h"
70 #include "foundation/platform/path.h"
71 #include "foundation/platform/python.h"
72 #include "foundation/platform/system.h"
73 #include "foundation/utility/containers/dictionary.h"
74 #include "foundation/utility/foreach.h"
75 #include "foundation/utility/log/logmessage.h"
76
77 // Qt headers.
78 #include <QAction>
79 #include <QActionGroup>
80 #include <QApplication>
81 #include <QCloseEvent>
82 #include <QDir>
83 #include <QDragEnterEvent>
84 #include <QFileInfo>
85 #include <QFileSystemWatcher>
86 #include <QLabel>
87 #include <QLayout>
88 #include <QLineEdit>
89 #include <QMenu>
90 #include <QMessageBox>
91 #include <QMimeData>
92 #include <QRect>
93 #include <QSettings>
94 #include <QStatusBar>
95 #include <QString>
96 #include <QStringList>
97 #include <Qt>
98 #include <QUrl>
99 #include <QVariant>
100
101 // Boost headers.
102 #include "boost/filesystem/path.hpp"
103
104 // Standard headers.
105 #include <algorithm>
106 #include <cassert>
107 #include <cstdlib>
108
109 using namespace appleseed::shared;
110 using namespace foundation;
111 using namespace renderer;
112 using namespace std;
113 namespace bf = boost::filesystem;
114
115 namespace appleseed {
116 namespace studio {
117
118 //
119 // MainWindow class implementation.
120 //
121
122 namespace
123 {
124 const int MaxRecentlyOpenedFiles = 15;
125 }
126
MainWindow(QWidget * parent)127 MainWindow::MainWindow(QWidget* parent)
128 : QMainWindow(parent)
129 , m_ui(new Ui::MainWindow())
130 , m_rendering_manager(m_status_bar)
131 , m_project_explorer(nullptr)
132 , m_attribute_editor(nullptr)
133 , m_project_file_watcher(nullptr)
134 , m_light_paths_tab(nullptr)
135 {
136 initialize_ocio();
137
138 m_ui->setupUi(this);
139
140 build_menus();
141 build_status_bar();
142 build_toolbar();
143 build_log_panel();
144 build_python_console_panel();
145 build_project_explorer();
146 build_connections();
147
148 slot_load_application_settings();
149 slot_check_fullscreen();
150
151 update_project_explorer();
152 update_workspace();
153
154 setAcceptDrops(true);
155 }
156
~MainWindow()157 MainWindow::~MainWindow()
158 {
159 delete m_project_explorer;
160 delete m_ui;
161 }
162
163 namespace
164 {
165 class CustomSignalMapper
166 : public QObject
167 {
168 Q_OBJECT
169
170 public:
CustomSignalMapper(QObject * parent,const QString & configuration)171 CustomSignalMapper(QObject* parent, const QString& configuration)
172 : QObject(parent)
173 , m_configuration(configuration)
174 {
175 }
176
177 signals:
178 void mapped(const QString& filepath, const QString& config, const bool success);
179
180 public slots:
map(const QString & filepath,const bool success)181 void map(const QString& filepath, const bool success)
182 {
183 emit mapped(filepath, m_configuration, success);
184 }
185
186 private:
187 const QString m_configuration;
188 };
189 }
190
new_project()191 void MainWindow::new_project()
192 {
193 m_project_manager.create_project();
194 on_project_change();
195 }
196
open_project(const QString & filepath)197 bool MainWindow::open_project(const QString& filepath)
198 {
199 save_state_before_project_open();
200
201 if (m_rendering_manager.is_rendering())
202 {
203 m_rendering_manager.abort_rendering();
204 m_rendering_manager.wait_until_rendering_end();
205 }
206
207 remove_render_tabs();
208
209 set_file_widgets_enabled(false, NotRendering);
210 set_project_explorer_enabled(false);
211 set_rendering_widgets_enabled(false, NotRendering);
212 set_diagnostics_widgets_enabled(false, NotRendering);
213
214 const bool successful = m_project_manager.load_project(filepath.toUtf8().constData());
215
216 if (successful)
217 {
218 on_project_change();
219 }
220 else
221 {
222 recreate_render_tabs();
223 update_workspace();
224 }
225
226 return successful;
227 }
228
open_project_async(const QString & filepath)229 void MainWindow::open_project_async(const QString& filepath)
230 {
231 save_state_before_project_open();
232
233 if (m_rendering_manager.is_rendering())
234 {
235 m_rendering_manager.abort_rendering();
236 m_rendering_manager.wait_until_rendering_end();
237 }
238
239 remove_render_tabs();
240
241 set_file_widgets_enabled(false, NotRendering);
242 set_project_explorer_enabled(false);
243 set_rendering_widgets_enabled(false, NotRendering);
244 set_diagnostics_widgets_enabled(false, NotRendering);
245
246 m_project_manager.load_project_async(filepath.toUtf8().constData());
247 }
248
open_and_render_project(const QString & filepath,const QString & configuration)249 void MainWindow::open_and_render_project(const QString& filepath, const QString& configuration)
250 {
251 CustomSignalMapper* mapper = new CustomSignalMapper(this, configuration);
252
253 connect(
254 &m_project_manager, SIGNAL(signal_load_project_async_complete(const QString&, const bool)),
255 mapper, SLOT(map(const QString&, const bool)));
256
257 connect(
258 mapper, SIGNAL(mapped(const QString&, const QString&, const bool)),
259 SLOT(slot_start_rendering_once(const QString&, const QString&, const bool)));
260
261 open_project_async(filepath);
262 }
263
save_project(QString filepath)264 bool MainWindow::save_project(QString filepath)
265 {
266 if (!m_project_manager.is_project_open())
267 return false;
268
269 const QString Extension = "appleseed";
270
271 if (QFileInfo(filepath).suffix() != Extension)
272 filepath += "." + Extension;
273
274 if (m_project_file_watcher)
275 stop_monitoring_project_file();
276
277 const bool successful = m_project_manager.save_project_as(filepath.toUtf8().constData());
278
279 if (m_project_file_watcher)
280 start_monitoring_project_file();
281
282 if (successful)
283 update_recent_files_menu(filepath);
284 update_workspace();
285
286 return successful;
287 }
288
pack_project(QString filepath)289 bool MainWindow::pack_project(QString filepath)
290 {
291 if (!m_project_manager.is_project_open())
292 return false;
293
294 const QString Extension = "appleseedz";
295
296 if (QFileInfo(filepath).suffix() != Extension)
297 filepath += "." + Extension;
298
299 return m_project_manager.pack_project_as(filepath.toUtf8().constData());
300 }
301
close_project()302 void MainWindow::close_project()
303 {
304 m_project_manager.close_project();
305 on_project_change();
306 }
307
get_project_manager()308 ProjectManager* MainWindow::get_project_manager()
309 {
310 return &m_project_manager;
311 }
312
get_application_settings()313 ParamArray& MainWindow::get_application_settings()
314 {
315 return m_application_settings;
316 }
317
create_dock_widget(const char * dock_name)318 QDockWidget* MainWindow::create_dock_widget(const char* dock_name)
319 {
320 QDockWidget* dock_widget = new QDockWidget(this);
321
322 const QString object_name = QString(dock_name).toLower().split(' ').join("_");
323 dock_widget->setObjectName(object_name);
324 dock_widget->setWindowTitle(dock_name);
325
326 const QList<QAction*> actions = m_ui->menu_view->actions();
327 QAction* menu_separator = actions.last();
328 for (int i = actions.size() - 2; i != 0; --i)
329 {
330 if (actions[i]->isSeparator())
331 {
332 menu_separator = actions[i];
333 break;
334 }
335 }
336
337 m_ui->menu_view->insertAction(
338 menu_separator,
339 dock_widget->toggleViewAction());
340
341 m_minimize_buttons.push_back(new MinimizeButton(dock_widget));
342
343 statusBar()->insertPermanentWidget(
344 static_cast<int>(m_minimize_buttons.size()),
345 m_minimize_buttons.back());
346
347 return dock_widget;
348 }
349
build_menus()350 void MainWindow::build_menus()
351 {
352 //
353 // File menu.
354 //
355
356 m_ui->action_file_new_project->setShortcut(QKeySequence::New);
357 connect(m_ui->action_file_new_project, SIGNAL(triggered()), SLOT(slot_new_project()));
358
359 m_ui->action_file_open_project->setShortcut(QKeySequence::Open);
360 connect(m_ui->action_file_open_project, SIGNAL(triggered()), SLOT(slot_open_project()));
361
362 build_recent_files_menu();
363
364 connect(m_ui->action_file_open_builtin_project_cornellbox, SIGNAL(triggered()), SLOT(slot_open_cornellbox_builtin_project()));
365 connect(m_ui->action_file_reload_project, SIGNAL(triggered()), SLOT(slot_reload_project()));
366
367 connect(m_ui->action_file_monitor_project, SIGNAL(toggled(bool)), SLOT(slot_toggle_project_file_monitoring(const bool)));
368
369 m_ui->action_file_save_project->setShortcut(QKeySequence::Save);
370 connect(m_ui->action_file_save_project, SIGNAL(triggered()), SLOT(slot_save_project()));
371
372 m_ui->action_file_save_project_as->setShortcut(QKeySequence::SaveAs);
373 connect(m_ui->action_file_save_project_as, SIGNAL(triggered()), SLOT(slot_save_project_as()));
374
375 connect(m_ui->action_file_pack_project_as, SIGNAL(triggered()), SLOT(slot_pack_project_as()));
376
377 m_ui->action_file_close_project->setShortcut(QKeySequence::Close);
378 connect(m_ui->action_file_close_project, SIGNAL(triggered()), SLOT(slot_close_project()));
379
380 m_ui->action_file_exit->setShortcut(QKeySequence::Quit);
381 connect(m_ui->action_file_exit, SIGNAL(triggered()), SLOT(close()));
382
383 //
384 // View menu.
385 //
386
387 m_ui->menu_view->addAction(m_ui->project_explorer->toggleViewAction());
388 m_ui->menu_view->addAction(m_ui->attribute_editor->toggleViewAction());
389 m_ui->menu_view->addAction(m_ui->log->toggleViewAction());
390 m_ui->menu_view->addAction(m_ui->python_console->toggleViewAction());
391 m_ui->menu_view->addSeparator();
392
393 m_action_fullscreen = m_ui->menu_view->addAction("Fullscreen");
394 m_action_fullscreen->setCheckable(true);
395 m_action_fullscreen->setShortcut(Qt::Key_F11);
396
397 for (const auto dock_widget : findChildren<QDockWidget*>())
398 connect(dock_widget->toggleViewAction(), SIGNAL(triggered()), SLOT(slot_check_fullscreen()));
399
400 connect(m_action_fullscreen, SIGNAL(triggered()), SLOT(slot_fullscreen()));
401
402 //
403 // Rendering menu.
404 //
405
406 connect(m_ui->action_rendering_start_interactive_rendering, SIGNAL(triggered()), SLOT(slot_start_interactive_rendering()));
407 connect(m_ui->action_rendering_start_final_rendering, SIGNAL(triggered()), SLOT(slot_start_final_rendering()));
408 connect(m_ui->action_rendering_pause_resume_rendering, SIGNAL(toggled(bool)), SLOT(slot_pause_or_resume_rendering(const bool)));
409 connect(m_ui->action_rendering_stop_rendering, SIGNAL(triggered()), &m_rendering_manager, SLOT(slot_abort_rendering()));
410 connect(m_ui->action_rendering_rendering_settings, SIGNAL(triggered()), SLOT(slot_show_rendering_settings_window()));
411
412 //
413 // Diagnostics menu.
414 //
415
416 build_override_shading_menu_item();
417
418 connect(m_ui->action_diagnostics_false_colors, SIGNAL(triggered()), SLOT(slot_show_false_colors_window()));
419
420 //
421 // Debug menu.
422 //
423
424 connect(m_ui->action_debug_tests, SIGNAL(triggered()), SLOT(slot_show_test_window()));
425 connect(m_ui->action_debug_benchmarks, SIGNAL(triggered()), SLOT(slot_show_benchmark_window()));
426
427 //
428 // Tools menu.
429 //
430
431 connect(m_ui->action_tools_settings, SIGNAL(triggered()), SLOT(slot_show_application_settings_window()));
432 connect(m_ui->action_tools_save_settings, SIGNAL(triggered()), SLOT(slot_save_application_settings()));
433 connect(m_ui->action_tools_reload_settings, SIGNAL(triggered()), SLOT(slot_load_application_settings()));
434
435 //
436 // Help menu.
437 //
438
439 connect(m_ui->action_help_about, SIGNAL(triggered()), SLOT(slot_show_about_window()));
440 }
441
build_override_shading_menu_item()442 void MainWindow::build_override_shading_menu_item()
443 {
444 QActionGroup* action_group = new QActionGroup(this);
445
446 // No Override.
447 connect(
448 m_ui->action_diagnostics_override_shading_no_override, SIGNAL(triggered()),
449 SLOT(slot_clear_shading_override()));
450 action_group->addAction(m_ui->action_diagnostics_override_shading_no_override);
451
452 for (int i = 0; i < DiagnosticSurfaceShader::ShadingModeCount; ++i)
453 {
454 const char* shading_mode_value = DiagnosticSurfaceShader::ShadingModeNames[i].m_key;
455 const char* shading_mode_name = DiagnosticSurfaceShader::ShadingModeNames[i].m_value;
456
457 QAction* action = new QAction(this);
458 action->setObjectName(
459 QString::fromUtf8("action_diagnostics_override_shading_") + shading_mode_value);
460 action->setCheckable(true);
461 action->setText(shading_mode_name);
462
463 const int shortcut_number = i + 1;
464 if (shortcut_number <= 9)
465 {
466 action->setShortcut(
467 QKeySequence(QString::fromUtf8("Ctrl+Shift+%1").arg(shortcut_number)));
468 }
469
470 action->setData(shading_mode_value);
471
472 connect(
473 action, SIGNAL(triggered()),
474 SLOT(slot_set_shading_override()));
475
476 m_ui->menu_diagnostics_override_shading->addAction(action);
477 action_group->addAction(action);
478 }
479 }
480
update_override_shading_menu_item()481 void MainWindow::update_override_shading_menu_item()
482 {
483 const ParamArray project_params = get_project_params("interactive");
484 const ParamArray shading_engine_params = project_params.child("shading_engine");
485
486 if (shading_engine_params.dictionaries().exist("override_shading"))
487 {
488 const string shading_mode =
489 shading_engine_params.child("override_shading").get_optional<string>("mode", "coverage");
490
491 for (const_each<QList<QAction*>> i = m_ui->menu_diagnostics_override_shading->actions(); i; ++i)
492 {
493 QAction* action = *i;
494
495 if (action->data().toString().toStdString() == shading_mode)
496 {
497 action->activate(QAction::Trigger);
498 break;
499 }
500 }
501 }
502 else
503 {
504 m_ui->action_diagnostics_override_shading_no_override->activate(QAction::Trigger);
505 }
506 }
507
build_recent_files_menu()508 void MainWindow::build_recent_files_menu()
509 {
510 assert(m_recently_opened.empty());
511 m_recently_opened.reserve(MaxRecentlyOpenedFiles);
512
513 for (int i = 0; i < MaxRecentlyOpenedFiles; ++i)
514 {
515 QAction* action = new QAction(this);
516 action->setVisible(false);
517
518 connect(action, SIGNAL(triggered()), SLOT(slot_open_recent()));
519
520 m_ui->menu_open_recent->addAction(action);
521 m_recently_opened.push_back(action);
522 }
523
524 QSettings settings(SETTINGS_ORGANIZATION, SETTINGS_APPLICATION);
525 QStringList files = settings.value("recent_file_list").toStringList();
526
527 update_recent_files_menu(files);
528
529 m_ui->menu_open_recent->addSeparator();
530
531 QAction* clear_missing_files = new QAction(this);
532 clear_missing_files->setText("Clear &Missing Files");
533 connect(clear_missing_files, SIGNAL(triggered()), SLOT(slot_clear_recent_missing_project_files()));
534 m_ui->menu_open_recent->addAction(clear_missing_files);
535
536 QAction* clear_all_files = new QAction(this);
537 clear_all_files->setText("Clear &All Files");
538 connect(clear_all_files, SIGNAL(triggered()), SLOT(slot_clear_all_recent_project_files()));
539 m_ui->menu_open_recent->addAction(clear_all_files);
540 }
541
update_recent_files_menu(const QString & filepath)542 void MainWindow::update_recent_files_menu(const QString& filepath)
543 {
544 QSettings settings(SETTINGS_ORGANIZATION, SETTINGS_APPLICATION);
545 QStringList files = settings.value("recent_file_list").toStringList();
546
547 files.removeAll(filepath);
548 files.prepend(filepath);
549
550 while (files.size() > MaxRecentlyOpenedFiles)
551 files.removeLast();
552
553 update_recent_files_menu(files);
554
555 settings.setValue("recent_file_list", files);
556 }
557
update_recent_files_menu(const QStringList & files)558 void MainWindow::update_recent_files_menu(const QStringList& files)
559 {
560 const int recent_file_count = min(files.size(), MaxRecentlyOpenedFiles);
561
562 for (int i = 0; i < recent_file_count; ++i)
563 {
564 const int number = i + 1;
565 const QString filepath = files[i];
566 const QString format = number <= 9 ? "&%1 %2" : "%1 %2";
567 const QString text = format.arg(number).arg(filepath);
568
569 m_recently_opened[i]->setText(text);
570 m_recently_opened[i]->setData(filepath);
571 m_recently_opened[i]->setVisible(true);
572 }
573
574 for (int i = recent_file_count; i < MaxRecentlyOpenedFiles; ++i)
575 m_recently_opened[i]->setVisible(false);
576 }
577
update_pause_resume_checkbox(const bool checked)578 void MainWindow::update_pause_resume_checkbox(const bool checked)
579 {
580 bool old_state = m_action_pause_resume_rendering->blockSignals(true);
581 m_action_pause_resume_rendering->setChecked(checked);
582 m_action_pause_resume_rendering->blockSignals(old_state);
583
584 old_state = m_ui->action_rendering_pause_resume_rendering->blockSignals(true);
585 m_ui->action_rendering_pause_resume_rendering->setChecked(checked);
586 m_ui->action_rendering_pause_resume_rendering->blockSignals(old_state);
587 }
588
build_status_bar()589 void MainWindow::build_status_bar()
590 {
591 statusBar()->addWidget(&m_status_bar);
592
593 m_minimize_buttons.push_back(new MinimizeButton(m_ui->project_explorer));
594 m_minimize_buttons.push_back(new MinimizeButton(m_ui->attribute_editor));
595 m_minimize_buttons.push_back(new MinimizeButton(m_ui->log));
596 m_minimize_buttons.push_back(new MinimizeButton(m_ui->python_console));
597
598 for (size_t i = 0; i < m_minimize_buttons.size(); ++i)
599 {
600 statusBar()->insertPermanentWidget(
601 static_cast<int>(i + 1),
602 m_minimize_buttons[i]);
603 }
604 }
605
build_toolbar()606 void MainWindow::build_toolbar()
607 {
608 //
609 // File actions.
610 //
611
612 m_action_new_project = new QAction(load_icons("project_new"), combine_name_and_shortcut("New Project", m_ui->action_file_new_project->shortcut()), this);
613 connect(m_action_new_project, SIGNAL(triggered()), SLOT(slot_new_project()));
614 m_ui->main_toolbar->addAction(m_action_new_project);
615
616 m_action_open_project = new QAction(load_icons("project_open"), combine_name_and_shortcut("Open Project...", m_ui->action_file_open_project->shortcut()), this);
617 connect(m_action_open_project, SIGNAL(triggered()), SLOT(slot_open_project()));
618 m_ui->main_toolbar->addAction(m_action_open_project);
619
620 m_action_save_project = new QAction(load_icons("project_save") , combine_name_and_shortcut("Save Project", m_ui->action_file_save_project->shortcut()), this);
621 connect(m_action_save_project, SIGNAL(triggered()), SLOT(slot_save_project()));
622 m_ui->main_toolbar->addAction(m_action_save_project);
623
624 m_action_reload_project = new QAction(load_icons("project_reload"), combine_name_and_shortcut("Reload Project", m_ui->action_file_reload_project->shortcut()), this);
625 connect(m_action_reload_project, SIGNAL(triggered()), SLOT(slot_reload_project()));
626 m_ui->main_toolbar->addAction(m_action_reload_project);
627
628 m_action_monitor_project_file = new QAction(load_icons("project_monitor"), "Toggle Project File Monitoring", this);
629 m_action_monitor_project_file->setCheckable(true);
630 connect(m_action_monitor_project_file, SIGNAL(toggled(bool)), SLOT(slot_toggle_project_file_monitoring(const bool)));
631 m_ui->main_toolbar->addAction(m_action_monitor_project_file);
632
633 m_ui->main_toolbar->addSeparator();
634
635 //
636 // Rendering actions.
637 //
638
639 m_action_start_interactive_rendering = new QAction(load_icons("rendering_start_interactive"), combine_name_and_shortcut("Start Interactive Rendering", m_ui->action_rendering_start_interactive_rendering->shortcut()), this);
640 connect(m_action_start_interactive_rendering, SIGNAL(triggered()), SLOT(slot_start_interactive_rendering()));
641 m_ui->main_toolbar->addAction(m_action_start_interactive_rendering);
642
643 m_action_start_final_rendering = new QAction(load_icons("rendering_start_final"), combine_name_and_shortcut("Start Final Rendering", m_ui->action_rendering_start_final_rendering->shortcut()), this);
644 connect(m_action_start_final_rendering, SIGNAL(triggered()), SLOT(slot_start_final_rendering()));
645 m_ui->main_toolbar->addAction(m_action_start_final_rendering);
646
647 m_action_pause_resume_rendering = new QAction(load_icons("rendering_pause_resume"), combine_name_and_shortcut("Pause/Resume Rendering", m_ui->action_rendering_pause_resume_rendering->shortcut()), this);
648 m_action_pause_resume_rendering->setCheckable(true);
649 connect(m_action_pause_resume_rendering, SIGNAL(toggled(bool)), SLOT(slot_pause_or_resume_rendering(const bool)));
650 m_ui->main_toolbar->addAction(m_action_pause_resume_rendering);
651
652 m_action_stop_rendering = new QAction(load_icons("rendering_stop"), combine_name_and_shortcut("Stop Rendering", m_ui->action_rendering_stop_rendering->shortcut()), this);
653 connect(m_action_stop_rendering, SIGNAL(triggered()), &m_rendering_manager, SLOT(slot_abort_rendering()));
654 m_ui->main_toolbar->addAction(m_action_stop_rendering);
655
656 m_action_rendering_settings = new QAction(load_icons("rendering_settings"), combine_name_and_shortcut("Rendering Settings...", m_ui->action_rendering_rendering_settings->shortcut()), this);
657 connect(m_action_rendering_settings, SIGNAL(triggered()), SLOT(slot_show_rendering_settings_window()));
658 m_ui->main_toolbar->addAction(m_action_rendering_settings);
659 }
660
build_log_panel()661 void MainWindow::build_log_panel()
662 {
663 LogWidget* log_widget = new LogWidget(m_ui->log_contents);
664 log_widget->setObjectName("textedit_log");
665 log_widget->setUndoRedoEnabled(false);
666 log_widget->setLineWrapMode(QTextEdit::NoWrap);
667 log_widget->setReadOnly(true);
668 log_widget->setTextInteractionFlags(Qt::TextSelectableByMouse);
669 m_ui->log_contents->layout()->addWidget(log_widget);
670
671 m_log_target.reset(new QtLogTarget(log_widget));
672 global_logger().add_target(m_log_target.get());
673
674 RENDERER_LOG_INFO(
675 "%s, %s configuration\n"
676 "compiled on %s at %s using %s version %s",
677 Appleseed::get_synthetic_version_string(),
678 Appleseed::get_lib_configuration(),
679 Appleseed::get_lib_compilation_date(),
680 Appleseed::get_lib_compilation_time(),
681 Compiler::get_compiler_name(),
682 Compiler::get_compiler_version());
683
684 System::print_information(global_logger());
685 }
686
build_python_console_panel()687 void MainWindow::build_python_console_panel()
688 {
689 char* python_home = Py_EncodeLocale(Py_GetPythonHome(), nullptr);
690 if (python_home == nullptr)
691 RENDERER_LOG_INFO("Python home not set.");
692 else RENDERER_LOG_INFO("Python home set to %s.", python_home);
693
694 PythonConsoleWidget* python_console_widget = new PythonConsoleWidget(m_ui->python_console_contents);
695 python_console_widget->setObjectName("textedit_python_console");
696 m_ui->python_console_contents->layout()->addWidget(python_console_widget);
697 }
698
build_project_explorer()699 void MainWindow::build_project_explorer()
700 {
701 m_ui->treewidget_project_explorer_scene->setColumnWidth(0, 295); // name
702
703 disable_osx_focus_rect(m_ui->treewidget_project_explorer_scene);
704
705 connect(
706 m_ui->lineedit_filter, SIGNAL(textChanged(const QString&)),
707 SLOT(slot_filter_text_changed(const QString&)));
708
709 connect(
710 m_ui->pushbutton_clear_filter, SIGNAL(clicked()),
711 SLOT(slot_clear_filter()));
712
713 m_ui->pushbutton_clear_filter->setEnabled(false);
714 }
715
build_connections()716 void MainWindow::build_connections()
717 {
718 connect(
719 m_action_monitor_project_file, SIGNAL(toggled(bool)),
720 m_ui->action_file_monitor_project, SLOT(setChecked(bool)));
721
722 connect(
723 m_ui->action_file_monitor_project, SIGNAL(toggled(bool)),
724 m_action_monitor_project_file, SLOT(setChecked(bool)));
725
726 connect(
727 &m_project_manager, SIGNAL(signal_load_project_async_complete(const QString&, const bool)),
728 SLOT(slot_open_project_complete(const QString&, const bool)));
729
730 connect(
731 &m_rendering_manager, SIGNAL(signal_rendering_end()),
732 SLOT(slot_rendering_end()));
733 }
734
update_workspace()735 void MainWindow::update_workspace()
736 {
737 update_window_title();
738
739 // Enable/disable menus and widgets appropriately.
740 set_file_widgets_enabled(true, NotRendering);
741 set_project_explorer_enabled(true);
742 set_rendering_widgets_enabled(true, NotRendering);
743 set_diagnostics_widgets_enabled(true, NotRendering);
744 update_pause_resume_checkbox(false);
745 m_ui->attribute_editor_scrollarea_contents->setEnabled(true);
746
747 // Add/remove light paths tab.
748 if (m_project_manager.is_project_open() &&
749 m_project_manager.get_project()->get_light_path_recorder().get_light_path_count() > 0)
750 add_light_paths_tab();
751 else remove_light_paths_tab();
752 }
753
update_project_explorer()754 void MainWindow::update_project_explorer()
755 {
756 delete m_project_explorer;
757 m_project_explorer = nullptr;
758
759 delete m_attribute_editor;
760 m_attribute_editor = nullptr;
761
762 if (m_project_manager.is_project_open())
763 {
764 m_attribute_editor =
765 new AttributeEditor(
766 m_ui->attribute_editor_scrollarea_contents,
767 *m_project_manager.get_project(),
768 m_application_settings);
769
770 m_project_explorer =
771 new ProjectExplorer(
772 m_ui->treewidget_project_explorer_scene,
773 m_attribute_editor,
774 *m_project_manager.get_project(),
775 m_project_manager,
776 m_rendering_manager,
777 m_application_settings);
778
779 connect(
780 m_project_explorer, SIGNAL(signal_project_modified()),
781 SLOT(slot_project_modified()));
782
783 connect(
784 m_project_explorer, SIGNAL(signal_frame_modified()),
785 SLOT(slot_frame_modified()));
786 }
787
788 m_ui->lineedit_filter->clear();
789 }
790
update_window_title()791 void MainWindow::update_window_title()
792 {
793 QString title;
794
795 if (m_project_manager.is_project_open())
796 {
797 if (m_project_manager.is_project_dirty())
798 title.append("* ");
799
800 title.append(QString::fromStdString(m_project_manager.get_project_display_name()));
801 title.append(" - ");
802 }
803
804 title.append("appleseed.studio");
805
806 setWindowTitle(title);
807 }
808
set_file_widgets_enabled(const bool is_enabled,const RenderingMode rendering_mode)809 void MainWindow::set_file_widgets_enabled(const bool is_enabled, const RenderingMode rendering_mode)
810 {
811 const bool is_project_open = m_project_manager.is_project_open();
812 const bool project_has_path = is_project_open && m_project_manager.get_project()->has_path();
813
814 // File -> New Project.
815 m_ui->action_file_new_project->setEnabled(is_enabled);
816 m_action_new_project->setEnabled(is_enabled);
817
818 // File -> Open Project.
819 m_ui->action_file_open_project->setEnabled(is_enabled);
820 m_action_open_project->setEnabled(is_enabled);
821
822 // File -> Open Recent.
823 m_ui->menu_open_recent->setEnabled(is_enabled);
824
825 // File -> Open Built-in Project.
826 m_ui->menu_file_open_builtin_project->setEnabled(is_enabled);
827
828 // File -> Reload Project.
829 const bool allow_reload = (is_enabled || rendering_mode == InteractiveRendering) && project_has_path;
830 m_ui->action_file_reload_project->setEnabled(allow_reload);
831 m_action_reload_project->setEnabled(allow_reload);
832
833 // File -> Monitor Project.
834 const bool allow_monitor = (is_enabled || rendering_mode == InteractiveRendering) && project_has_path;
835 m_ui->action_file_monitor_project->setEnabled(allow_monitor);
836 m_action_monitor_project_file->setEnabled(allow_monitor);
837
838 // File -> Save Project, Save Project As and Pack Project As.
839 const bool allow_save = is_enabled && is_project_open;
840 m_ui->action_file_save_project->setEnabled(allow_save);
841 m_action_save_project->setEnabled(allow_save);
842 m_ui->action_file_save_project_as->setEnabled(allow_save);
843 m_ui->action_file_pack_project_as->setEnabled(allow_save);
844
845 // File -> Close Project.
846 const bool allow_close = is_enabled && is_project_open;
847 m_ui->action_file_close_project->setEnabled(allow_close);
848
849 // File -> Exit.
850 m_ui->action_file_exit->setEnabled(is_enabled);
851 }
852
set_project_explorer_enabled(const bool is_enabled)853 void MainWindow::set_project_explorer_enabled(const bool is_enabled)
854 {
855 const bool is_project_open = m_project_manager.is_project_open();
856
857 m_ui->label_filter->setEnabled(is_enabled && is_project_open);
858 m_ui->lineedit_filter->setEnabled(is_enabled && is_project_open);
859 m_ui->pushbutton_clear_filter->setEnabled(is_enabled && is_project_open && !m_ui->lineedit_filter->text().isEmpty());
860 m_ui->treewidget_project_explorer_scene->setEnabled(is_enabled && is_project_open);
861 }
862
set_rendering_widgets_enabled(const bool is_enabled,const RenderingMode rendering_mode)863 void MainWindow::set_rendering_widgets_enabled(const bool is_enabled, const RenderingMode rendering_mode)
864 {
865 const bool is_project_open = m_project_manager.is_project_open();
866 const bool allow_start = is_enabled && is_project_open && rendering_mode == NotRendering;
867 const bool allow_stop = is_enabled && is_project_open && rendering_mode != NotRendering;
868
869 // Rendering -> Rendering Settings.
870 m_ui->action_rendering_rendering_settings->setEnabled(allow_start);
871 m_action_rendering_settings->setEnabled(allow_start);
872
873 // Rendering -> Start Interactive Rendering.
874 m_ui->action_rendering_start_interactive_rendering->setEnabled(allow_start);
875 m_action_start_interactive_rendering->setEnabled(allow_start);
876
877 // Rendering -> Start Final Rendering.
878 m_ui->action_rendering_start_final_rendering->setEnabled(allow_start);
879 m_action_start_final_rendering->setEnabled(allow_start);
880
881 // Rendering -> Pause/Resume Rendering.
882 m_ui->action_rendering_pause_resume_rendering->setEnabled(allow_stop);
883 m_action_pause_resume_rendering->setEnabled(allow_stop);
884
885 // Rendering -> Stop Rendering.
886 m_ui->action_rendering_stop_rendering->setEnabled(allow_stop);
887 m_action_stop_rendering->setEnabled(allow_stop);
888
889 // Rendering -> Render Settings.
890 m_ui->action_rendering_rendering_settings->setEnabled(allow_start);
891
892 // Render tab buttons.
893 const int current_tab_index = m_ui->tab_render_channels->currentIndex();
894 if (current_tab_index != -1)
895 {
896 const auto render_tab_it = m_tab_index_to_render_tab.find(current_tab_index);
897 if (render_tab_it != m_tab_index_to_render_tab.end())
898 {
899 RenderTab* render_tab = render_tab_it->second;
900
901 // Clear frame.
902 render_tab->set_clear_frame_button_enabled(
903 is_enabled && is_project_open && rendering_mode == NotRendering);
904
905 // Set/clear rendering region.
906 render_tab->set_render_region_buttons_enabled(
907 is_enabled && is_project_open && rendering_mode != FinalRendering);
908
909 // Scene picker.
910 render_tab->get_scene_picking_handler()->set_enabled(
911 is_enabled && is_project_open && rendering_mode != FinalRendering);
912 }
913 }
914 }
915
set_diagnostics_widgets_enabled(const bool is_enabled,const RenderingMode rendering_mode)916 void MainWindow::set_diagnostics_widgets_enabled(const bool is_enabled, const RenderingMode rendering_mode)
917 {
918 const bool is_project_open = m_project_manager.is_project_open();
919
920 m_ui->menu_diagnostics_override_shading->setEnabled(is_enabled && is_project_open);
921 m_ui->action_diagnostics_false_colors->setEnabled(is_enabled && is_project_open && rendering_mode == NotRendering);
922 }
923
save_state_before_project_open()924 void MainWindow::save_state_before_project_open()
925 {
926 m_state_before_project_open.reset(new StateBeforeProjectOpen());
927
928 m_state_before_project_open->m_is_rendering = m_rendering_manager.is_rendering();
929
930 for (const_each<RenderTabCollection> i = m_render_tabs; i; ++i)
931 m_state_before_project_open->m_render_tab_states[i->first] = i->second->save_state();
932 }
933
restore_state_after_project_open()934 void MainWindow::restore_state_after_project_open()
935 {
936 if (m_state_before_project_open.get())
937 {
938 for (const_each<RenderTabCollection> i = m_render_tabs; i; ++i)
939 {
940 const RenderTabStateCollection& tab_states = m_state_before_project_open->m_render_tab_states;
941 const RenderTabStateCollection::const_iterator tab_state_it = tab_states.find(i->first);
942
943 if (tab_state_it != tab_states.end())
944 i->second->load_state(tab_state_it->second);
945 }
946
947 if (m_state_before_project_open->m_is_rendering)
948 start_rendering(InteractiveRendering);
949 }
950 }
951
recreate_render_tabs()952 void MainWindow::recreate_render_tabs()
953 {
954 remove_render_tabs();
955
956 if (m_project_manager.is_project_open())
957 add_render_tab("RGB");
958 }
959
remove_render_tabs()960 void MainWindow::remove_render_tabs()
961 {
962 for (const_each<RenderTabCollection> i = m_render_tabs; i; ++i)
963 delete i->second;
964
965 m_render_tabs.clear();
966 m_tab_index_to_render_tab.clear();
967
968 while (m_ui->tab_render_channels->count() > 0)
969 m_ui->tab_render_channels->removeTab(0);
970 }
971
add_render_tab(const QString & label)972 void MainWindow::add_render_tab(const QString& label)
973 {
974 // Create render tab.
975 RenderTab* render_tab =
976 new RenderTab(
977 *m_project_explorer,
978 *m_project_manager.get_project(),
979 m_rendering_manager,
980 m_ocio_config);
981
982 // Connect the render tab to the main window and the rendering manager.
983 connect(
984 render_tab, SIGNAL(signal_render_widget_context_menu(const QPoint&)),
985 SLOT(slot_render_widget_context_menu(const QPoint&)));
986 connect(
987 render_tab, SIGNAL(signal_set_render_region(const QRect&)),
988 SLOT(slot_set_render_region(const QRect&)));
989 connect(
990 render_tab, SIGNAL(signal_clear_render_region()),
991 SLOT(slot_clear_render_region()));
992 connect(
993 render_tab, SIGNAL(signal_save_frame_and_aovs()),
994 SLOT(slot_save_frame_and_aovs()));
995 connect(
996 render_tab, SIGNAL(signal_quicksave_frame_and_aovs()),
997 SLOT(slot_quicksave_frame_and_aovs()));
998 connect(
999 render_tab, SIGNAL(signal_reset_zoom()),
1000 SLOT(slot_reset_zoom()));
1001 connect(
1002 render_tab, SIGNAL(signal_clear_frame()),
1003 SLOT(slot_clear_frame()));
1004 connect(
1005 render_tab, SIGNAL(signal_entity_picked(renderer::ScenePicker::PickingResult)),
1006 SLOT(slot_clear_filter()));
1007 connect(
1008 render_tab, SIGNAL(signal_camera_change_begin()),
1009 &m_rendering_manager, SLOT(slot_camera_change_begin()));
1010 connect(
1011 render_tab, SIGNAL(signal_camera_change_end()),
1012 &m_rendering_manager, SLOT(slot_camera_change_end()));
1013 connect(
1014 render_tab, SIGNAL(signal_camera_changed()),
1015 &m_rendering_manager, SLOT(slot_camera_changed()));
1016
1017 // Add the render tab to the tab bar.
1018 const int tab_index = m_ui->tab_render_channels->addTab(render_tab, label);
1019
1020 // Update mappings.
1021 m_render_tabs[label.toStdString()] = render_tab;
1022 m_tab_index_to_render_tab[tab_index] = render_tab;
1023 }
1024
add_light_paths_tab()1025 void MainWindow::add_light_paths_tab()
1026 {
1027 if (m_light_paths_tab == nullptr)
1028 {
1029 // Create light paths tab.
1030 m_light_paths_tab =
1031 new LightPathsTab(
1032 *m_project_manager.get_project(),
1033 m_application_settings);
1034
1035 // Connect render tabs to the light paths tab.
1036 for (const auto& kv : m_render_tabs)
1037 {
1038 connect(
1039 kv.second, SIGNAL(signal_entity_picked(renderer::ScenePicker::PickingResult)),
1040 m_light_paths_tab, SLOT(slot_entity_picked(renderer::ScenePicker::PickingResult)));
1041 connect(
1042 kv.second, SIGNAL(signal_rectangle_selection(const QRect&)),
1043 m_light_paths_tab, SLOT(slot_rectangle_selection(const QRect&)));
1044 }
1045
1046 // Add the light paths tab to the tab bar.
1047 m_ui->tab_render_channels->addTab(m_light_paths_tab, "Light Paths");
1048 }
1049 }
1050
remove_light_paths_tab()1051 void MainWindow::remove_light_paths_tab()
1052 {
1053 if (m_light_paths_tab != nullptr)
1054 {
1055 delete m_light_paths_tab;
1056 m_light_paths_tab = nullptr;
1057 }
1058 }
1059
get_project_params(const char * configuration_name) const1060 ParamArray MainWindow::get_project_params(const char* configuration_name) const
1061 {
1062 ParamArray params;
1063
1064 Configuration* configuration =
1065 m_project_manager.is_project_open()
1066 ? m_project_manager.get_project()->configurations().get_by_name(configuration_name)
1067 : nullptr;
1068
1069 // Start with the parameters from the base configuration.
1070 if (configuration && configuration->get_base())
1071 params = configuration->get_base()->get_parameters();
1072
1073 // Override with application settings.
1074 params.merge(m_application_settings);
1075
1076 // Override with parameters from the configuration.
1077 if (configuration)
1078 params.merge(configuration->get_parameters());
1079
1080 return params;
1081 }
1082
1083 namespace
1084 {
show_modified_project_message_box(QWidget * parent)1085 int show_modified_project_message_box(QWidget* parent)
1086 {
1087 QMessageBox msgbox(parent);
1088 msgbox.setWindowTitle("Save Changes?");
1089 msgbox.setIcon(QMessageBox::Question);
1090 msgbox.setText("The project has been modified.");
1091 msgbox.setInformativeText("Do you want to save your changes?");
1092 msgbox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
1093 msgbox.setDefaultButton(QMessageBox::Save);
1094 return msgbox.exec();
1095 }
1096 }
1097
can_close_project()1098 bool MainWindow::can_close_project()
1099 {
1100 // Project being loaded: can't close.
1101 if (m_project_manager.is_project_loading())
1102 return false;
1103
1104 // No project open: no problem.
1105 if (!m_project_manager.is_project_open())
1106 return true;
1107
1108 // Unmodified project: no problem.
1109 if (!m_project_manager.is_project_dirty())
1110 return true;
1111
1112 // The current project has been modified, ask the user what to do.
1113 switch (show_modified_project_message_box(this))
1114 {
1115 case QMessageBox::Save:
1116 slot_save_project();
1117 return true;
1118
1119 case QMessageBox::Discard:
1120 return true;
1121
1122 case QMessageBox::Cancel:
1123 return false;
1124 }
1125
1126 assert(!"Should never be reached.");
1127 return false;
1128 }
1129
on_project_change()1130 void MainWindow::on_project_change()
1131 {
1132 update_project_explorer();
1133 recreate_render_tabs();
1134
1135 update_override_shading_menu_item();
1136 m_false_colors_window.reset();
1137
1138 if (m_rendering_settings_window.get() != nullptr &&
1139 m_project_manager.get_project() != nullptr)
1140 m_rendering_settings_window->reload();
1141
1142 m_status_bar.clear();
1143
1144 update_workspace();
1145
1146 restore_state_after_project_open();
1147
1148 if (m_project_file_watcher)
1149 start_monitoring_project_file();
1150 }
1151
enable_project_file_monitoring()1152 void MainWindow::enable_project_file_monitoring()
1153 {
1154 if (m_project_file_watcher == nullptr)
1155 {
1156 m_project_file_watcher = new QFileSystemWatcher(this);
1157
1158 connect(
1159 m_project_file_watcher,
1160 SIGNAL(fileChanged(const QString&)),
1161 SLOT(slot_project_file_changed(const QString&)));
1162
1163 RENDERER_LOG_INFO("project file monitoring is now enabled.");
1164
1165 start_monitoring_project_file();
1166 }
1167 }
1168
disable_project_file_monitoring()1169 void MainWindow::disable_project_file_monitoring()
1170 {
1171 if (m_project_file_watcher)
1172 {
1173 delete m_project_file_watcher;
1174 m_project_file_watcher = nullptr;
1175
1176 RENDERER_LOG_INFO("project file monitoring is now disabled.");
1177 }
1178 }
1179
start_monitoring_project_file()1180 void MainWindow::start_monitoring_project_file()
1181 {
1182 assert(m_project_file_watcher);
1183
1184 if (m_project_manager.is_project_open() &&
1185 m_project_manager.get_project()->has_path())
1186 {
1187 m_project_file_watcher->addPath(m_project_manager.get_project()->get_path());
1188 }
1189 }
1190
stop_monitoring_project_file()1191 void MainWindow::stop_monitoring_project_file()
1192 {
1193 assert(m_project_file_watcher);
1194
1195 if (m_project_manager.is_project_open() &&
1196 m_project_manager.get_project()->has_path())
1197 {
1198 m_project_file_watcher->removePath(m_project_manager.get_project()->get_path());
1199 }
1200 }
1201
dragEnterEvent(QDragEnterEvent * event)1202 void MainWindow::dragEnterEvent(QDragEnterEvent* event)
1203 {
1204 if (event->mimeData()->hasFormat("text/uri-list"))
1205 event->acceptProposedAction();
1206 }
1207
dropEvent(QDropEvent * event)1208 void MainWindow::dropEvent(QDropEvent* event)
1209 {
1210 if (event->mimeData()->hasFormat("text/uri-list"))
1211 {
1212 const QList<QUrl> urls = event->mimeData()->urls();
1213 QApplication::sendEvent(this, new QCloseEvent());
1214 open_project_async(urls[0].toLocalFile());
1215 event->acceptProposedAction();
1216 }
1217 }
1218
start_rendering(const RenderingMode rendering_mode)1219 void MainWindow::start_rendering(const RenderingMode rendering_mode)
1220 {
1221 assert(m_project_manager.is_project_open());
1222
1223 // Don't start a new render until the previous has completely ended.
1224 if (m_rendering_manager.is_rendering())
1225 return;
1226
1227 m_false_colors_window.reset();
1228
1229 // Enable/disable menus and widgets appropriately.
1230 set_file_widgets_enabled(false, rendering_mode);
1231 set_project_explorer_enabled(rendering_mode == InteractiveRendering);
1232 set_rendering_widgets_enabled(true, rendering_mode);
1233 set_diagnostics_widgets_enabled(rendering_mode == InteractiveRendering, rendering_mode);
1234 m_ui->attribute_editor_scrollarea_contents->setEnabled(rendering_mode == InteractiveRendering);
1235
1236 // Remove light paths tab.
1237 remove_light_paths_tab();
1238
1239 // Stop monitoring the project file in Final rendering mode.
1240 if (rendering_mode == FinalRendering)
1241 {
1242 if (m_project_file_watcher)
1243 stop_monitoring_project_file();
1244 }
1245
1246 Project* project = m_project_manager.get_project();
1247 Frame* frame = project->get_frame();
1248
1249 frame->clear_main_and_aov_images();
1250
1251 // Darken render widgets.
1252 for (const_each<RenderTabCollection> i = m_render_tabs; i; ++i)
1253 {
1254 i->second->darken();
1255 i->second->update();
1256 }
1257
1258 // Retrieve the appropriate rendering configuration.
1259 const char* configuration_name =
1260 rendering_mode == InteractiveRendering ? "interactive" : "final";
1261 const ParamArray params = get_project_params(configuration_name);
1262
1263 // Effectively start rendering.
1264 m_rendering_manager.start_rendering(
1265 project,
1266 params,
1267 rendering_mode == InteractiveRendering
1268 ? RenderingManager::InteractiveRendering
1269 : RenderingManager::FinalRendering,
1270 m_render_tabs["RGB"]);
1271 }
1272
apply_false_colors_settings()1273 void MainWindow::apply_false_colors_settings()
1274 {
1275 Project* project = m_project_manager.get_project();
1276 assert(project != nullptr);
1277
1278 Frame* frame = project->get_frame();
1279 assert(frame != nullptr);
1280
1281 const ParamArray& false_colors_params = m_application_settings.child("false_colors");
1282 const bool false_colors_enabled = false_colors_params.get_optional<bool>("enabled", false);
1283
1284 if (false_colors_enabled)
1285 {
1286 // Make a temporary copy of the frame.
1287 // Render info, AOVs and other data are not copied.
1288 // todo: creating a frame with denoising enabled is very expensive, see benchmark_frame.cpp.
1289 auto_release_ptr<Frame> working_frame =
1290 FrameFactory::create(
1291 (string(frame->get_name()) + "_copy").c_str(),
1292 frame->get_parameters()
1293 .remove_path("denoiser"));
1294 working_frame->image().copy_from(frame->image());
1295
1296 // Create post-processing stage.
1297 auto_release_ptr<PostProcessingStage> stage(
1298 ColorMapPostProcessingStageFactory().create(
1299 "__false_colors_post_processing_stage",
1300 false_colors_params));
1301
1302 // Apply post-processing stage.
1303 apply_post_processing_stage(stage.ref(), working_frame.ref());
1304 }
1305 else
1306 {
1307 // Blit the regular frame into the render widget.
1308 for (const_each<RenderTabCollection> i = m_render_tabs; i; ++i)
1309 {
1310 i->second->get_render_widget()->blit_frame(*frame);
1311 i->second->get_render_widget()->update();
1312 }
1313 }
1314 }
1315
apply_post_processing_stage(PostProcessingStage & stage,Frame & working_frame)1316 void MainWindow::apply_post_processing_stage(
1317 PostProcessingStage& stage,
1318 Frame& working_frame)
1319 {
1320 Project* project = m_project_manager.get_project();
1321 assert(project != nullptr);
1322
1323 // Prepare the post-processing stage.
1324 OnFrameBeginRecorder recorder;
1325 if (stage.on_frame_begin(*project, nullptr, recorder, nullptr))
1326 {
1327 // Execute the post-processing stage.
1328 stage.execute(working_frame);
1329
1330 // Blit the frame copy into the render widget.
1331 for (const_each<RenderTabCollection> i = m_render_tabs; i; ++i)
1332 {
1333 i->second->get_render_widget()->blit_frame(working_frame);
1334 i->second->get_render_widget()->update();
1335 }
1336 }
1337 }
1338
1339 namespace
1340 {
ask_abort_rendering_confirmation(QWidget * parent)1341 int ask_abort_rendering_confirmation(QWidget* parent)
1342 {
1343 QMessageBox msgbox(parent);
1344 msgbox.setWindowTitle("Abort Rendering?");
1345 msgbox.setIcon(QMessageBox::Question);
1346 msgbox.setText("Rendering is in progress.");
1347 msgbox.setInformativeText("Do you want to abort rendering?");
1348 msgbox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1349 msgbox.setDefaultButton(QMessageBox::No);
1350 return msgbox.exec();
1351 }
1352 }
1353
closeEvent(QCloseEvent * event)1354 void MainWindow::closeEvent(QCloseEvent* event)
1355 {
1356 if (m_rendering_manager.is_rendering())
1357 {
1358 if (ask_abort_rendering_confirmation(this) != QMessageBox::Yes)
1359 {
1360 event->ignore();
1361 return;
1362 }
1363
1364 m_rendering_manager.abort_rendering();
1365 m_rendering_manager.wait_until_rendering_end();
1366 }
1367
1368 if (!can_close_project())
1369 {
1370 event->ignore();
1371 return;
1372 }
1373
1374 slot_save_application_settings();
1375
1376 if (m_test_window.get())
1377 m_test_window->close();
1378
1379 if (m_benchmark_window.get())
1380 m_benchmark_window->close();
1381
1382 remove_render_tabs();
1383
1384 m_project_manager.close_project();
1385
1386 event->accept();
1387 }
1388
slot_new_project()1389 void MainWindow::slot_new_project()
1390 {
1391 if (!can_close_project())
1392 return;
1393
1394 new_project();
1395 }
1396
slot_open_project()1397 void MainWindow::slot_open_project()
1398 {
1399 if (!can_close_project())
1400 return;
1401
1402 QString filepath =
1403 get_open_filename(
1404 this,
1405 "Open...",
1406 get_project_files_filter(),
1407 m_application_settings,
1408 SETTINGS_FILE_DIALOG_PROJECTS);
1409
1410 if (!filepath.isEmpty())
1411 {
1412 filepath = QDir::toNativeSeparators(filepath);
1413
1414 open_project_async(filepath);
1415 update_recent_files_menu(filepath);
1416 }
1417 }
1418
slot_open_recent()1419 void MainWindow::slot_open_recent()
1420 {
1421 if (!can_close_project())
1422 return;
1423
1424 QAction* action = qobject_cast<QAction*>(sender());
1425
1426 if (action)
1427 {
1428 const QString filepath = action->data().toString();
1429 open_project_async(filepath);
1430 }
1431 }
1432
slot_clear_all_recent_project_files()1433 void MainWindow::slot_clear_all_recent_project_files()
1434 {
1435 QSettings settings(SETTINGS_ORGANIZATION, SETTINGS_APPLICATION);
1436 settings.setValue("recent_file_list", QStringList());
1437
1438 update_recent_files_menu(QStringList());
1439 }
1440
slot_clear_recent_missing_project_files()1441 void MainWindow::slot_clear_recent_missing_project_files()
1442 {
1443 QSettings settings(SETTINGS_ORGANIZATION, SETTINGS_APPLICATION);
1444 QStringList files = settings.value("recent_file_list").toStringList();
1445 QStringList existing_files;
1446
1447 for (int i = 0; i < files.size(); i++)
1448 {
1449 if (QFileInfo(files[i]).isFile())
1450 existing_files << files[i];
1451 }
1452
1453 settings.setValue("recent_file_list", existing_files);
1454 update_recent_files_menu(existing_files);
1455 }
1456
slot_open_cornellbox_builtin_project()1457 void MainWindow::slot_open_cornellbox_builtin_project()
1458 {
1459 if (!can_close_project())
1460 return;
1461
1462 APPLESEED_UNUSED const bool successful = m_project_manager.load_builtin_project("cornell_box");
1463 assert(successful);
1464
1465 on_project_change();
1466 }
1467
slot_reload_project()1468 void MainWindow::slot_reload_project()
1469 {
1470 assert(m_project_manager.is_project_open());
1471 assert(m_project_manager.get_project()->has_path());
1472
1473 if (!can_close_project())
1474 return;
1475
1476 open_project_async(m_project_manager.get_project()->get_path());
1477 }
1478
1479 namespace
1480 {
show_project_file_loading_failed_message_box(QWidget * parent,const QString & filepath)1481 void show_project_file_loading_failed_message_box(QWidget* parent, const QString& filepath)
1482 {
1483 QMessageBox msgbox(parent);
1484 msgbox.setWindowTitle("Loading Error");
1485 msgbox.setIcon(QMessageBox::Critical);
1486 msgbox.setText("Failed to load the project file " + filepath + ".");
1487 msgbox.setInformativeText(
1488 "The project file may be invalid, corrupted or missing. "
1489 "Please look at the Log window for details.");
1490 msgbox.setStandardButtons(QMessageBox::Ok);
1491 msgbox.exec();
1492 }
1493 }
1494
slot_open_project_complete(const QString & filepath,const bool successful)1495 void MainWindow::slot_open_project_complete(const QString& filepath, const bool successful)
1496 {
1497 if (successful)
1498 on_project_change();
1499 else
1500 {
1501 show_project_file_loading_failed_message_box(this, filepath);
1502 recreate_render_tabs();
1503 update_workspace();
1504 }
1505 }
1506
slot_save_project()1507 void MainWindow::slot_save_project()
1508 {
1509 assert(m_project_manager.is_project_open());
1510
1511 if (!m_project_manager.get_project()->has_path())
1512 slot_save_project_as();
1513 else save_project(m_project_manager.get_project()->get_path());
1514 }
1515
slot_save_project_as()1516 void MainWindow::slot_save_project_as()
1517 {
1518 assert(m_project_manager.is_project_open());
1519
1520 QString filepath =
1521 get_save_filename(
1522 this,
1523 "Save As...",
1524 get_project_files_filter(ProjectFilesFilterPlainProjects),
1525 m_application_settings,
1526 SETTINGS_FILE_DIALOG_PROJECTS);
1527
1528 if (!filepath.isEmpty())
1529 {
1530 filepath = QDir::toNativeSeparators(filepath);
1531
1532 save_project(filepath);
1533 }
1534 }
1535
slot_pack_project_as()1536 void MainWindow::slot_pack_project_as()
1537 {
1538 assert(m_project_manager.is_project_open());
1539
1540 QString filepath =
1541 get_save_filename(
1542 this,
1543 "Pack As...",
1544 get_project_files_filter(ProjectFilesFilterPackedProjects),
1545 m_application_settings,
1546 SETTINGS_FILE_DIALOG_PROJECTS);
1547
1548 if (!filepath.isEmpty())
1549 {
1550 filepath = QDir::toNativeSeparators(filepath);
1551
1552 pack_project(filepath);
1553
1554 // Don't update the Recent Files menu.
1555 }
1556 }
1557
slot_close_project()1558 void MainWindow::slot_close_project()
1559 {
1560 if (!can_close_project())
1561 return;
1562
1563 close_project();
1564 }
1565
initialize_ocio()1566 void MainWindow::initialize_ocio()
1567 {
1568 // Try first a user specified OCIO config.
1569 if (getenv("OCIO"))
1570 {
1571 try
1572 {
1573 m_ocio_config = OCIO::GetCurrentConfig();
1574 RENDERER_LOG_INFO("using ocio configuration: %s", getenv("OCIO"));
1575 return;
1576 }
1577 catch (const OCIO::Exception&)
1578 {
1579 }
1580 }
1581
1582 // Try the bundled default OCIO config.
1583 const bf::path root_path(Application::get_root_path());
1584 const string default_ocio_config = (root_path / "ocio" / "config.ocio").string();
1585
1586 try
1587 {
1588 m_ocio_config = OCIO::Config::CreateFromFile(default_ocio_config.c_str());
1589 RENDERER_LOG_INFO("using ocio configuration: %s", default_ocio_config.c_str());
1590 OCIO::SetCurrentConfig(m_ocio_config);
1591 return;
1592 }
1593 catch (const OCIO::Exception&)
1594 {
1595 }
1596
1597 // Default to an empty OCIO config if everything else fails.
1598 m_ocio_config = OCIO::GetCurrentConfig();
1599 RENDERER_LOG_ERROR("could not find an ocio configuration, using empty configuration.");
1600 }
1601
slot_project_modified()1602 void MainWindow::slot_project_modified()
1603 {
1604 assert(m_project_manager.is_project_open());
1605
1606 m_project_manager.set_project_dirty_flag();
1607
1608 update_window_title();
1609 }
1610
slot_toggle_project_file_monitoring(const bool checked)1611 void MainWindow::slot_toggle_project_file_monitoring(const bool checked)
1612 {
1613 if (checked)
1614 enable_project_file_monitoring();
1615 else disable_project_file_monitoring();
1616
1617 m_application_settings.insert_path(
1618 SETTINGS_WATCH_FILE_CHANGES,
1619 m_project_file_watcher != nullptr);
1620 }
1621
slot_project_file_changed(const QString & filepath)1622 void MainWindow::slot_project_file_changed(const QString& filepath)
1623 {
1624 RENDERER_LOG_INFO("project file changed on disk, reloading it.");
1625
1626 assert(m_project_file_watcher);
1627 m_project_file_watcher->removePath(filepath);
1628
1629 slot_reload_project();
1630 }
1631
slot_load_application_settings()1632 void MainWindow::slot_load_application_settings()
1633 {
1634 const QSettings qt_settings(SETTINGS_ORGANIZATION, SETTINGS_APPLICATION);
1635 restoreGeometry(qt_settings.value("main_window_geometry").toByteArray());
1636 restoreState(qt_settings.value("main_window_state").toByteArray());
1637 m_ui->treewidget_project_explorer_scene->header()->restoreGeometry(
1638 qt_settings.value("main_window_project_explorer_geometry").toByteArray());
1639 m_ui->treewidget_project_explorer_scene->header()->restoreState(
1640 qt_settings.value("main_window_project_explorer_state").toByteArray());
1641
1642 Dictionary settings;
1643 if (Application::load_settings("appleseed.studio.xml", settings, global_logger(), LogMessage::Info))
1644 {
1645 m_application_settings = settings;
1646 slot_apply_application_settings();
1647 }
1648 }
1649
slot_save_application_settings()1650 void MainWindow::slot_save_application_settings()
1651 {
1652 QSettings settings(SETTINGS_ORGANIZATION, SETTINGS_APPLICATION);
1653 settings.setValue("main_window_geometry", saveGeometry());
1654 settings.setValue("main_window_state", saveState());
1655 settings.setValue("main_window_project_explorer_geometry",
1656 m_ui->treewidget_project_explorer_scene->header()->saveGeometry());
1657 settings.setValue("main_window_project_explorer_state",
1658 m_ui->treewidget_project_explorer_scene->header()->saveState());
1659
1660 Application::save_settings("appleseed.studio.xml", m_application_settings, global_logger(), LogMessage::Info);
1661 }
1662
slot_apply_application_settings()1663 void MainWindow::slot_apply_application_settings()
1664 {
1665 if (m_application_settings.strings().exist(SETTINGS_MESSAGE_VERBOSITY))
1666 {
1667 const char* level_name = m_application_settings.get(SETTINGS_MESSAGE_VERBOSITY);
1668 const LogMessage::Category level = LogMessage::get_category_value(level_name);
1669
1670 if (level < LogMessage::NumMessageCategories)
1671 global_logger().set_verbosity_level(level);
1672 else RENDERER_LOG_ERROR("invalid message verbosity level \"%s\".", level_name);
1673 }
1674
1675 if (m_application_settings.get_optional<bool>(SETTINGS_WATCH_FILE_CHANGES))
1676 {
1677 m_action_monitor_project_file->setChecked(true);
1678 enable_project_file_monitoring();
1679 }
1680 else
1681 {
1682 m_action_monitor_project_file->setChecked(false);
1683 disable_project_file_monitoring();
1684 }
1685
1686 emit signal_application_settings_modified();
1687 }
1688
slot_start_interactive_rendering()1689 void MainWindow::slot_start_interactive_rendering()
1690 {
1691 start_rendering(InteractiveRendering);
1692 }
1693
slot_start_final_rendering()1694 void MainWindow::slot_start_final_rendering()
1695 {
1696 start_rendering(FinalRendering);
1697 }
1698
slot_start_rendering_once(const QString & filepath,const QString & configuration,const bool successful)1699 void MainWindow::slot_start_rendering_once(const QString& filepath, const QString& configuration, const bool successful)
1700 {
1701 sender()->deleteLater();
1702
1703 if (successful)
1704 {
1705 if (configuration == "interactive")
1706 start_rendering(InteractiveRendering);
1707 else start_rendering(FinalRendering);
1708 }
1709 }
1710
slot_pause_or_resume_rendering(const bool checked)1711 void MainWindow::slot_pause_or_resume_rendering(const bool checked)
1712 {
1713 if (checked)
1714 {
1715 assert(!m_rendering_manager.is_rendering_paused());
1716 m_rendering_manager.pause_rendering();
1717 }
1718 else
1719 {
1720 m_rendering_manager.resume_rendering();
1721 }
1722
1723 update_pause_resume_checkbox(checked);
1724 }
1725
slot_rendering_end()1726 void MainWindow::slot_rendering_end()
1727 {
1728 apply_false_colors_settings();
1729
1730 update_workspace();
1731
1732 // Restart monitoring the project file if monitoring was enabled
1733 // (monitoring would have been stopped if rendering in Final mode).
1734 if (m_project_file_watcher)
1735 start_monitoring_project_file();
1736 }
1737
slot_camera_changed()1738 void MainWindow::slot_camera_changed()
1739 {
1740 m_project_manager.set_project_dirty_flag();
1741 }
1742
1743 namespace
1744 {
1745 class ClearShadingOverrideAction
1746 : public RenderingManager::IStickyAction
1747 {
1748 public:
operator ()(MasterRenderer & master_renderer,Project & project)1749 void operator()(
1750 MasterRenderer& master_renderer,
1751 Project& project) override
1752 {
1753 master_renderer.get_parameters()
1754 .push("shading_engine")
1755 .dictionaries().remove("override_shading");
1756 }
1757 };
1758
1759 class SetShadingOverrideAction
1760 : public RenderingManager::IStickyAction
1761 {
1762 public:
SetShadingOverrideAction(const string & shading_mode)1763 explicit SetShadingOverrideAction(const string& shading_mode)
1764 : m_shading_mode(shading_mode)
1765 {
1766 }
1767
operator ()(MasterRenderer & master_renderer,Project & project)1768 void operator()(
1769 MasterRenderer& master_renderer,
1770 Project& project) override
1771 {
1772 master_renderer.get_parameters()
1773 .push("shading_engine")
1774 .push("override_shading")
1775 .insert("mode", m_shading_mode);
1776 }
1777
1778 private:
1779 const string m_shading_mode;
1780 };
1781 }
1782
slot_clear_shading_override()1783 void MainWindow::slot_clear_shading_override()
1784 {
1785 m_rendering_manager.set_sticky_action(
1786 "override_shading",
1787 unique_ptr<RenderingManager::IStickyAction>(
1788 new ClearShadingOverrideAction()));
1789
1790 m_rendering_manager.reinitialize_rendering();
1791 }
1792
slot_set_shading_override()1793 void MainWindow::slot_set_shading_override()
1794 {
1795 QAction* action = qobject_cast<QAction*>(sender());
1796 const string shading_mode = action->data().toString().toStdString();
1797
1798 m_rendering_manager.set_sticky_action(
1799 "override_shading",
1800 unique_ptr<RenderingManager::IStickyAction>(
1801 new SetShadingOverrideAction(shading_mode)));
1802
1803 m_rendering_manager.reinitialize_rendering();
1804 }
1805
slot_show_false_colors_window()1806 void MainWindow::slot_show_false_colors_window()
1807 {
1808 if (m_false_colors_window.get() == nullptr)
1809 {
1810 m_false_colors_window.reset(new FalseColorsWindow(this));
1811
1812 QObject::connect(
1813 m_false_colors_window.get(), SIGNAL(signal_applied(foundation::Dictionary)),
1814 SLOT(slot_apply_false_colors_settings_changes(foundation::Dictionary)));
1815
1816 QObject::connect(
1817 m_false_colors_window.get(), SIGNAL(signal_accepted(foundation::Dictionary)),
1818 SLOT(slot_apply_false_colors_settings_changes(foundation::Dictionary)));
1819
1820 QObject::connect(
1821 m_false_colors_window.get(), SIGNAL(signal_canceled(foundation::Dictionary)),
1822 SLOT(slot_apply_false_colors_settings_changes(foundation::Dictionary)));
1823 }
1824
1825 Project* project = m_project_manager.get_project();
1826 assert(project);
1827
1828 m_false_colors_window->initialize(
1829 *project,
1830 m_application_settings,
1831 m_application_settings.child("false_colors"));
1832
1833 m_false_colors_window->showNormal();
1834 m_false_colors_window->activateWindow();
1835 }
1836
slot_apply_false_colors_settings_changes(Dictionary values)1837 void MainWindow::slot_apply_false_colors_settings_changes(Dictionary values)
1838 {
1839 m_application_settings.push("false_colors").merge(values);
1840 apply_false_colors_settings();
1841 }
1842
1843 namespace
1844 {
1845 class ClearRenderRegionAction
1846 : public RenderingManager::IScheduledAction
1847 {
1848 public:
ClearRenderRegionAction(const AttributeEditor * attribute_editor)1849 explicit ClearRenderRegionAction(const AttributeEditor* attribute_editor)
1850 : m_attribute_editor(attribute_editor)
1851 {
1852 }
1853
operator ()(Project & project)1854 void operator()(
1855 Project& project) override
1856 {
1857 project.get_frame()->reset_crop_window();
1858
1859 m_attribute_editor->refresh();
1860 }
1861
1862 private:
1863 const AttributeEditor* m_attribute_editor;
1864 };
1865
1866 class SetRenderRegionAction
1867 : public RenderingManager::IScheduledAction
1868 {
1869 public:
SetRenderRegionAction(const QRect & rect,const AttributeEditor * attribute_editor)1870 SetRenderRegionAction(
1871 const QRect& rect,
1872 const AttributeEditor* attribute_editor)
1873 : m_rect(rect),
1874 m_attribute_editor(attribute_editor)
1875 {
1876 }
1877
operator ()(Project & project)1878 void operator()(
1879 Project& project) override
1880 {
1881 const int w = m_rect.width();
1882 const int h = m_rect.height();
1883 const int x0 = m_rect.x();
1884 const int y0 = m_rect.y();
1885 const int x1 = x0 + w - 1;
1886 const int y1 = y0 + h - 1;
1887
1888 assert(x0 >= 0);
1889 assert(y0 >= 0);
1890 assert(x0 <= x1);
1891 assert(y0 <= y1);
1892
1893 project.get_frame()->set_crop_window(
1894 AABB2i(
1895 Vector2i(x0, y0),
1896 Vector2i(x1, y1)));
1897
1898 m_attribute_editor->refresh();
1899 }
1900
1901 private:
1902 const QRect m_rect;
1903 const AttributeEditor* m_attribute_editor;
1904 };
1905 }
1906
slot_clear_render_region()1907 void MainWindow::slot_clear_render_region()
1908 {
1909 unique_ptr<RenderingManager::IScheduledAction> clear_render_region_action(
1910 new ClearRenderRegionAction(m_attribute_editor));
1911
1912 if (m_rendering_manager.is_rendering())
1913 m_rendering_manager.schedule(std::move(clear_render_region_action));
1914 else clear_render_region_action.get()->operator()(*m_project_manager.get_project());
1915
1916 m_rendering_manager.reinitialize_rendering();
1917 }
1918
slot_set_render_region(const QRect & rect)1919 void MainWindow::slot_set_render_region(const QRect& rect)
1920 {
1921 unique_ptr<RenderingManager::IScheduledAction> set_render_region_action(
1922 new SetRenderRegionAction(rect, m_attribute_editor));
1923
1924 if (!m_rendering_manager.is_rendering())
1925 {
1926 set_render_region_action.get()->operator()(*m_project_manager.get_project());
1927
1928 if (m_application_settings.get_path_optional<bool>(SETTINGS_RENDER_REGION_TRIGGERS_RENDERING))
1929 start_rendering(InteractiveRendering);
1930 }
1931 else
1932 {
1933 m_rendering_manager.schedule(std::move(set_render_region_action));
1934 m_rendering_manager.reinitialize_rendering();
1935 }
1936 }
1937
slot_render_widget_context_menu(const QPoint & point)1938 void MainWindow::slot_render_widget_context_menu(const QPoint& point)
1939 {
1940 if (!(QApplication::keyboardModifiers() & Qt::ShiftModifier))
1941 return;
1942
1943 if (m_rendering_manager.is_rendering())
1944 return;
1945
1946 QMenu* menu = new QMenu(this);
1947 menu->addAction("Save Frame...", this, SLOT(slot_save_frame()));
1948 menu->addAction("Save Frame and AOVs...", this, SLOT(slot_save_frame_and_aovs()));
1949 menu->addSeparator();
1950 menu->addAction("Save Render Widget Content...", this, SLOT(slot_save_render_widget_content()));
1951 menu->addSeparator();
1952 menu->addAction("Clear All", this, SLOT(slot_clear_frame()));
1953 menu->exec(point);
1954 }
1955
1956 namespace
1957 {
ask_frame_save_file_path(QWidget * parent,const QString & caption,const QString & filter,const QString & default_ext,ParamArray & settings)1958 QString ask_frame_save_file_path(
1959 QWidget* parent,
1960 const QString& caption,
1961 const QString& filter,
1962 const QString& default_ext,
1963 ParamArray& settings)
1964 {
1965 QString filepath =
1966 get_save_filename(
1967 parent,
1968 caption,
1969 filter,
1970 settings,
1971 SETTINGS_FILE_DIALOG_FRAMES);
1972
1973 if (!filepath.isEmpty())
1974 {
1975 if (QFileInfo(filepath).suffix().isEmpty())
1976 filepath += default_ext;
1977
1978 filepath = QDir::toNativeSeparators(filepath);
1979 }
1980
1981 return filepath;
1982 }
1983 }
1984
slot_save_frame()1985 void MainWindow::slot_save_frame()
1986 {
1987 assert(m_project_manager.is_project_open());
1988 assert(!m_rendering_manager.is_rendering());
1989
1990 const QString filepath =
1991 ask_frame_save_file_path(
1992 this,
1993 "Save Frame As...",
1994 get_oiio_image_files_filter(),
1995 ".exr",
1996 m_application_settings);
1997
1998 if (filepath.isEmpty())
1999 return;
2000
2001 const Frame* frame = m_project_manager.get_project()->get_frame();
2002 frame->write_main_image(filepath.toUtf8().constData());
2003 }
2004
slot_save_frame_and_aovs()2005 void MainWindow::slot_save_frame_and_aovs()
2006 {
2007 assert(m_project_manager.is_project_open());
2008 assert(!m_rendering_manager.is_rendering());
2009
2010 const QString filepath =
2011 ask_frame_save_file_path(
2012 this,
2013 "Save Frame and AOVs As...",
2014 get_oiio_image_files_filter(),
2015 ".exr",
2016 m_application_settings);
2017
2018 if (filepath.isEmpty())
2019 return;
2020
2021 const Frame* frame = m_project_manager.get_project()->get_frame();
2022 frame->write_main_image(filepath.toUtf8().constData());
2023 frame->write_aov_images(filepath.toUtf8().constData());
2024 }
2025
2026 namespace
2027 {
write_main_and_aov_images(const Project & project,const bf::path & image_path)2028 void write_main_and_aov_images(
2029 const Project& project,
2030 const bf::path& image_path)
2031 {
2032 bf::create_directories(image_path.parent_path());
2033
2034 const Frame* frame = project.get_frame();
2035 frame->write_main_image(image_path.string().c_str());
2036 frame->write_aov_images(image_path.string().c_str());
2037 }
2038 }
2039
slot_quicksave_frame_and_aovs()2040 void MainWindow::slot_quicksave_frame_and_aovs()
2041 {
2042 assert(m_project_manager.is_project_open());
2043 assert(!m_rendering_manager.is_rendering());
2044
2045 const Project& project = *m_project_manager.get_project();
2046
2047 const bf::path project_path(project.get_path());
2048 const bf::path quicksave_dir = project_path.parent_path() / "quicksaves";
2049
2050 write_main_and_aov_images(
2051 project,
2052 bf::absolute(
2053 quicksave_dir / "quicksave.exr"));
2054
2055 write_main_and_aov_images(
2056 project,
2057 bf::absolute(
2058 find_next_available_path(quicksave_dir / "quicksave####.exr")));
2059 }
2060
slot_save_render_widget_content()2061 void MainWindow::slot_save_render_widget_content()
2062 {
2063 assert(m_project_manager.is_project_open());
2064 assert(!m_rendering_manager.is_rendering());
2065
2066 const QString filepath =
2067 ask_frame_save_file_path(
2068 this,
2069 "Save Render Widget Content As...",
2070 g_qt_image_files_filter,
2071 ".png",
2072 m_application_settings);
2073
2074 if (filepath.isEmpty())
2075 return;
2076
2077 // todo: this is sketchy. The render tab should be retrieved from the signal.
2078 m_render_tabs["RGB"]->get_render_widget()->capture().save(filepath);
2079
2080 RENDERER_LOG_INFO("wrote image file %s.", filepath.toStdString().c_str());
2081 }
2082
slot_clear_frame()2083 void MainWindow::slot_clear_frame()
2084 {
2085 Frame* frame = m_project_manager.get_project()->get_frame();
2086 frame->clear_main_and_aov_images();
2087
2088 // In the UI, clear all render widgets to black.
2089 for (const_each<RenderTabCollection> i = m_render_tabs; i; ++i)
2090 i->second->clear();
2091 }
2092
slot_reset_zoom()2093 void MainWindow::slot_reset_zoom()
2094 {
2095 const int current_tab_index = m_ui->tab_render_channels->currentIndex();
2096 const auto render_tab_it = m_tab_index_to_render_tab.find(current_tab_index);
2097 if (render_tab_it != m_tab_index_to_render_tab.end())
2098 render_tab_it->second->reset_zoom();
2099 }
2100
slot_filter_text_changed(const QString & pattern)2101 void MainWindow::slot_filter_text_changed(const QString& pattern)
2102 {
2103 m_ui->pushbutton_clear_filter->setEnabled(!pattern.isEmpty());
2104 m_project_explorer->filter_items(pattern);
2105 }
2106
slot_clear_filter()2107 void MainWindow::slot_clear_filter()
2108 {
2109 m_ui->lineedit_filter->clear();
2110 }
2111
slot_frame_modified()2112 void MainWindow::slot_frame_modified()
2113 {
2114 for (each<RenderTabCollection> i = m_render_tabs; i; ++i)
2115 i->second->update_size();
2116 }
2117
slot_fullscreen()2118 void MainWindow::slot_fullscreen()
2119 {
2120 m_fullscreen = !m_fullscreen;
2121
2122 bool all_minimized = true;
2123 bool not_minimized = false;
2124 for (const MinimizeButton* button : m_minimize_buttons)
2125 {
2126 all_minimized = all_minimized && button->is_on();
2127 not_minimized = not_minimized || !button->is_on();
2128 }
2129
2130 // All were manually minimized, exit full screen mode.
2131 if (all_minimized)
2132 m_fullscreen = false;
2133
2134 // At least one is on screen, enter full screen mode.
2135 if (not_minimized)
2136 m_fullscreen = true;
2137
2138 for (MinimizeButton* button : m_minimize_buttons)
2139 button->set_fullscreen(m_fullscreen);
2140 }
2141
slot_check_fullscreen()2142 void MainWindow::slot_check_fullscreen()
2143 {
2144 const QList<QDockWidget*> dock_widgets = findChildren<QDockWidget*>();
2145
2146 const bool is_fullscreen = all_of(dock_widgets.cbegin(),
2147 dock_widgets.cend(),
2148 [](QDockWidget* dock) {return dock->isHidden();});
2149
2150 m_action_fullscreen->setChecked(is_fullscreen);
2151 }
2152
slot_show_application_settings_window()2153 void MainWindow::slot_show_application_settings_window()
2154 {
2155 if (m_application_settings_window.get() == nullptr)
2156 {
2157 m_application_settings_window.reset(
2158 new ApplicationSettingsWindow(m_application_settings, this));
2159
2160 connect(
2161 m_application_settings_window.get(), SIGNAL(signal_application_settings_modified()),
2162 SLOT(slot_save_application_settings()));
2163
2164 connect(
2165 m_application_settings_window.get(), SIGNAL(signal_application_settings_modified()),
2166 SLOT(slot_apply_application_settings()));
2167
2168 connect(
2169 this, SIGNAL(signal_application_settings_modified()),
2170 m_application_settings_window.get(), SLOT(slot_reload_application_settings()));
2171 }
2172
2173 m_application_settings_window->showNormal();
2174 m_application_settings_window->activateWindow();
2175 }
2176
slot_show_rendering_settings_window()2177 void MainWindow::slot_show_rendering_settings_window()
2178 {
2179 assert(m_project_manager.is_project_open());
2180
2181 if (m_rendering_settings_window.get() == nullptr)
2182 {
2183 m_rendering_settings_window.reset(
2184 new RenderingSettingsWindow(
2185 m_project_manager,
2186 m_application_settings,
2187 this));
2188
2189 connect(
2190 m_rendering_settings_window.get(), SIGNAL(signal_rendering_settings_modified()),
2191 SLOT(slot_project_modified()));
2192
2193 connect(
2194 this, SIGNAL(signal_application_settings_modified()),
2195 m_rendering_settings_window.get(), SLOT(slot_reload_application_settings()));
2196 }
2197
2198 m_rendering_settings_window->showNormal();
2199 m_rendering_settings_window->activateWindow();
2200 }
2201
slot_show_test_window()2202 void MainWindow::slot_show_test_window()
2203 {
2204 if (m_test_window.get() == nullptr)
2205 m_test_window.reset(new TestWindow(this));
2206
2207 m_test_window->showNormal();
2208 m_test_window->activateWindow();
2209 }
2210
slot_show_benchmark_window()2211 void MainWindow::slot_show_benchmark_window()
2212 {
2213 if (m_benchmark_window.get() == nullptr)
2214 m_benchmark_window.reset(new BenchmarkWindow(this));
2215
2216 m_benchmark_window->showNormal();
2217 m_benchmark_window->activateWindow();
2218 }
2219
slot_show_about_window()2220 void MainWindow::slot_show_about_window()
2221 {
2222 AboutWindow* about_window = new AboutWindow(this);
2223 about_window->showNormal();
2224 about_window->activateWindow();
2225 }
2226
2227 } // namespace studio
2228 } // namespace appleseed
2229
2230 #include "mainwindow/moc_cpp_mainwindow.cxx"
2231