diff options
author | Evgeny Shtanov <shtanov_evgenii@mail.ru> | 2021-03-10 22:33:56 +0300 |
---|---|---|
committer | Evgeny Shtanov <shtanov_evgenii@mail.ru> | 2021-03-19 00:48:34 +0300 |
commit | a72f9e33dc8fe60ef80eee0ecf97965fc8482fe9 (patch) | |
tree | 6af3cd4cbdfdef1a137bb690f076bb15608eb414 /examples/serialbus/can | |
parent | 55f97fd0178485bafed1e97eaf0d4ebf91a312b5 (diff) |
CAN example: Replace received frames edit with proper table and model
Add a class based on QAbstractTableModel to show the received frames.
Also add a configurable queue to buffer the frames before displaying
them. This should avoid frames loss in flood traffic mode.
Task-number: QTBUG-85611
Change-Id: Iac4671096c1cbfdc8297acf3c99be960b7eaf010
Reviewed-by: André Hartmann <aha_1980@gmx.de>
Diffstat (limited to 'examples/serialbus/can')
-rw-r--r-- | examples/serialbus/can/CMakeLists.txt | 2 | ||||
-rw-r--r-- | examples/serialbus/can/can.pro | 5 | ||||
-rw-r--r-- | examples/serialbus/can/common.h | 70 | ||||
-rw-r--r-- | examples/serialbus/can/connectdialog.cpp | 8 | ||||
-rw-r--r-- | examples/serialbus/can/connectdialog.h | 3 | ||||
-rw-r--r-- | examples/serialbus/can/connectdialog.ui | 486 | ||||
-rw-r--r-- | examples/serialbus/can/mainwindow.cpp | 45 | ||||
-rw-r--r-- | examples/serialbus/can/mainwindow.h | 9 | ||||
-rw-r--r-- | examples/serialbus/can/mainwindow.ui | 100 | ||||
-rw-r--r-- | examples/serialbus/can/receivedframesmodel.cpp | 226 | ||||
-rw-r--r-- | examples/serialbus/can/receivedframesmodel.h | 90 | ||||
-rw-r--r-- | examples/serialbus/can/receivedframesview.cpp | 117 | ||||
-rw-r--r-- | examples/serialbus/can/receivedframesview.h | 67 | ||||
-rw-r--r-- | examples/serialbus/can/sendframebox.ui | 20 |
14 files changed, 973 insertions, 275 deletions
diff --git a/examples/serialbus/can/CMakeLists.txt b/examples/serialbus/can/CMakeLists.txt index 0f24732..c9d3a11 100644 --- a/examples/serialbus/can/CMakeLists.txt +++ b/examples/serialbus/can/CMakeLists.txt @@ -24,6 +24,8 @@ qt_add_executable(can main.cpp mainwindow.cpp mainwindow.h mainwindow.ui sendframebox.cpp sendframebox.h sendframebox.ui + receivedframesmodel.cpp receivedframesmodel.h + receivedframesview.cpp receivedframesview.h ) set_target_properties(can PROPERTIES WIN32_EXECUTABLE TRUE diff --git a/examples/serialbus/can/can.pro b/examples/serialbus/can/can.pro index 81e29ee..06a75f3 100644 --- a/examples/serialbus/can/can.pro +++ b/examples/serialbus/can/can.pro @@ -9,12 +9,17 @@ SOURCES += \ connectdialog.cpp \ main.cpp \ mainwindow.cpp \ + receivedframesmodel.cpp \ + receivedframesview.cpp \ sendframebox.cpp HEADERS += \ bitratebox.h \ + common.h \ connectdialog.h \ mainwindow.h \ + receivedframesmodel.h \ + receivedframesview.h \ sendframebox.h FORMS += mainwindow.ui \ diff --git a/examples/serialbus/can/common.h b/examples/serialbus/can/common.h new file mode 100644 index 0000000..9ebabfe --- /dev/null +++ b/examples/serialbus/can/common.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Evgeny Shtanov <shtanov_evgenii@mail.ru> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the QtSerialBus module. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMMON_H +#define COMMON_H + +#include <qnamespace.h> + +enum ReceivedFramesModelColumns { + Number = 0, + Timestamp, + Flags, + CanID, + DLC, + Data, + Count +}; + +enum : int { + ClipboardTextRole = Qt::UserRole + 1 +}; + +#endif // COMMON_H diff --git a/examples/serialbus/can/connectdialog.cpp b/examples/serialbus/can/connectdialog.cpp index a0430b4..802c25a 100644 --- a/examples/serialbus/can/connectdialog.cpp +++ b/examples/serialbus/can/connectdialog.cpp @@ -82,6 +82,10 @@ ConnectDialog::ConnectDialog(QWidget *parent) : this, &ConnectDialog::pluginChanged); connect(m_ui->interfaceListBox, &QComboBox::currentTextChanged, this, &ConnectDialog::interfaceChanged); + connect(m_ui->ringBufferBox, &QCheckBox::stateChanged, [this](int state){ + m_ui->ringBufferLimitBox->setEnabled(state == Qt::CheckState::Checked); + }); + m_ui->rawFilterEdit->hide(); m_ui->rawFilterLabel->hide(); @@ -195,6 +199,10 @@ void ConnectDialog::updateSettings() m_currentSettings.deviceInterfaceName = m_ui->interfaceListBox->currentText(); m_currentSettings.useConfigurationEnabled = m_ui->useConfigurationBox->isChecked(); + m_currentSettings.useModelRingBuffer = m_ui->ringBufferBox->isChecked(); + m_currentSettings.modelRingBufferSize = m_ui->ringBufferLimitBox->value(); + m_currentSettings.useAutoscroll = m_ui->autoscrollBox->isChecked(); + if (m_currentSettings.useConfigurationEnabled) { m_currentSettings.configurations.clear(); // process LoopBack diff --git a/examples/serialbus/can/connectdialog.h b/examples/serialbus/can/connectdialog.h index d7a0a55..ed23f9d 100644 --- a/examples/serialbus/can/connectdialog.h +++ b/examples/serialbus/can/connectdialog.h @@ -76,6 +76,9 @@ public: QString deviceInterfaceName; QList<ConfigurationItem> configurations; bool useConfigurationEnabled = false; + bool useModelRingBuffer = true; + int modelRingBufferSize = 1000; + bool useAutoscroll = false; }; explicit ConnectDialog(QWidget *parent = nullptr); diff --git a/examples/serialbus/can/connectdialog.ui b/examples/serialbus/can/connectdialog.ui index b4ba898..54c3550 100644 --- a/examples/serialbus/can/connectdialog.ui +++ b/examples/serialbus/can/connectdialog.ui @@ -6,236 +6,302 @@ <rect> <x>0</x> <y>0</y> - <width>446</width> - <height>418</height> + <width>542</width> + <height>558</height> </rect> </property> <property name="windowTitle"> <string>Connect</string> </property> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="5" column="0" colspan="2"> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <layout class="QGridLayout" name="gridLayout_6"> + <item row="0" column="0"> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="0"> + <widget class="QGroupBox" name="selectPluginBox"> + <property name="title"> + <string>Select CAN plugin</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QComboBox" name="pluginListBox"/> + </item> + </layout> + </widget> + </item> + <item row="0" column="1" rowspan="4"> + <widget class="QGroupBox" name="configurationBox"> + <property name="enabled"> + <bool>false</bool> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>96</width> - <height>20</height> - </size> + <property name="title"> + <string>Specify Configuration</string> </property> - </spacer> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <widget class="QLabel" name="rawFilterLabel"> + <property name="text"> + <string>RAW Filter</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="rawFilterEdit"> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="errorFilterLabel"> + <property name="text"> + <string>Error Filter</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="errorFilterEdit"> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="placeholderText"> + <string>FrameError bits</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="loopbackLabel"> + <property name="text"> + <string>Loopback</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="loopbackBox"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="receiveOwnLabel"> + <property name="text"> + <string>Receive Own</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QComboBox" name="receiveOwnBox"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="bitrateLabel"> + <property name="text"> + <string>Bitrate</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="BitRateBox" name="bitrateBox"/> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="canFdLabel"> + <property name="text"> + <string>CAN FD</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QComboBox" name="canFdBox"/> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="dataBitrateLabel"> + <property name="text"> + <string>Data Bitrate</string> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="BitRateBox" name="dataBitrateBox"/> + </item> + </layout> + </widget> </item> - <item> - <widget class="QPushButton" name="cancelButton"> - <property name="text"> - <string>Cancel</string> + <item row="1" column="0"> + <widget class="QGroupBox" name="specifyInterfaceNameBox"> + <property name="title"> + <string>Specify CAN interface name</string> </property> - <property name="autoDefault"> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QComboBox" name="interfaceListBox"> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="2" column="0"> + <widget class="QGroupBox" name="interfacePropertiesBox"> + <property name="enabled"> <bool>false</bool> </property> + <property name="title"> + <string>CAN interface properties</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="descriptionLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="serialNumberLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="aliasLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="channelLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="isFlexibleDataRateCapable"> + <property name="text"> + <string>Flexible Data Rate</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="isVirtual"> + <property name="text"> + <string>Virtual</string> + </property> + </widget> + </item> + </layout> </widget> </item> - <item> - <widget class="QPushButton" name="okButton"> + <item row="3" column="0"> + <widget class="QCheckBox" name="useConfigurationBox"> <property name="text"> - <string>OK</string> + <string>Custom configuration</string> </property> - <property name="autoDefault"> - <bool>false</bool> - </property> - <property name="default"> - <bool>true</bool> + </widget> + </item> + <item row="4" column="0" colspan="2"> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>GUI Settings</string> </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QCheckBox" name="ringBufferBox"> + <property name="toolTip"> + <string><html><head/><body><p>Use ring buffer in table view model</p></body></html></string> + </property> + <property name="text"> + <string>Use ring buffer</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="ringBufferLimitBox"> + <property name="toolTip"> + <string><html><head/><body><p>Limit of ring buffer in table view model</p></body></html></string> + </property> + <property name="minimum"> + <number>10</number> + </property> + <property name="maximum"> + <number>10000000</number> + </property> + <property name="singleStep"> + <number>10</number> + </property> + <property name="stepType"> + <enum>QAbstractSpinBox::AdaptiveDecimalStepType</enum> + </property> + <property name="value"> + <number>1000</number> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="autoscrollBox"> + <property name="toolTip"> + <string><html><head/><body><p>Scroll to bottom table view on each portion of received frames</p></body></html></string> + </property> + <property name="text"> + <string>Autoscroll</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> </widget> </item> + <item row="5" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>96</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="cancelButton"> + <property name="text"> + <string>Cancel</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="okButton"> + <property name="text"> + <string>OK</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> </layout> </item> - <item row="0" column="0"> - <widget class="QGroupBox" name="selectPluginBox"> - <property name="title"> - <string>Select CAN plugin</string> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <widget class="QComboBox" name="pluginListBox"/> - </item> - </layout> - </widget> - </item> - <item row="0" column="1" rowspan="5"> - <widget class="QGroupBox" name="configurationBox"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="title"> - <string>Specify Configuration</string> - </property> - <layout class="QGridLayout" name="gridLayout_4"> - <item row="0" column="0"> - <widget class="QLabel" name="rawFilterLabel"> - <property name="text"> - <string>RAW Filter</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="rawFilterEdit"> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="errorFilterLabel"> - <property name="text"> - <string>Error Filter</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="errorFilterEdit"> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="placeholderText"> - <string>FrameError bits</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="loopbackLabel"> - <property name="text"> - <string>Loopback</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QComboBox" name="loopbackBox"/> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="receiveOwnLabel"> - <property name="text"> - <string>Receive Own</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QComboBox" name="receiveOwnBox"/> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="bitrateLabel"> - <property name="text"> - <string>Bitrate</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="BitRateBox" name="bitrateBox"/> - </item> - <item row="5" column="0"> - <widget class="QLabel" name="canFdLabel"> - <property name="text"> - <string>CAN FD</string> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="QComboBox" name="canFdBox"/> - </item> - <item row="6" column="0"> - <widget class="QLabel" name="dataBitrateLabel"> - <property name="text"> - <string>Data Bitrate</string> - </property> - </widget> - </item> - <item row="6" column="1"> - <widget class="BitRateBox" name="dataBitrateBox"/> - </item> - </layout> - </widget> - </item> - <item row="1" column="0"> - <widget class="QGroupBox" name="specifyInterfaceNameBox"> - <property name="title"> - <string>Specify CAN interface name</string> - </property> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="0" column="0"> - <widget class="QComboBox" name="interfaceListBox"> - <property name="editable"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item row="4" column="0"> - <widget class="QCheckBox" name="useConfigurationBox"> - <property name="text"> - <string>Custom configuration</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QGroupBox" name="interfacePropertiesBox"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="title"> - <string>CAN interface properties</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QLabel" name="descriptionLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="serialNumberLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="aliasLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="channelLabel"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="isFlexibleDataRateCapable"> - <property name="text"> - <string>Flexible Data Rate</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="isVirtual"> - <property name="text"> - <string>Virtual</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> </layout> </widget> <customwidgets> diff --git a/examples/serialbus/can/mainwindow.cpp b/examples/serialbus/can/mainwindow.cpp index e8d688b..298d80a 100644 --- a/examples/serialbus/can/mainwindow.cpp +++ b/examples/serialbus/can/mainwindow.cpp @@ -51,11 +51,14 @@ #include "mainwindow.h" #include "ui_mainwindow.h" #include "connectdialog.h" +#include "receivedframesmodel.h" #include <QCanBus> #include <QCanBusFrame> #include <QCloseEvent> +#include <QDateTime> #include <QDesktopServices> +#include <QLabel> #include <QTimer> MainWindow::MainWindow(QWidget *parent) : @@ -73,10 +76,20 @@ MainWindow::MainWindow(QWidget *parent) : m_written = new QLabel; m_ui->statusBar->addWidget(m_written); + m_received = new QLabel; + m_ui->statusBar->addWidget(m_received); + + m_model = new ReceivedFramesModel(this); + m_model->setQueueLimit(1000); + m_ui->receivedFramesView->setModel(m_model); + initActionsConnections(); QTimer::singleShot(50, m_connectDialog, &ConnectDialog::show); connect(m_busStatusTimer, &QTimer::timeout, this, &MainWindow::busStatus); + m_appendTimer = new QTimer(this); + connect(m_appendTimer, &QTimer::timeout, this, &MainWindow::onAppendFramesTimeout); + m_appendTimer->start(350); } MainWindow::~MainWindow() @@ -102,7 +115,7 @@ void MainWindow::initActionsConnections() }); connect(m_ui->actionQuit, &QAction::triggered, this, &QWidget::close); connect(m_ui->actionAboutQt, &QAction::triggered, qApp, &QApplication::aboutQt); - connect(m_ui->actionClearLog, &QAction::triggered, m_ui->receivedMessagesEdit, &QTextEdit::clear); + connect(m_ui->actionClearLog, &QAction::triggered, m_model, &ReceivedFramesModel::clear); connect(m_ui->actionPluginDocumentation, &QAction::triggered, this, []() { QDesktopServices::openUrl(QUrl("http://doc.qt.io/qt-5/qtcanbus-backends.html#can-bus-plugins")); }); @@ -127,6 +140,11 @@ void MainWindow::connectDevice() { const ConnectDialog::Settings p = m_connectDialog->settings(); + if (p.useModelRingBuffer) + m_model->setQueueLimit(p.modelRingBufferSize); + else + m_model->setQueueLimit(0); + QString errorString; m_canDevice.reset(QCanBus::instance()->createDevice(p.pluginName, p.deviceInterfaceName, &errorString)); @@ -263,13 +281,14 @@ void MainWindow::processReceivedFrames() return; while (m_canDevice->framesAvailable()) { + m_numberFramesReceived++; const QCanBusFrame frame = m_canDevice->readFrame(); - QString view; + QString data; if (frame.frameType() == QCanBusFrame::ErrorFrame) - view = m_canDevice->interpretErrorFrame(frame); + data = m_canDevice->interpretErrorFrame(frame); else - view = frame.toString(); + data = QLatin1String(frame.payload().toHex(' ').toUpper()); const QString time = QString::fromLatin1("%1.%2 ") .arg(frame.timeStamp().seconds(), 10, 10, QLatin1Char(' ')) @@ -277,7 +296,10 @@ void MainWindow::processReceivedFrames() const QString flags = frameFlags(frame); - m_ui->receivedMessagesEdit->append(time + flags + view); + const QString id = QString::number(frame.frameId(), 16); + const QString dlc = QString::number(frame.payload().size()); + + m_model->appendFrame(QStringList({QString::number(m_numberFramesReceived), time, flags, id, dlc, data})); } } @@ -288,3 +310,16 @@ void MainWindow::sendFrame(const QCanBusFrame &frame) const m_canDevice->writeFrame(frame); } + +void MainWindow::onAppendFramesTimeout() +{ + if (!m_canDevice) + return; + + if (m_model->needUpdate()) { + m_model->update(); + if (m_connectDialog->settings().useAutoscroll) + m_ui->receivedFramesView->scrollToBottom(); + m_received->setText(tr("%1 frames received").arg(m_numberFramesReceived)); + } +} diff --git a/examples/serialbus/can/mainwindow.h b/examples/serialbus/can/mainwindow.h index 2a7bbf0..ca1a512 100644 --- a/examples/serialbus/can/mainwindow.h +++ b/examples/serialbus/can/mainwindow.h @@ -52,10 +52,10 @@ #define MAINWINDOW_H #include <QCanBusDevice> - #include <QMainWindow> class ConnectDialog; +class ReceivedFramesModel; QT_BEGIN_NAMESPACE @@ -75,7 +75,7 @@ class MainWindow : public QMainWindow public: explicit MainWindow(QWidget *parent = nullptr); - ~MainWindow(); + ~MainWindow() override; private slots: void processReceivedFrames(); @@ -85,6 +85,7 @@ private slots: void busStatus(); void disconnectDevice(); void processFramesWritten(qint64); + void onAppendFramesTimeout(); protected: void closeEvent(QCloseEvent *event) override; @@ -93,12 +94,16 @@ private: void initActionsConnections(); qint64 m_numberFramesWritten = 0; + qint64 m_numberFramesReceived = 0; Ui::MainWindow *m_ui = nullptr; QLabel *m_status = nullptr; QLabel *m_written = nullptr; + QLabel *m_received = nullptr; ConnectDialog *m_connectDialog = nullptr; std::unique_ptr<QCanBusDevice> m_canDevice; QTimer *m_busStatusTimer = nullptr; + QTimer *m_appendTimer = nullptr; + ReceivedFramesModel *m_model = nullptr; }; #endif // MAINWINDOW_H diff --git a/examples/serialbus/can/mainwindow.ui b/examples/serialbus/can/mainwindow.ui index f773ffb..b67490d 100644 --- a/examples/serialbus/can/mainwindow.ui +++ b/examples/serialbus/can/mainwindow.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>551</width> - <height>481</height> + <width>634</width> + <height>527</height> </rect> </property> <property name="windowTitle"> @@ -27,51 +27,50 @@ <property name="title"> <string>Received CAN messages</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_4"> - <item> - <widget class="QLabel" name="label_3"> - <property name="font"> - <font> - <family>Courier</family> - <kerning>false</kerning> - </font> - </property> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="text"> - <string>Timestamp Flags CAN-ID DLC Data</string> - </property> - </widget> - </item> - <item> - <widget class="QTextEdit" name="receivedMessagesEdit"> - <property name="font"> - <font> - <family>Courier</family> - <kerning>false</kerning> - </font> - </property> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="undoRedoEnabled"> - <bool>false</bool> - </property> - <property name="lineWrapMode"> - <enum>QTextEdit::NoWrap</enum> - </property> - <property name="readOnly"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="busStatus"> - <property name="text"> - <string/> - </property> - </widget> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="ReceivedFramesView" name="receivedFramesView"> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> + </property> + <property name="showDropIndicator" stdset="0"> + <bool>false</bool> + </property> + <property name="dragDropOverwriteMode"> + <bool>false</bool> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="busStatus"> + <property name="text"> + <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> + </layout> + </item> + </layout> </item> </layout> </widget> @@ -83,7 +82,7 @@ <rect> <x>0</x> <y>0</y> - <width>551</width> + <width>634</width> <height>22</height> </rect> </property> @@ -189,6 +188,11 @@ <header location="global">sendframebox.h</header> <container>1</container> </customwidget> + <customwidget> + <class>ReceivedFramesView</class> + <extends>QTableView</extends> + <header location="global">receivedframesview.h</header> + </customwidget> </customwidgets> <resources> <include location="can.qrc"/> diff --git a/examples/serialbus/can/receivedframesmodel.cpp b/examples/serialbus/can/receivedframesmodel.cpp new file mode 100644 index 0000000..520e73e --- /dev/null +++ b/examples/serialbus/can/receivedframesmodel.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Evgeny Shtanov <shtanov_evgenii@mail.ru> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the QtSerialBus module. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "receivedframesmodel.h" + +#include <iterator> +#include <QSize> + +constexpr int ColumnAlignment[] = { + Qt::AlignRight | Qt::AlignVCenter, + Qt::AlignRight | Qt::AlignVCenter, + Qt::AlignCenter, + Qt::AlignRight | Qt::AlignVCenter, + Qt::AlignRight | Qt::AlignVCenter, + Qt::AlignLeft | Qt::AlignVCenter +}; + +ReceivedFramesModel::ReceivedFramesModel(QObject *parent) : QAbstractTableModel(parent) +{ + +} + +bool ReceivedFramesModel::removeRows(int row, int count, const QModelIndex &parent) +{ + beginRemoveRows(parent, row, row + count - 1); + + QList<QStringList>::iterator i_start = m_framesQueue.begin() + row; + QList<QStringList>::iterator i_end = i_start + count; + m_framesQueue.erase(i_start, i_end); + + endRemoveRows(); + + return true; +} + +QVariant ReceivedFramesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if ((role == Qt::DisplayRole) && (orientation == Qt::Horizontal)) { + switch (section) { + case Number: + return tr("#"); + case Timestamp: + return tr("Timestamp"); + case Flags: + return tr("Flags"); + case CanID: + return tr("CAN-ID"); + case DLC: + return tr("DLC"); + case Data: + return tr("Data"); + } + } + + if ((role == Qt::SizeHintRole) && (orientation == Qt::Horizontal)) { + switch (section) { + case Number: + return QSize(80, 25); + case Timestamp: + return QSize(130, 25); + case Flags: + return QSize(25, 25); + case CanID: + return QSize(50, 25); + case DLC: + return QSize(25, 25); + case Data: + return QSize(200, 25); + } + } + + return {}; +} + +QVariant ReceivedFramesModel::data(const QModelIndex &index, int role) const +{ + if (m_framesQueue.empty()) + return {}; + + const int row = index.row(); + const int column = index.column(); + + switch (role) { + case Qt::TextAlignmentRole: + return ColumnAlignment[index.column()]; + case Qt::DisplayRole: + return m_framesQueue.at(row).at(column); + case ClipboardTextRole: + if (index.column() == DLC) + return QString("[%1]").arg(m_framesQueue.at(row).at(column)); + else + return m_framesQueue.at(row).at(column); + default: + return {}; + } +} + +int ReceivedFramesModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_framesQueue.size(); +} + +int ReceivedFramesModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : Count; +} + +void ReceivedFramesModel::appendFrames(const QList<QStringList> &slvector) +{ + m_framesAccumulator.append(slvector); +} + +bool ReceivedFramesModel::needUpdate() const { + return !m_framesAccumulator.empty(); +} + +void ReceivedFramesModel::update() { + if (m_framesAccumulator.empty()) + return; + + if (m_queueLimit) + appendFramesRingBuffer(m_framesAccumulator); + else + appendFramesUnlimited(m_framesAccumulator); + + m_framesAccumulator.clear(); +} + +void ReceivedFramesModel::appendFramesRingBuffer(const QList<QStringList> &slvector) +{ + if (m_queueLimit <= (rowCount() + slvector.size())) { + if (slvector.size() < m_queueLimit) + removeRows(0, rowCount() + slvector.size() - m_queueLimit + 1); + else + clear(); + } + + beginInsertRows(QModelIndex(), rowCount(), rowCount() + slvector.size() - 1); + + if (slvector.size() < m_queueLimit) + m_framesQueue.append(slvector); + else + m_framesQueue.append(slvector.mid(slvector.size() - m_queueLimit)); + + endInsertRows(); +} + +void ReceivedFramesModel::appendFrame(const QStringList &slist) +{ + appendFrames({slist}); +} + +void ReceivedFramesModel::appendFramesUnlimited(const QList<QStringList> &slvector) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount() + slvector.size() - 1); + + m_framesQueue.append(slvector); + + endInsertRows(); +} + +void ReceivedFramesModel::clear() +{ + if (m_framesQueue.count()) { + beginResetModel(); + + m_framesQueue.clear(); + + endResetModel(); + } +} + +void ReceivedFramesModel::setQueueLimit(int limit) +{ + m_queueLimit = limit; + + if (limit && m_framesQueue.size() > limit) + removeRows(0, m_framesQueue.size() - limit); +} diff --git a/examples/serialbus/can/receivedframesmodel.h b/examples/serialbus/can/receivedframesmodel.h new file mode 100644 index 0000000..84efdd6 --- /dev/null +++ b/examples/serialbus/can/receivedframesmodel.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Evgeny Shtanov <shtanov_evgenii@mail.ru> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the QtSerialBus module. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RECEIVEDFRAMESMODEL_H +#define RECEIVEDFRAMESMODEL_H + +#include "common.h" + +#include <QAbstractTableModel> +#include <QCanBusFrame> +#include <QQueue> + +class ReceivedFramesModel : public QAbstractTableModel +{ +public: + explicit ReceivedFramesModel(QObject *parent = nullptr); + + void appendFrame(const QStringList &slist); + void appendFrames(const QList<QStringList> &slvector); + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + void clear(); + void setQueueLimit(int limit = 0); // 0 - unlimited + int getQueueLimit() { return m_queueLimit; } + bool needUpdate() const; + void update(); + +protected: + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + +private: + void appendFramesRingBuffer(const QList<QStringList> &slvector); + void appendFramesUnlimited(const QList<QStringList> &slvector); + +private: + QQueue<QStringList> m_framesQueue; + QList<QStringList> m_framesAccumulator; // Temporary variable to insert frames data + int m_queueLimit = 0; +}; + +#endif // RECEIVEDFRAMESMODEL_H diff --git a/examples/serialbus/can/receivedframesview.cpp b/examples/serialbus/can/receivedframesview.cpp new file mode 100644 index 0000000..9c5524e --- /dev/null +++ b/examples/serialbus/can/receivedframesview.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Evgeny Shtanov <shtanov_evgenii@mail.ru> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the QtSerialBus module. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "common.h" +#include "receivedframesview.h" + +#include <QAction> +#include <QApplication> +#include <QClipboard> +#include <QKeyEvent> +#include <QMenu> + +ReceivedFramesView::ReceivedFramesView(QWidget *parent) + : QTableView(parent) +{ + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, &QWidget::customContextMenuRequested, + [this] (const QPoint &pos) { + QMenu contextMenu(tr("Context menu"), this); + + QAction action1("Copy", this); + QAction action2("Select all", this); + + connect(&action1, &QAction::triggered, this, &ReceivedFramesView::copyRow); + connect(&action2, &QAction::triggered, this, &QAbstractItemView::selectAll); + + if (selectedIndexes().count()) + contextMenu.addAction(&action1); + contextMenu.addAction(&action2); + + contextMenu.exec(mapToGlobal(pos)); + }); +} + +void ReceivedFramesView::setModel(QAbstractItemModel *model) +{ + QTableView::setModel(model); + + for (int i = 0, count = model->columnCount(); i < count; i++) { + const QSize size = model->headerData(i, Qt::Horizontal, Qt::SizeHintRole).value<QSize>(); + setColumnWidth(i, size.width()); + } +} + +void ReceivedFramesView::keyPressEvent(QKeyEvent *event) { + if (event->matches(QKeySequence::Copy)) { + copyRow(); + } else if (event->matches(QKeySequence::SelectAll)) { + selectAll(); + } else { + QTableView::keyPressEvent(event); + } +} + +void ReceivedFramesView::copyRow() { + QClipboard *clipboard = QApplication::clipboard(); + + const QModelIndexList ilist = selectedIndexes(); + + QString strRow; + + for (const QModelIndex &index : ilist) { + strRow += index.data(ClipboardTextRole).toString() + " "; + if (index.column() == model()->columnCount() - 1) + strRow += '\n'; + } + + clipboard->setText(strRow); +} diff --git a/examples/serialbus/can/receivedframesview.h b/examples/serialbus/can/receivedframesview.h new file mode 100644 index 0000000..2d30ba9 --- /dev/null +++ b/examples/serialbus/can/receivedframesview.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Evgeny Shtanov <shtanov_evgenii@mail.ru> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the QtSerialBus module. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RECEIVEDFRAMESVIEW_H +#define RECEIVEDFRAMESVIEW_H + +#include <QTableView> + +class ReceivedFramesView final: public QTableView +{ +public: + explicit ReceivedFramesView(QWidget *parent = nullptr); + void setModel(QAbstractItemModel *model) final; + +protected: + void keyPressEvent(QKeyEvent *event) final; + void copyRow(); +}; + +#endif // RECEIVEDFRAMESVIEW_H diff --git a/examples/serialbus/can/sendframebox.ui b/examples/serialbus/can/sendframebox.ui index 0abeff5..d913e33 100644 --- a/examples/serialbus/can/sendframebox.ui +++ b/examples/serialbus/can/sendframebox.ui @@ -17,6 +17,9 @@ <string/> </property> <layout class="QVBoxLayout" name="verticalLayout_4"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> <item> <widget class="QGroupBox" name="frameTypeBox"> <property name="title"> @@ -118,6 +121,9 @@ </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_3"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> <item> <layout class="QVBoxLayout" name="verticalLayout"> <item> @@ -181,17 +187,11 @@ <item> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> + <widget class="QLabel" name="label"> + <property name="text"> + <string/> </property> - </spacer> + </widget> </item> <item> <widget class="QPushButton" name="sendButton"> |