find_package(ECM 1.0.0 NO_MODULE)
if(Qt5_FOUND AND ECM_FOUND)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
- find_package(KF5 OPTIONAL_COMPONENTS CoreAddons I18n ItemModels ThreadWeaver ConfigWidgets)
+ find_package(KF5 OPTIONAL_COMPONENTS CoreAddons I18n ItemModels ThreadWeaver ConfigWidgets KIO)
find_package(KChart "2.6.0")
set_package_properties(KChart PROPERTIES TYPE RECOMMENDED PURPOSE "Required for the heaptrack_gui executable. Get it from the kdiagram module.")
if (KF5_FOUND AND KChart_FOUND)
KF5::ThreadWeaver
KF5::ConfigWidgets
KF5::I18n
+ KF5::KIOWidgets
KChart
sharedprint)
#include <cmath>
#include <KRecursiveFilterProxyModel>
-#include <KStandardAction>
#include <KLocalizedString>
#include <QFileDialog>
this, [this, removeProgress] (const QString& failedFile) {
removeProgress();
m_ui->pages->setCurrentWidget(m_ui->openPage);
- m_ui->messages->setText(i18n("Failed to parse file %1.", failedFile));
- m_ui->messages->show();
+ showError(i18n("Failed to parse file %1.", failedFile));
});
m_ui->messages->hide();
connect(m_ui->callerCalleeFilterModule, &QLineEdit::textChanged,
bottomUpProxy, &TreeProxy::setModuleFilter);
- auto openFile = KStandardAction::open(this, SLOT(openFile()), this);
- m_ui->openFile->setDefaultAction(openFile);
+ auto validateInputFile = [this] (const QString &path, bool allowEmpty) -> bool {
+ if (path.isEmpty()) {
+ return allowEmpty;
+ }
+
+ const auto file = QFileInfo(path);
+ if (!file.exists()) {
+ showError(i18n("Input data %1 does not exist.", path));
+ } else if (!file.isFile()) {
+ showError(i18n("Input data %1 is not a file.", path));
+ } else if (!file.isReadable()) {
+ showError(i18n("Input data %1 is not readable.", path));
+ } else {
+ return true;
+ }
+ return false;
+ };
+
+ auto validateInput = [this, validateInputFile] () {
+ m_ui->messages->hide();
+ m_ui->buttonBox->setEnabled(
+ validateInputFile(m_ui->openFile->url().toLocalFile(), false)
+ && validateInputFile(m_ui->compareTo->url().toLocalFile(), true)
+ );
+ };
+
+ connect(m_ui->openFile, &KUrlRequester::textChanged,
+ this, validateInput);
+ connect(m_ui->compareTo, &KUrlRequester::textChanged,
+ this, validateInput);
+ connect(m_ui->buttonBox, &QDialogButtonBox::accepted,
+ this, [this] () {
+ const auto path = m_ui->openFile->url().toLocalFile();
+ Q_ASSERT(!path.isEmpty());
+ const auto base = m_ui->compareTo->url().toLocalFile();
+ loadFile(path, base);
+ });
setupStacks();
void MainWindow::loadFile(const QString& file, const QString& diffBase)
{
m_ui->loadingLabel->setText(i18n("Loading file %1, please wait...", file));
- setWindowTitle(i18nc("%1: file name that is open", "Heaptrack - %1", file));
+ if (diffBase.isEmpty()) {
+ setWindowTitle(i18nc("%1: file name that is open", "Heaptrack - %1",
+ QFileInfo(file).fileName()));
+ } else {
+ setWindowTitle(i18nc("%1, %2: file names that are open", "Heaptrack - %1 compared to %2",
+ QFileInfo(file).fileName(), QFileInfo(diffBase).fileName()));
+ }
m_ui->pages->setCurrentWidget(m_ui->loadingPage);
m_parser->parse(file, diffBase);
}
dialog->show();
}
+void MainWindow::showError(const QString &message)
+{
+ m_ui->messages->setText(message);
+ m_ui->messages->show();
+}
+
void MainWindow::setupStacks()
{
auto stacksModel = new StacksModel(this);
void openFile();
private:
+ void showError(const QString &message);
void setupStacks();
QScopedPointer<Ui::MainWindow> m_ui;
<item>
<widget class="QStackedWidget" name="pages">
<widget class="QWidget" name="openPage">
- <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <layout class="QVBoxLayout" name="verticalLayout_15">
<item>
- <widget class="QToolButton" name="openFile">
- <property name="text">
- <string notr="true">...</string>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Open Heaptrack Data</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="1" column="1">
+ <widget class="KUrlRequester" name="openFile">
+ <property name="toolTip">
+ <string><qt><p>This field specifies the primary heaptrack data file. These files are called <tt>heaptrack.$APP.$PID.gz</tt>. You can produce such a file by profiling your application, e.g. via:</p>
+<pre><code>heaptrack &lt;yourapplication&gt; ...</code></pre>
+<p>Or, alternatively, you can attach to a running process via</p>
+<pre><code>heaptrack --pid $(pidof &lt;yourapplication&gt;)</code></pre></qt></string>
+ </property>
+ <property name="filter">
+ <string>heaptrack.*.*.gz</string>
+ </property>
+ <property name="placeholderText">
+ <string>path/to/heaptrack.$APP.$PID.gz</string>
+ </property>
+ <property name="fileDialogModality">
+ <enum>Qt::WindowModal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="KUrlRequester" name="compareTo">
+ <property name="toolTip">
+ <string><qt>You can optionally specify a second heaptrack data file to compare to. If set, this file will be used as a base and its cost gets subtracted from the primary data costs.</qt></string>
+ </property>
+ <property name="filter">
+ <string>heaptrack.*.*.gz</string>
+ </property>
+ <property name="placeholderText">
+ <string>path/to/heaptrack.$APP.$PID.gz</string>
+ </property>
+ <property name="fileDialogModality">
+ <enum>Qt::WindowModal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="openFileLabel">
+ <property name="text">
+ <string>Profile &Data:</string>
+ </property>
+ <property name="buddy">
+ <cstring>openFile</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="compareToLabel">
+ <property name="text">
+ <string>Compare to:</string>
+ </property>
+ <property name="buddy">
+ <cstring>compareTo</cstring>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="enabled">
+ <bool>false</bool>
</property>
- <property name="toolButtonStyle">
- <enum>Qt::ToolButtonTextUnderIcon</enum>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Open</set>
</property>
</widget>
</item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="loadingPage">
<header>kmessagewidget.h</header>
</customwidget>
<customwidget>
+ <class>KUrlRequester</class>
+ <extends>QWidget</extends>
+ <header>kurlrequester.h</header>
+ </customwidget>
+ <customwidget>
<class>ChartWidget</class>
<extends>QWidget</extends>
<header>chartwidget.h</header>