summaryrefslogtreecommitdiffstats
path: root/examples/serialbus/can
diff options
context:
space:
mode:
authorEvgeny Shtanov <shtanov_evgenii@mail.ru>2021-03-10 22:33:56 +0300
committerEvgeny Shtanov <shtanov_evgenii@mail.ru>2021-03-19 00:48:34 +0300
commita72f9e33dc8fe60ef80eee0ecf97965fc8482fe9 (patch)
tree6af3cd4cbdfdef1a137bb690f076bb15608eb414 /examples/serialbus/can
parent55f97fd0178485bafed1e97eaf0d4ebf91a312b5 (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.txt2
-rw-r--r--examples/serialbus/can/can.pro5
-rw-r--r--examples/serialbus/can/common.h70
-rw-r--r--examples/serialbus/can/connectdialog.cpp8
-rw-r--r--examples/serialbus/can/connectdialog.h3
-rw-r--r--examples/serialbus/can/connectdialog.ui486
-rw-r--r--examples/serialbus/can/mainwindow.cpp45
-rw-r--r--examples/serialbus/can/mainwindow.h9
-rw-r--r--examples/serialbus/can/mainwindow.ui100
-rw-r--r--examples/serialbus/can/receivedframesmodel.cpp226
-rw-r--r--examples/serialbus/can/receivedframesmodel.h90
-rw-r--r--examples/serialbus/can/receivedframesview.cpp117
-rw-r--r--examples/serialbus/can/receivedframesview.h67
-rw-r--r--examples/serialbus/can/sendframebox.ui20
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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use ring buffer in table view model&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Limit of ring buffer in table view model&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Scroll to bottom table view on each portion of received frames&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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">