2 * This file is part of the SmuView project.
4 * Copyright (C) 2017-2021 Frank Stettner <frank-stettner@gmx.net>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include <QApplication>
26 #include <QDockWidget>
27 #include <QFontDatabase>
28 #include <QHBoxLayout>
30 #include <QMessageBox>
31 #include <QSizePolicy>
32 #include <QVBoxLayout>
34 #include "mainwindow.hpp"
36 #include "src/devicemanager.hpp"
37 #include "src/session.hpp"
38 #include "src/settingsmanager.hpp"
39 #include "src/util.hpp"
40 #include "src/data/basesignal.hpp"
41 #include "src/data/analogtimesignal.hpp"
42 #include "src/devices/basedevice.hpp"
43 #include "src/devices/configurable.hpp"
44 #include "src/devices/deviceutil.hpp"
45 #include "src/devices/hardwaredevice.hpp"
46 #include "src/devices/measurementdevice.hpp"
47 #include "src/devices/sourcesinkdevice.hpp"
48 #include "src/devices/userdevice.hpp"
49 #include "src/channels/basechannel.hpp"
50 #include "src/python/smuscriptrunner.hpp"
51 #include "src/ui/dialogs/connectdialog.hpp"
52 #include "src/ui/tabs/basetab.hpp"
53 #include "src/ui/tabs/devicetab.hpp"
54 #include "src/ui/tabs/smuscripttab.hpp"
55 #include "src/ui/tabs/tabhelper.hpp"
56 #include "src/ui/tabs/welcometab.hpp"
57 #include "src/ui/views/devicesview.hpp"
58 #include "src/ui/views/smuscripttreeview.hpp"
61 using std::shared_ptr
;
64 Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr
)
65 Q_DECLARE_METATYPE(std::shared_ptr
<sv::devices::BaseDevice
>)
66 Q_DECLARE_METATYPE(std::shared_ptr
<sv::channels::BaseChannel
>)
67 Q_DECLARE_METATYPE(std::shared_ptr
<sv::data::BaseSignal
>)
72 MainWindow::MainWindow(DeviceManager
&device_manager
,
73 shared_ptr
<Session
> session
, QWidget
*parent
) :
75 device_manager_(device_manager
),
78 qRegisterMetaType
<util::Timestamp
>("util::Timestamp");
79 qRegisterMetaType
<uint64_t>("uint64_t");
80 qRegisterMetaType
<std::string
>("std::string");
81 qRegisterMetaType
<Qt::DockWidgetArea
>("Qt::DockWidgetArea");
82 qRegisterMetaType
<shared_ptr
<devices::BaseDevice
>>("shared_ptr<sv::devices::BaseDevice>");
83 qRegisterMetaType
<shared_ptr
<devices::Configurable
>>("shared_ptr<sv::devices::Configurable>");
84 qRegisterMetaType
<shared_ptr
<devices::HardwareDevice
>>("shared_ptr<sv::devices::HardwareDevice>");
85 qRegisterMetaType
<shared_ptr
<channels::BaseChannel
>>("shared_ptr<sv::channels::BaseChannel>");
86 qRegisterMetaType
<shared_ptr
<data::BaseSignal
>>("shared_ptr<sv::data::BaseSignal>");
87 qRegisterMetaType
<shared_ptr
<data::AnalogTimeSignal
>>("shared_ptr<sv::data::AnalogTimeSignal>");
88 qRegisterMetaType
<devices::ConfigKey
>("devices::ConfigKey");
90 // Add embedded mono space font for the value display.
91 QFontDatabase::addApplicationFont(":/fonts/DejaVuSansMono.ttf");
93 session_
->set_main_window(this);
96 if (SettingsManager::restore_settings())
102 MainWindow::~MainWindow()
106 void MainWindow::add_tab(ui::tabs::BaseTab
*tab_window
)
108 // NOTE: This must be before QTabWidget::addTab() and
109 // QTabWidget::setCurrentIndex() otherwise the tab_window is null for
110 // the SmuScriptTab after inserting into tab_window_map_.
111 tab_window_map_
.insert(make_pair(tab_window
->id(), tab_window
));
113 int index
= tab_widget_
->addTab(tab_window
, tab_window
->title());
114 tab_widget_
->setCurrentIndex(index
);
117 ui::tabs::DeviceTab
*MainWindow::add_device_tab(
118 shared_ptr
<sv::devices::BaseDevice
> device
)
120 auto *tab
= ui::tabs::tabhelper::get_tab_for_device(*session_
, device
);
123 // Connect device error handler to show a message box
124 connect(device
.get(), &sv::devices::BaseDevice::device_error
,
125 this, &MainWindow::error_handler
);
130 ui::tabs::WelcomeTab
*MainWindow::add_welcome_tab()
132 auto *tab
= new ui::tabs::WelcomeTab(*session_
);
137 ui::tabs::SmuScriptTab
*MainWindow::add_smuscript_tab(const string
&file_name
)
139 auto *tab
= new ui::tabs::SmuScriptTab(*session_
, file_name
);
144 void MainWindow::remove_tab(const string
&tab_id
)
146 if (tab_window_map_
.count(tab_id
) == 0)
149 remove_tab(tab_widget_
->indexOf(tab_window_map_
[tab_id
]));
152 void MainWindow::remove_tab(int tab_index
)
154 QWidget
*tab_window
= tab_widget_
->widget(tab_index
);
156 tab_widget_
->removeTab(tab_index
);
158 for (const auto &pair
: tab_window_map_
) {
159 if (pair
.second
== tab_window
) {
160 tab_window_map_
.erase(pair
.first
);
165 // Call close() of the tab window to save settings (*Tab::closeEvent())
167 tab_window
->deleteLater();
169 if (tab_window_map_
.empty()) {
170 // When there are no more tabs, display the WelcomeTab
175 void MainWindow::change_tab_icon(const string
&tab_id
, const QIcon
&icon
)
177 int tab_index
= tab_widget_
->indexOf(tab_window_map_
[tab_id
]);
178 tab_widget_
->setTabIcon(tab_index
, icon
);
181 void MainWindow::change_tab_title(const string
&tab_id
, const QString
&title
)
183 int tab_index
= tab_widget_
->indexOf(tab_window_map_
[tab_id
]);
184 tab_widget_
->setTabText(tab_index
, title
);
187 ui::tabs::BaseTab
*MainWindow::get_tab_from_tab_id(const string
&id
)
189 if (tab_window_map_
.count(id
) == 0)
192 return tab_window_map_
[id
];
195 void MainWindow::setup_ui()
198 mainIcon
.addFile(QStringLiteral(":/icons/smuview.ico"),
199 QSize(), QIcon::Normal
, QIcon::Off
);
200 this->setWindowIcon(mainIcon
);
202 QString window_title
= QString("%1 %2").
203 arg(tr("SmuView"), SV_VERSION_STRING
);
204 this->setWindowTitle(window_title
);
206 QHBoxLayout
*centralLayout
= new QHBoxLayout();
207 centralLayout
->setContentsMargins(2, 2, 2, 2);
208 central_widget_
= new QWidget();
209 central_widget_
->setLayout(centralLayout
);
212 tab_widget_
= new QTabWidget();
213 tab_widget_
->setTabsClosable(true);
214 connect(tab_widget_
, &QTabWidget::tabCloseRequested
,
215 this, &MainWindow::on_tab_close_requested
);
216 centralLayout
->addWidget(tab_widget_
);
218 this->setCentralWidget(central_widget_
);
220 // DeviceTreeView Dock
221 devices_view_
= new ui::views::DevicesView(*session_
);
222 devices_view_
->setSizePolicy(
223 QSizePolicy::MinimumExpanding
, QSizePolicy::Expanding
);
225 // A layout must be set to the central widget of the main window
226 // before dev_dock->setWidget() is called.
227 QDockWidget
* dev_dock
= new QDockWidget(devices_view_
->title());
228 dev_dock
->setObjectName("dev_doc");
229 dev_dock
->setAllowedAreas(Qt::AllDockWidgetAreas
);
230 dev_dock
->setContextMenuPolicy(Qt::PreventContextMenu
);
231 dev_dock
->setFeatures(QDockWidget::DockWidgetMovable
|
232 QDockWidget::DockWidgetFloatable
);
233 dev_dock
->setWidget(devices_view_
);
234 this->addDockWidget(Qt::LeftDockWidgetArea
, dev_dock
);
236 // This fixes a qt bug. See: https://bugreports.qt.io/browse/QTBUG-65592
237 this->resizeDocks({dev_dock
}, {40}, Qt::Horizontal
);
239 // SmuScript Tree Dock
240 smu_script_tree_view_
= new ui::views::SmuScriptTreeView(*session_
);
242 QDockWidget
* script_dock
= new QDockWidget(smu_script_tree_view_
->title());
243 script_dock
->setObjectName("script_dock");
244 script_dock
->setAllowedAreas(Qt::AllDockWidgetAreas
);
245 script_dock
->setContextMenuPolicy(Qt::PreventContextMenu
);
246 script_dock
->setFeatures(QDockWidget::DockWidgetMovable
|
247 QDockWidget::DockWidgetFloatable
);
248 script_dock
->setWidget(smu_script_tree_view_
);
249 this->tabifyDockWidget(dev_dock
, script_dock
);
251 // Select device tree dock tab
256 void MainWindow::init_device_tabs()
258 if (device_manager_
.user_spec_devices().empty()) {
259 // Display the WelcomeTab if no DeviceTabs will be opened, because
260 // without a tab in the QTabWidget the main window looks so empty...
265 for (const auto &device
: device_manager_
.user_spec_devices()) {
266 add_device_tab(device
);
270 void MainWindow::connect_signals()
272 // Connect error handlers
273 connect(session_
->smu_script_runner().get(), &python::SmuScriptRunner::script_error
,
274 this, &MainWindow::error_handler
);
277 void MainWindow::save_settings()
281 smu_script_tree_view_
->save_settings(settings
);
283 settings
.beginGroup("MainWindow");
284 settings
.setValue("geometry", saveGeometry());
285 settings
.setValue("state", saveState());
289 void MainWindow::restore_settings()
293 // Restore main window stuff
294 settings
.beginGroup("MainWindow");
295 if (settings
.contains("geometry")) {
296 restoreGeometry(settings
.value("geometry").toByteArray());
297 restoreState(settings
.value("state").toByteArray());
304 void MainWindow::error_handler(
305 const std::string
&sender
, const std::string
&msg
)
307 QMessageBox
msg_box(this);
308 msg_box
.setText(QString::fromStdString(sender
));
309 msg_box
.setInformativeText(QString::fromStdString(msg
));
310 msg_box
.setStandardButtons(QMessageBox::Ok
);
311 msg_box
.setIcon(QMessageBox::Critical
);
315 void MainWindow::on_tab_close_requested(int tab_index
)
317 auto *tab_window
= (ui::tabs::BaseTab
*)tab_widget_
->widget(tab_index
);
318 if (tab_window
->request_close())
319 remove_tab(tab_index
);
322 void MainWindow::closeEvent(QCloseEvent
*event
)
324 for (const auto &tab_window_pair
: this->tab_window_map_
) {
325 // Call close() of the tab window to save settings (*Tab::closeEvent())
326 tab_window_pair
.second
->close();