diff options
author | Simon Rettberg | 2017-02-24 12:33:55 +0100 |
---|---|---|
committer | Simon Rettberg | 2017-02-24 12:33:55 +0100 |
commit | d4f10f9fe446da2aff65088e5236d6ffcdd034b5 (patch) | |
tree | 5d7467a6f67272064619c000a14800e706368ac6 | |
download | speedcheck-d4f10f9fe446da2aff65088e5236d6ffcdd034b5.tar.gz speedcheck-d4f10f9fe446da2aff65088e5236d6ffcdd034b5.tar.xz speedcheck-d4f10f9fe446da2aff65088e5236d6ffcdd034b5.zip |
initial commit
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 77 | ||||
-rw-r--r-- | src/copythread.cpp | 72 | ||||
-rw-r--r-- | src/copythread.h | 26 | ||||
-rw-r--r-- | src/datasource/cpuload.cpp | 55 | ||||
-rw-r--r-- | src/datasource/cpuload.h | 22 | ||||
-rw-r--r-- | src/datasource/idatasource.h | 16 | ||||
-rw-r--r-- | src/datasource/networkspeed.cpp | 50 | ||||
-rw-r--r-- | src/datasource/networkspeed.h | 24 | ||||
-rw-r--r-- | src/graphwidget.cpp | 95 | ||||
-rw-r--r-- | src/graphwidget.h | 33 | ||||
-rw-r--r-- | src/main.cpp | 16 | ||||
-rw-r--r-- | src/speedcheck.cpp | 81 | ||||
-rw-r--r-- | src/speedcheck.h | 35 | ||||
-rw-r--r-- | src/types.h | 6 | ||||
-rw-r--r-- | src/ui/speedcheck.ui | 107 |
16 files changed, 718 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2854e38 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.project +/.cproject +/build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a642836 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,77 @@ +cmake_minimum_required(VERSION 2.6) + +# cmake -G"Eclipse CDT4 - Unix Makefiles" -D CMAKE_BUILD_TYPE=Debug .. + +# project name +project(speedcheck) + +set(CMAKE_BUILD_TYPE Debug) +set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wall -Wextra -pedantic -Werror -Wno-multichar") +set(CMAKE_CXX_FLAGS_RELEASE "-O2 -Wno-multichar") + +file(GLOB_RECURSE SPEEDCHECK_SOURCES src/*.cpp) +file(GLOB_RECURSE SPEEDCHECK_MOC_HEADERS src/*.h) +file(GLOB_RECURSE SPEEDCHECK_UIS src/ui/*.ui) +file(GLOB_RECURSE SPEEDCHECK_RESOURCES src/*.qrc) +file(GLOB_RECURSE SPEEDCHECK_TSS src/i18n/*.ts) + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ./src) + +# +# Qt4 +# +find_package(Qt4 4.7.0 REQUIRED) +if(QT4_FOUND) + message(STATUS "Qt4 found") +else(QT4_FOUND) + message(FATAL_ERROR "Qt4 not found") +endif(QT4_FOUND) + +set(QT_USE_QTSVG TRUE) +set(QT_USE_QTNETWORK TRUE) + +include(${QT_USE_FILE}) + +option(UPDATE_TRANSLATIONS "Update .ts files (WARNING: make clean will delete the .ts files!)") +if(SPEEDCHECK_TSS) + if (UPDATE_TRANSLATIONS) + set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${SPEEDCHECK_SOURCES} ${SPEEDCHECK_UIS}) + QT4_CREATE_TRANSLATION(SPEEDCHECK_QMS ${FILES_TO_TRANSLATE} ${SPEEDCHECK_TSS} OPTIONS -noobsolete) + message(STATUS ".tr files have been updated") + else (UPDATE_TRANSLATIONS) + QT4_ADD_TRANSLATION(SPEEDCHECK_QMS ${SPEEDCHECK_TSS}) + endif (UPDATE_TRANSLATIONS) + + # write a resource file for qm files + set(resource_file_content "<RCC>\n <qresource prefix=\"/\">\n") + foreach(file ${SPEEDCHECK_QMS}) + get_filename_component(filename ${file} NAME) + set(resource_file_content "${resource_file_content} <file>${filename}</file>\n") + endforeach(file) + set(resource_file_content "${resource_file_content} </qresource>\n</RCC>\n") + file(WRITE "${CMAKE_BINARY_DIR}/translation.qrc" "${resource_file_content}") + set(SPEEDCHECK_RESOURCES ${SPEEDCHECK_RESOURCES} "${CMAKE_BINARY_DIR}/translation.qrc") + + #add_custom_target(translations_target DEPENDS ${SPEEDCHECK_QMS}) +endif(SPEEDCHECK_TSS) + +QT4_ADD_RESOURCES(SPEEDCHECK_RC_SOURCES ${SPEEDCHECK_RESOURCES}) +QT4_WRAP_UI(SPEEDCHECK_UI_HEADERS ${SPEEDCHECK_UIS}) +QT4_WRAP_CPP(SPEEDCHECK_MOC_SOURCES ${SPEEDCHECK_MOC_HEADERS}) + +# +# build speedcheck +# +add_executable(speedcheck + ${SPEEDCHECK_SOURCES} + ${SPEEDCHECK_MOC_SOURCES} + ${SPEEDCHECK_UI_HEADERS} + ${SPEEDCHECK_RC_SOURCES} + ${SPEEDCHECK_QMS} +) + +target_link_libraries(speedcheck + ${QT_LIBRARIES} +) + +install(TARGETS speedcheck RUNTIME DESTINATION bin) diff --git a/src/copythread.cpp b/src/copythread.cpp new file mode 100644 index 0000000..1f9ec2f --- /dev/null +++ b/src/copythread.cpp @@ -0,0 +1,72 @@ +#include "copythread.h" +#include <QFile> +#include <QElapsedTimer> +#include <QCoreApplication> +#include <QDebug> + +// 1M read size +#define BUFFER_SIZE (1000000) +// 20 seconds for each test +#define TEST_LENGTH (60000) + +CopyThread::CopyThread(QFile *file, QObject *parent) + : QThread(parent), + _doStop(false) +{ + _file = file; +} + +CopyThread::~CopyThread() +{ + delete _file; +} + +void CopyThread::stop() +{ + _doStop = true; +} + +void CopyThread::run() +{ + qDebug() << "Entering run method"; + char *buffer = new char[BUFFER_SIZE]; + QElapsedTimer timer; + qint64 ret; + qint64 seqSum = 0, rndSum = 0; + qint64 seqTime, rndTime = -1; + + // Sequential read + emit logMessage(trUtf8("Starting sequential read test")); + timer.start(); + do { + ret = _file->read(buffer, BUFFER_SIZE); + seqSum += ret; + if (ret == 0) { + _file->seek(0); + } + } while (!_doStop && ret >= 0 && timer.elapsed() < TEST_LENGTH); + seqTime = timer.elapsed(); + + // Random read + qsrand((uint)QCoreApplication::applicationPid()); + const qint64 size = _file->size() - BUFFER_SIZE; + if (size > 0) { + emit logMessage(trUtf8("Starting random read test")); + timer.restart(); + do { + _file->seek(((qint64)qrand() ^ ((qint64)qrand() << 15)) % size); + ret = _file->read(buffer, BUFFER_SIZE); + rndSum += ret; + } while (!_doStop && ret > 0 && timer.elapsed() < TEST_LENGTH); + rndTime = timer.elapsed(); + } + + // All done + const qint64 seqSpeed = seqSum / (seqTime * 1024 + 1); + const qint64 rndSpeed = rndSum / (rndTime * 1024 + 1); + emit logMessage(trUtf8("Seq: %1MiB/s, Random: %2MiB/s - [%3s / %4s]") + .arg(QString::number(seqSpeed), QString::number(rndSpeed), + QString::number(seqTime / 1000), QString::number(rndTime / 1000))); + delete[] buffer; + _file->close(); +} diff --git a/src/copythread.h b/src/copythread.h new file mode 100644 index 0000000..428c7fa --- /dev/null +++ b/src/copythread.h @@ -0,0 +1,26 @@ +#ifndef COPYTHREAD_H_ +#define COPYTHREAD_H_ + +#include <QThread> + +class QFile; + +class CopyThread : public QThread { + Q_OBJECT +public: + CopyThread(QFile *file, QObject *parent = NULL); + virtual ~CopyThread(); + void stop(); + +protected: + virtual void run(); + +private: + QFile *_file; + volatile bool _doStop; + +signals: + void logMessage(QString message); +}; + +#endif /* COPYTHREAD_H_ */ diff --git a/src/datasource/cpuload.cpp b/src/datasource/cpuload.cpp new file mode 100644 index 0000000..48831f3 --- /dev/null +++ b/src/datasource/cpuload.cpp @@ -0,0 +1,55 @@ +#include "cpuload.h" +#include <QStringList> + +CpuLoad::CpuLoad() : + _lastLoad(0), + _lastTotal(0), + _file("/proc/stat") +{ + // TODO Auto-generated constructor stub + +} + +CpuLoad::~CpuLoad() { + // TODO Auto-generated destructor stub +} + +qint64 CpuLoad::read() +{ + static QChar space(' '); + if (!_file.open(QIODevice::ReadOnly)) { + return -1; + } + char buffer[500]; + _file.readLine(buffer, sizeof(buffer)); + _file.close(); + QString line(buffer); + QStringList list = line.split(space, QString::SkipEmptyParts); + if (list.length() < 8) { + return -1; + } + // "cpu", user, nice, system, idle, iowait, irq, softirq + qint64 load = 0, total = 0; + for (int i = 1; i < 8; ++i) { + const qint64 value = list.at(i).toLongLong(); + if (i < 4 || i > 5) { + load += value; + } + total += value; + } + const qint64 ret = ((load - _lastLoad) * 1000) / (total - _lastTotal); + _lastTotal = total; + _lastLoad = load; + return ret; +} + +const QList<qint64>& CpuLoad::getBars() const +{ + static QList<qint64> list; + return list; +} + +qint64 CpuLoad::getMaximum() +{ + return 1000; +} diff --git a/src/datasource/cpuload.h b/src/datasource/cpuload.h new file mode 100644 index 0000000..2a5a2cb --- /dev/null +++ b/src/datasource/cpuload.h @@ -0,0 +1,22 @@ +#ifndef CPULOAD_H_ +#define CPULOAD_H_ + +#include "idatasource.h" +#include <QFile> + +class CpuLoad : public IDataSource { +public: + CpuLoad(); + virtual ~CpuLoad(); + + virtual qint64 read(); + virtual const QList<qint64>& getBars() const; + virtual qint64 getMaximum(); + +private: + qint64 _lastLoad; + qint64 _lastTotal; + QFile _file; +}; + +#endif /* CPULOAD_H_ */ diff --git a/src/datasource/idatasource.h b/src/datasource/idatasource.h new file mode 100644 index 0000000..4639d57 --- /dev/null +++ b/src/datasource/idatasource.h @@ -0,0 +1,16 @@ +#ifndef IDATASOURCE_H_ +#define IDATASOURCE_H_ + +#include <QList> + +class IDataSource { +public: + IDataSource() {}; + virtual ~IDataSource() {}; + + virtual qint64 read() = 0; + virtual const QList<qint64>& getBars() const = 0; + virtual qint64 getMaximum() = 0; +}; + +#endif /* IDATASOURCE_H_ */ diff --git a/src/datasource/networkspeed.cpp b/src/datasource/networkspeed.cpp new file mode 100644 index 0000000..8888fd4 --- /dev/null +++ b/src/datasource/networkspeed.cpp @@ -0,0 +1,50 @@ +#include "networkspeed.h" + +NetworkSpeed::NetworkSpeed() : + _lastBytes(0), + _lastMs(0), + _file("/sys/class/net/eth0/statistics/rx_bytes") +{ + _timer.start(); +} + +NetworkSpeed::~NetworkSpeed() +{ + // TODO Auto-generated destructor stub +} + +qint64 NetworkSpeed::read() +{ + char buffer[500]; + qint64 ret, now; + if (!_file.open(QIODevice::ReadOnly)) { + return -1; + } + now = _timer.elapsed(); + _file.readLine(buffer, sizeof(buffer)); + _file.close(); + QString line(buffer); + const qint64 counter = (qint64)line.toLongLong(); + if (_lastBytes == 0) { + ret = 0; + } else { + ret = counter - _lastBytes; + } + if (_lastMs < now) { + ret = (ret * 1000) / (now - _lastMs); + } + _lastMs = now; + _lastBytes = counter; + return ret; +} + +const QList<qint64>& NetworkSpeed::getBars() const +{ + static QList<qint64> list = QList<qint64>() << ((qint64)1024 * 1024 * 10); + return list; +} + +qint64 NetworkSpeed::getMaximum() +{ + return ((qint64)1024 * 1024 * 100); +} diff --git a/src/datasource/networkspeed.h b/src/datasource/networkspeed.h new file mode 100644 index 0000000..482f4cd --- /dev/null +++ b/src/datasource/networkspeed.h @@ -0,0 +1,24 @@ +#ifndef NETWORKSPEED_H_ +#define NETWORKSPEED_H_ + +#include "idatasource.h" +#include <QElapsedTimer> +#include <QFile> + +class NetworkSpeed : public IDataSource { +public: + NetworkSpeed(); + virtual ~NetworkSpeed(); + + virtual qint64 read(); + virtual const QList<qint64>& getBars() const; + virtual qint64 getMaximum(); + +private: + qint64 _lastBytes; + qint64 _lastMs; + QElapsedTimer _timer; + QFile _file; +}; + +#endif /* NETWORKSPEED_H_ */ diff --git a/src/graphwidget.cpp b/src/graphwidget.cpp new file mode 100644 index 0000000..ec15026 --- /dev/null +++ b/src/graphwidget.cpp @@ -0,0 +1,95 @@ +#include "graphwidget.h" +#include "datasource/idatasource.h" +#include <QDebug> +#include <QResizeEvent> +#include <QPaintEvent> +#include <QPainter> + +const QColor GraphWidget::LINE_COLOR = QColor::fromRgb(20, 255, 80); +const QColor GraphWidget::BACK_COLOR = QColor::fromRgb(10, 0, 20); +const QColor GraphWidget::BAR_COLOR = QColor::fromRgb(200, 200, 200); + +GraphWidget::GraphWidget(QWidget* parent) + : QWidget(parent), + _source(NULL), + _histPos(0), + _vScale(0), + _doubleSpace(false) +{ + memset(_history, 0, sizeof(*_history) * HISTSIZE); +} + +GraphWidget::~GraphWidget() +{ + delete _source; +} + +void GraphWidget::setDataSource(IDataSource* source) +{ + delete _source; + _source = source; + memset(_history, 0, sizeof(*_history) * HISTSIZE); + _vScale = (float)height() / (float)_source->getMaximum(); + qDebug() << "sDS " << _vScale; + update(); +} + +void GraphWidget::readNextValue() +{ + if (_source == NULL) + return; + // + _history[_histPos] = _source->read(); + _histPos = (_histPos + 1) % HISTSIZE; + if (_doubleSpace && _histPos % 2 == 1) { + _history[_histPos] = _history[(_histPos + HISTSIZE - 1) % HISTSIZE]; + update(_updateRectSmall); + } else { + update(); + } +} + +void GraphWidget::resizeEvent(QResizeEvent * event) +{ + const QSize &size = event->size(); + _updateRectSmall = QRect(size.width() - 1, 0, 1, size.height()); + if (_source != NULL) { + _vScale = (float)size.height() / (float)_source->getMaximum(); + } + _doubleSpace = (width() < 400); + update(); +} + +void GraphWidget::paintEvent(QPaintEvent * event) +{ + qint64 value; + int x, y; + const QRect &rect = event->rect(); + QPainter painter; + painter.begin(this); + painter.fillRect(rect, BACK_COLOR); + if (_source == NULL) + return; + painter.setPen(LINE_COLOR); + const int offset = HISTSIZE + HISTSIZE + _histPos - (this->width() - rect.x()) * (_doubleSpace ? 2 : 1); + // TODO: double space + for (int index = 0; index < rect.width(); ++index) { + x = rect.x() + index; + if (_doubleSpace) { + value = (_history[(offset + index * 2) % HISTSIZE] + _history[(offset + index * 2 + 1) % HISTSIZE]) / 2; + } else { + value = _history[(offset + index) % HISTSIZE]; + } + y = height() - int(value * _vScale); + painter.drawLine(x, y, x, height()); + } + const QList<qint64> &bars = _source->getBars(); + if (!bars.empty()) { + painter.setPen(BAR_COLOR); + for (QList<qint64>::const_iterator it = bars.begin(); it != bars.end(); ++it) { + y = height() - int(*it * _vScale); + painter.drawLine(rect.left(), y, rect.right() + 1, y); + } + } + painter.end(); +} diff --git a/src/graphwidget.h b/src/graphwidget.h new file mode 100644 index 0000000..f43e0db --- /dev/null +++ b/src/graphwidget.h @@ -0,0 +1,33 @@ +#ifndef GRAPHWIDGET_H_ +#define GRAPHWIDGET_H_ + +#include <QWidget> + +class IDataSource; + +class GraphWidget : public QWidget +{ + Q_OBJECT +public: + GraphWidget(QWidget* parent = NULL); + virtual ~GraphWidget(); + + void setDataSource(IDataSource* source); + void readNextValue(); + +protected: + virtual void resizeEvent(QResizeEvent * event); + virtual void paintEvent(QPaintEvent * event); + +private: + static const int HISTSIZE = 2000; + static const QColor LINE_COLOR, BACK_COLOR, BAR_COLOR; + IDataSource* _source; + qint64 _history[HISTSIZE]; + int _histPos; + float _vScale; + bool _doubleSpace; + QRect _updateRectSmall; +}; + +#endif /* GRAPHWIDGET_H_ */ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d5b71fb --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,16 @@ +#include "speedcheck.h" +#include <QApplication> +#include <QMessageBox> + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + if (argc != 2) { + QMessageBox::critical(NULL, "Error", "Need one argument: file name"); + return 1; + } + SpeedCheck main(QString::fromLocal8Bit(argv[1])); + main.show(); + app.exec(); + return 0; +} diff --git a/src/speedcheck.cpp b/src/speedcheck.cpp new file mode 100644 index 0000000..8ff8c93 --- /dev/null +++ b/src/speedcheck.cpp @@ -0,0 +1,81 @@ +#include "speedcheck.h" +#include "ui_speedcheck.h" +#include "copythread.h" +#include "datasource/cpuload.h" +#include "datasource/networkspeed.h" + +#include <QCoreApplication> +#include <QMessageBox> +#include <QFile> +#include <QDebug> +#include <QTimer> + +SpeedCheck::SpeedCheck(QString fileName) + : _ui(new Ui::MainWindow), + _thread(NULL), + _doQuit(false), + _fileName(fileName) +{ + _ui->setupUi(this); + connect(_ui->btnStart, SIGNAL(clicked(bool)), this, SLOT(startClicked(bool))); + connect(_ui->btnQuit, SIGNAL(clicked(bool)), this, SLOT(quitClicked(bool))); + _timer.setInterval(200); + connect(&_timer, SIGNAL(timeout()), this, SLOT(updateTimer())); +} + +SpeedCheck::~SpeedCheck() +{ + +} + +void SpeedCheck::logMessage(QString message) +{ + qDebug() << message; + _ui->statusBar->showMessage(message); +} + +void SpeedCheck::startClicked(bool) +{ + QFile *file = new QFile(_fileName); + if (!file->open(QIODevice::ReadOnly)) { + QMessageBox::critical(this, trUtf8("Error"), trUtf8("Could not open %1 for reading.").arg(_fileName)); + return; + } + _ui->picCpu->setDataSource(new CpuLoad()); + _ui->picSpeed->setDataSource(new NetworkSpeed()); + _ui->btnStart->setDisabled(true); + delete _thread; + _thread = new CopyThread(file, this); + connect(_thread, SIGNAL(logMessage(QString)), this, SLOT(logMessage(QString)), Qt::QueuedConnection); + connect(_thread, SIGNAL(finished()), this, SLOT(testFinished()), Qt::QueuedConnection); + _timer.start(); + _thread->start(); +} + +void SpeedCheck::quitClicked(bool) +{ + if (_thread != NULL && _thread->isRunning()) { + _doQuit = true; + _thread->stop(); + } + if (_thread == NULL || !_thread->isRunning()) { + QCoreApplication::instance()->quit(); + } +} + +void SpeedCheck::testFinished() +{ + _timer.stop(); + delete _thread; + _thread = NULL; + if (_doQuit) { + QCoreApplication::instance()->quit(); + } + _ui->btnStart->setEnabled(true); +} + +void SpeedCheck::updateTimer() +{ + _ui->picCpu->readNextValue(); + _ui->picSpeed->readNextValue(); +} diff --git a/src/speedcheck.h b/src/speedcheck.h new file mode 100644 index 0000000..fd54605 --- /dev/null +++ b/src/speedcheck.h @@ -0,0 +1,35 @@ +#ifndef SPEEDCHECK_H_ +#define SPEEDCHECK_H_ + +#include <QMainWindow> +#include <QTimer> + +namespace Ui +{ + class MainWindow; +} +class CopyThread; + +class SpeedCheck : public QMainWindow +{ + Q_OBJECT +public: + SpeedCheck(QString fileName); + virtual ~SpeedCheck(); + +private slots: + void logMessage(QString message); + void startClicked(bool); + void quitClicked(bool); + void testFinished(); + void updateTimer(); + +private: + Ui::MainWindow *_ui; + CopyThread *_thread; + volatile bool _doQuit; + QString _fileName; + QTimer _timer; +}; + +#endif /* SPEEDCHECK_H_ */ diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..bc24687 --- /dev/null +++ b/src/types.h @@ -0,0 +1,6 @@ +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#define UNUSED __attribute__ ((unused)) + +#endif diff --git a/src/ui/speedcheck.ui b/src/ui/speedcheck.ui new file mode 100644 index 0000000..2f98f3b --- /dev/null +++ b/src/ui/speedcheck.ui @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>SpeedCheck</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="GraphWidget" name="picSpeed" native="true"/> + </item> + <item> + <widget class="GraphWidget" name="picCpu" native="true"/> + </item> + <item> + <widget class="QFrame" name="frame_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>32</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>32</height> + </size> + </property> + <property name="sizeIncrement"> + <size> + <width>0</width> + <height>32</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>0</width> + <height>32</height> + </size> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>3</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="btnStart"> + <property name="text"> + <string>Start</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="btnQuit"> + <property name="text"> + <string>Quit</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QStatusBar" name="statusBar"/> + </widget> + <customwidgets> + <customwidget> + <class>GraphWidget</class> + <extends>QWidget</extends> + <header>graphwidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> |