/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** Commercial Usage ** ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** **************************************************************************/ #include "canvasframerate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QLineGraph : public QWidget { Q_OBJECT public: QLineGraph(QAbstractSlider *slider, QWidget * = 0); void setPosition(int); public slots: void addSample(int, int, int, bool); void setResolutionForHeight(int); void clear(); protected: virtual void paintEvent(QPaintEvent *); virtual void mouseMoveEvent(QMouseEvent *); virtual void leaveEvent(QEvent *); virtual void wheelEvent(QWheelEvent *event); private slots: void sliderChanged(int); private: void updateSlider(); void drawSample(QPainter *, int, const QRect &, QList *); void drawTime(QPainter *, const QRect &); QRect findContainingRect(const QList &rects, const QPoint &pos) const; struct Sample { int sample[3]; bool isBreak; }; QList _samples; QAbstractSlider *slider; int position; int samplesPerWidth; int resolutionForHeight; bool ignoreScroll; QMargins graphMargins; QList rectsPaintTime; // time to do a paintEvent() QList rectsTimeBetween; // time between frames QRect highlightedBar; }; QLineGraph::QLineGraph(QAbstractSlider *slider, QWidget *parent) : QWidget(parent), slider(slider), position(-1), samplesPerWidth(99), resolutionForHeight(50), ignoreScroll(false), graphMargins(65, 10, 71, 35) { setMouseTracking(true); slider->setMaximum(0); slider->setMinimum(0); slider->setSingleStep(1); connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int))); } void QLineGraph::sliderChanged(int v) { if (ignoreScroll) return; if (v == slider->maximum()) position = -1; else position = v; update(); // update highlightedRect QPoint pos = mapFromGlobal(QCursor::pos()); if (geometry().contains(pos)) { QMouseEvent *me = new QMouseEvent(QEvent::MouseMove, pos, Qt::NoButton, Qt::NoButton, Qt::NoModifier); QApplication::postEvent(this, me); } } void QLineGraph::clear() { _samples.clear(); rectsPaintTime.clear(); rectsTimeBetween.clear(); highlightedBar = QRect(); position = -1; updateSlider(); update(); } void QLineGraph::updateSlider() { ignoreScroll = true; slider->setMaximum(qMax(0, _samples.count() - samplesPerWidth - 1)); if (position == -1) { slider->setValue(slider->maximum()); } else { slider->setValue(position); } ignoreScroll = false; } void QLineGraph::addSample(int a, int b, int d, bool isBreak) { Sample s; s.isBreak = isBreak; s.sample[0] = a; s.sample[1] = b; s.sample[2] = d; _samples << s; updateSlider(); update(); } void QLineGraph::setPosition(int p) { sliderChanged(p); } void QLineGraph::drawTime(QPainter *p, const QRect &rect) { if (_samples.isEmpty()) return; int first = position; if (first == -1) first = qMax(0, _samples.count() - samplesPerWidth - 1); int last = qMin(_samples.count() - 1, first + samplesPerWidth); qreal scaleX = qreal(rect.width()) / qreal(samplesPerWidth); int t = 0; for (int ii = first; ii <= last; ++ii) { int sampleTime = _samples.at(ii).sample[2] / 1000; if (sampleTime != t) { int xEnd = rect.left() + scaleX * (ii - first); p->drawLine(xEnd, rect.bottom(), xEnd, rect.bottom() + 7); QRect text(xEnd - 30, rect.bottom() + 10, 60, 30); p->drawText(text, Qt::AlignHCenter | Qt::AlignTop, QString::number(_samples.at(ii).sample[2])); t = sampleTime; } } } void QLineGraph::drawSample(QPainter *p, int s, const QRect &rect, QList *record) { if (_samples.isEmpty()) return; int first = position; if (first == -1) first = qMax(0, _samples.count() - samplesPerWidth - 1); int last = qMin(_samples.count() - 1, first + samplesPerWidth); qreal scaleY = qreal(rect.height()) / resolutionForHeight; qreal scaleX = qreal(rect.width()) / qreal(samplesPerWidth); int xEnd; int lastXEnd = rect.left(); p->save(); p->setPen(Qt::NoPen); for (int ii = first + 1; ii <= last; ++ii) { xEnd = rect.left() + scaleX * (ii - first); int yEnd = rect.bottom() - _samples.at(ii).sample[s] * scaleY; if (!(s == 0 && _samples.at(ii).isBreak)) { QRect bar(lastXEnd, yEnd, scaleX, _samples.at(ii).sample[s] * scaleY); record->append(bar); p->drawRect(bar); } lastXEnd = xEnd; } p->restore(); } void QLineGraph::paintEvent(QPaintEvent *) { QPainter p(this); p.setRenderHint(QPainter::Antialiasing); QRect r(graphMargins.left(), graphMargins.top(), width() - graphMargins.right(), height() - graphMargins.bottom()); p.save(); p.rotate(-90); p.translate(-r.height()/2 - r.width()/2 - graphMargins.right(), -r.height()/2); p.drawText(r, Qt::AlignCenter, tr("Frame rate")); p.restore(); p.setBrush(QColor("lightsteelblue")); rectsTimeBetween.clear(); drawSample(&p, 0, r, &rectsTimeBetween); p.setBrush(QColor("pink")); rectsPaintTime.clear(); drawSample(&p, 1, r, &rectsPaintTime); if (!highlightedBar.isNull()) { p.setBrush(Qt::darkGreen); p.drawRect(highlightedBar); } p.setBrush(Qt::NoBrush); p.drawRect(r); slider->setGeometry(x() + r.x(), slider->y(), r.width(), slider->height()); for (int ii = 0; ii <= resolutionForHeight; ++ii) { int y = 1 + r.bottom() - ii * r.height() / resolutionForHeight; if ((ii % 10) == 0) { p.drawLine(r.left() - 20, y, r.left(), y); QRect text(r.left() - 20 - 53, y - 10, 50, 20); p.drawText(text, Qt::AlignRight | Qt::AlignVCenter, QString::number(ii)); } else { p.drawLine(r.left() - 7, y, r.left(), y); } } drawTime(&p, r); } void QLineGraph::mouseMoveEvent(QMouseEvent *event) { QPoint pos = event->pos(); QRect rect = findContainingRect(rectsPaintTime, pos); if (rect.isNull()) rect = findContainingRect(rectsTimeBetween, pos); if (!highlightedBar.isNull()) update(highlightedBar.adjusted(-1, -1, 1, 1)); highlightedBar = rect; if (!rect.isNull()) { QRect graph(graphMargins.left(), graphMargins.top(), width() - graphMargins.right(), height() - graphMargins.bottom()); qreal scaleY = qreal(graph.height()) / resolutionForHeight; QToolTip::showText(event->globalPos(), QString::number(qRound(rect.height() / scaleY)), this, rect); update(rect.adjusted(-1, -1, 1, 1)); } } void QLineGraph::leaveEvent(QEvent *) { if (!highlightedBar.isNull()) { QRect bar = highlightedBar.adjusted(-1, -1, 1, 1); highlightedBar = QRect(); update(bar); } } void QLineGraph::wheelEvent(QWheelEvent *event) { QWheelEvent we(QPoint(0,0), event->delta(), event->buttons(), event->modifiers(), event->orientation()); QApplication::sendEvent(slider, &we); } void QLineGraph::setResolutionForHeight(int resolution) { resolutionForHeight = resolution; update(); } QRect QLineGraph::findContainingRect(const QList &rects, const QPoint &pos) const { for (int i=0; isetFocusPolicy(Qt::WheelFocus); m_graph = new QLineGraph(scroll); setFocusPolicy(Qt::WheelFocus); setFocusProxy(scroll); QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 5, 0); layout->setSpacing(0); layout->addWidget(m_graph, 2); layout->addWidget(new QLabel(tr("Total time elapsed (ms)")), 0, Qt::AlignHCenter); layout->addWidget(scroll); } void GraphWindow::addSample(int a, int b, int d, bool isBreak) { m_graph->addSample(a, b, d, isBreak); } void GraphWindow::setResolutionForHeight(int res) { m_graph->setResolutionForHeight(res); } void GraphWindow::clear() { m_graph->clear(); } QSize GraphWindow::sizeHint() const { return QSize(400, 220); } class CanvasFrameRatePlugin : public QDeclarativeDebugClient { Q_OBJECT public: CanvasFrameRatePlugin(QDeclarativeDebugConnection *client); signals: void sample(int, int, int, bool); protected: virtual void messageReceived(const QByteArray &); private: int lb; int ld; }; CanvasFrameRatePlugin::CanvasFrameRatePlugin(QDeclarativeDebugConnection *client) : QDeclarativeDebugClient(QLatin1String("CanvasFrameRate"), client), lb(-1) { } void CanvasFrameRatePlugin::messageReceived(const QByteArray &data) { QByteArray rwData = data; QDataStream stream(&rwData, QIODevice::ReadOnly); int b; int c; int d; bool isBreak; stream >> b >> c >> d >> isBreak; if (lb != -1) emit sample(c, lb, ld, isBreak); lb = b; ld = d; } CanvasFrameRate::CanvasFrameRate(QWidget *parent) : QWidget(parent), m_plugin(0) { m_tabs = new QTabWidget(this); QHBoxLayout *bottom = new QHBoxLayout; bottom->setMargin(0); bottom->setSpacing(10); m_res = new QSpinBox; m_res->setRange(30, 200); m_res->setValue(m_res->minimum()); m_res->setSingleStep(10); m_res->setSuffix(QLatin1String("ms")); bottom->addWidget(new QLabel(tr("Resolution:"))); bottom->addWidget(m_res); bottom->addStretch(); m_clearButton = new QPushButton(tr("Clear")); connect(m_clearButton, SIGNAL(clicked()), SLOT(clearGraph())); bottom->addWidget(m_clearButton); QPushButton *pb = new QPushButton(tr("New Graph"), this); connect(pb, SIGNAL(clicked()), this, SLOT(newTab())); bottom->addWidget(pb); m_group = new QGroupBox(tr("Enabled")); m_group->setCheckable(true); m_group->setChecked(false); connect(m_group, SIGNAL(toggled(bool)), SLOT(enabledToggled(bool))); QVBoxLayout *groupLayout = new QVBoxLayout(m_group); groupLayout->setContentsMargins(5, 0, 5, 0); groupLayout->setSpacing(2); groupLayout->addWidget(m_tabs); groupLayout->addLayout(bottom); QVBoxLayout *layout = new QVBoxLayout; layout->setContentsMargins(0, 10, 0, 0); layout->setSpacing(0); layout->addWidget(m_group); setLayout(layout); } void CanvasFrameRate::reset(QDeclarativeDebugConnection *conn) { delete m_plugin; m_plugin = 0; QWidget *w; for (int i=0; icount(); ++i) { w = m_tabs->widget(i); m_tabs->removeTab(i); delete w; } if (conn) { connect(conn, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(connectionStateChanged(QAbstractSocket::SocketState))); if (conn->state() == QAbstractSocket::ConnectedState) handleConnected(conn); } } void CanvasFrameRate::connectionStateChanged(QAbstractSocket::SocketState state) { if (state == QAbstractSocket::UnconnectedState) { delete m_plugin; m_plugin = 0; } else if (state == QAbstractSocket::ConnectedState) { handleConnected(qobject_cast(sender())); } } void CanvasFrameRate::handleConnected(QDeclarativeDebugConnection *conn) { delete m_plugin; m_plugin = new CanvasFrameRatePlugin(conn); enabledToggled(m_group->isChecked()); newTab(); } void CanvasFrameRate::setSizeHint(const QSize &size) { m_sizeHint = size; } QSize CanvasFrameRate::sizeHint() const { return m_sizeHint; } void CanvasFrameRate::clearGraph() { if (m_tabs->count()) { GraphWindow *w = qobject_cast(m_tabs->currentWidget()); if (w) w->clear(); } } void CanvasFrameRate::newTab() { if (!m_plugin) return; if (m_tabs->count()) { QWidget *w = m_tabs->widget(m_tabs->count() - 1); QObject::disconnect(m_plugin, SIGNAL(sample(int,int,int,bool)), w, SLOT(addSample(int,int,int,bool))); } int count = m_tabs->count(); GraphWindow *graph = new GraphWindow; graph->setResolutionForHeight(m_res->value()); connect(m_plugin, SIGNAL(sample(int,int,int,bool)), graph, SLOT(addSample(int,int,int,bool))); connect(m_res, SIGNAL(valueChanged(int)), graph, SLOT(setResolutionForHeight(int))); QString name = QLatin1String("Graph ") + QString::number(count + 1); m_tabs->addTab(graph, name); m_tabs->setCurrentIndex(count); } void CanvasFrameRate::enabledToggled(bool checked) { if (m_plugin) static_cast(m_plugin)->setEnabled(checked); } QT_END_NAMESPACE #include "canvasframerate.moc"