# HG changeset patch # User Hasan Yavuz ÖZDERYA # Date 2015-09-13 10:43:10 # Node ID 3d74de1a88e6c6ab475655c4d68e1b844647a832 # Parent 94f7460166ef48e7a5cdb3fb105925ea34f3a6c8 # Parent 1f38d055f6dc1acee67fdc45506f63b600fdd550 Merge with portlist diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,8 +51,9 @@ endif (QWT_USE_STATIC) # includes include_directories(${QWT_INCLUDE_DIR}) -# wrap UI files +# wrap UI and resource files qt5_wrap_ui(UI_FILES mainwindow.ui portcontrol.ui about_dialog.ui) +qt5_add_resources(RES_FILES misc/icons.qrc) add_executable(${PROGRAM_NAME} WIN32 main.cpp @@ -64,7 +65,9 @@ add_executable(${PROGRAM_NAME} WIN32 framebuffer.cpp scalepicker.cpp scalezoomer.cpp + portlist.cpp ${UI_FILES} + ${RES_FILES} misc/windows_icon.rc ) diff --git a/misc/bluetooth_icon.png b/misc/bluetooth_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d6cd9b54fc3c88c1a55a14a5619e7ed7ab34fe GIT binary patch literal 2722 zc${rkX;_n27CzrcAb~(g5F|>J5QY(Cbwxxa!BE917+gRE0avJFWfiDc@`Y4k1VzWS zV}w+n5dm2RX&aWISdplpJzt#pGtd0U^WN{=bKmpcdvngYf0KnT zvay1o|2%vu~3xwFT)0+USz6g~{R%G9Kao=WZtp9lF+U*-3KKBYdcBOM> zYr(jV$_{9y+^FrB`B)v8hSq{;BgeNk)$_2(Z{|-9&;0WvJCvEo& z-%Sl}n({FKJ=nAd;VxANpqyX9UZmrj6k73;cBAOou#+`YipOO~1vCDE+l{jkyO4W6 ziQ_8%jFB*Af!GXbFr-u~AUe{v;cCCnp#BUCeNUmwN_vJ%bkt8rx}(Sv$y9R*S6s6f zBPQg*hT9IvZ;iBpX0ifm4!Lh*7Lst0L`9RFK$7K1B1V!lnxp_p!f6s0hOjW0c9lT= zX_Q0|Xjq1ZIYqD`f{kmLcrLqfZ7-MH7cvXi3h?^fajoJPmU#Ry3aI4~9gnyuV0OUQ zx?5dt=(9zgqPum;14mw`(sb2^ybo{3?#jdE!mKo!lkG97;n;}d+19KAYCQ&9C4He@ zvm?W$|B`{1LZ1-ZeR9K(-9GEan$s96+>-X<3ftA!=R4Nsu9-!?q}2p>GOM2KFN{hq z<109{L1bW7J^Gd%m0UAk;X@lp4zsGaO+WCL-%sDw?G~4z(m~M?Wu(r9ypLzDWve)= z6*-K#7TU4T_omHFa=7AH9RsTF;JhwLWok?Iak6*2#WDeB^&!3ab33?{=o>nwzW&3S z3mhUP=gHNBws&@3+nHfYT|0YDymA(UT|TKHaJbOG zhvCG{VSQ?ithIJ4A}9d;^TE*T;<4DrzI#*BAr&2mi#hmhrL zs^U?@2Ynj0!@-i346-)A^I{~2>YTAV7~VvD-&;Y*vS;sSY2(U#e_RLQK?jl;32g@) z3(H8YF@y)+mHBBRH1Y}E*)%^5?a70%%3Wt?Kv546y2f>44SI%g;mau7FBSp6 zjYp~nIwe&c(EBfhd9kz39Kx8pSX8m6#kr}t$etK{Wza=3s39j;$c>wDuVo->pe14S z35zOA%+IRHd1Mnc=pi1+L7VQmoH?f zi#=Tj`;jAzN5xg|*1pdv9M&7-V6T~nNjda%&ciD(mEg4amy0xIX8g}w<(Qmg}Y zH(}Jcw0S#gh5QI7~( zhVm7I9;D7uF5t(DVE&M$B*l@`*_PT_XSqVlV5nq>OX`@V;{~>^Fn@5TWYC`ME%vq9 zRmOwl9jUTrJc>tMO8rKL_L`yRVODpGkhI}KP>uD#gAvV09G6IEXuB$vnvvg$d1<}{ z)HJeJzx%r?mUgAH6GsS>t0&&7fY`Uw*16KxwYJJyd)?V{wh-=APyWpSXN*Vdxb)>N z4kT+0>o1WE@wE!#g_XARCdKB#e9wHm|H^o_j~E)xOuqaQo@a+;6ZHSaeF|CI-}umy zA^y2qp|a9mxmm#EQom<%^cx_vE%KfMRE;_Kk4t$pOFxd$s~WdZ#?qGG+-u9FW`x^_ z-C#;7@xTS%A8$4;A?A#E`LVQH%Y2(xLuX&2-5z*;GUZnXGnsu!*q`V`FpshkM||~J z1WUVpiDOIvg!Dy+2;r~gYm%@aH74GeNUT~kBno7x-4|#MhdXCPRXE;qgOK3~=T4;P z?z_jwVl{Z2css9ClJ#BgS}X^@m6p%?r!wz<0yfH48pGnh5P}1$QKx^1`=I2l?_; zYtY9p?o-#5!~p-}x~a@P^HrGL{<1kq&-ECt?O%P$_BakEv=gR-0r41OCn8?5R|R4| zmu?gIjrPs-H{udR5)Pg$0 z7Gj^a(h7=t?65zJAl)xF2G?CDjw**@Oz#bPZZfvrmGI=hWg~&p-eX~sfhL-&#$@84 zX^Y8=mSi%GH))~0&{S+PA=j%8XXd6)x=30VZf%DH6~LF< + + usb_icon.png + bluetooth_icon.png + + diff --git a/misc/usb_icon.png b/misc/usb_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..679f4171a1072b39efa1508487542d856e143be2 GIT binary patch literal 816 zc$@(+1JC@4P)pe5) z%$#%QIzC@`;NE-Q^Sr2WwoH6!w>1nqH;9&JKt6?54N{jeUzC7M)e43oZ3sniM zlIL(sf)z9?iiA zI3#Uhlkqfu7<1pYN|aW>65KLzH9kIxk3O6jGydbM#O7oOAIf>&IV0bev?=5MIrwJG zxX-5{yezg$T7Jf=b^{uF>q2ZO;Iy?KEW@XGyZ-$<9Fg|_nWRxf*Wec%#%|nf9r%u^ zA5tDSyMr&(>)D+A!JIYp)UB6h3S5w)lc!jm#s>u_U4+e2n)KtklyWx;`YFmVM(Uqy zQd;iBXuH9Ai{QJ{5VTf05!UbrUYv%nJT!{nIq6I?A$hjJVyVgRc%u3g1nr77zmJ2X@6gozs;Z_2JuUsWr!T1GS%atbT)d>dP5mIk25 z_#jNhrWAl~ISJPAnN)m#@8rbY)@VFaw?$Grt(9f?w#*5;EdUKhwLh+wzH4=B0J8W8 uyE3l*+DRcn5ClOG1VIo4K@bGNk!`6 #include +#include #include #include "utils.h" @@ -41,24 +42,33 @@ PortControl::PortControl(QSerialPort* po openAction.setToolTip("Open Port (F2)"); QObject::connect(&openAction, &QAction::triggered, this, &PortControl::openActionTriggered); + + portToolBar.addWidget(&tbPortList); portToolBar.addAction(&openAction); + // setup port selection widgets + tbPortList.setModel(&portList); + ui->cbPortList->setModel(&portList); + QObject::connect(ui->cbPortList, + SELECT::OVERLOAD_OF(&QComboBox::activated), + this, &PortControl::onCbPortListActivated); + QObject::connect(&tbPortList, + SELECT::OVERLOAD_OF(&QComboBox::activated), + this, &PortControl::onTbPortListActivated); + QObject::connect(ui->cbPortList, + SELECT::OVERLOAD_OF(&QComboBox::activated), + this, &PortControl::selectPort); + QObject::connect(&tbPortList, + SELECT::OVERLOAD_OF(&QComboBox::activated), + this, &PortControl::selectPort); + // setup buttons QObject::connect(ui->pbReloadPorts, &QPushButton::clicked, this, &PortControl::loadPortList); ui->pbOpenPort->setDefaultAction(&openAction); - // TODO: port name coming from combobox is dirty, create a separate layer of signals - // that will sanitize this information - QObject::connect(ui->cbPortList, - SELECT::OVERLOAD_OF(&QComboBox::activated), - this, &PortControl::selectPort); - - QObject::connect(ui->cbPortList, - SELECT::OVERLOAD_OF(&QComboBox::activated), - this, &PortControl::onPortNameChanged); - + // setup baud rate selection widget QObject::connect(ui->cbBaudRate, SELECT::OVERLOAD_OF(&QComboBox::activated), this, &PortControl::selectBaudRate); @@ -118,36 +128,13 @@ PortControl::~PortControl() void PortControl::loadPortList() { - QString currentSelection = ui->cbPortList->currentText(); - - ui->cbPortList->clear(); - - discoveredPorts.clear(); - for (auto port : QSerialPortInfo::availablePorts()) + QString currentSelection = ui->cbPortList->currentData(PortNameRole).toString(); + portList.loadPortList(); + int index = portList.indexOf(currentSelection); + if (index >= 0) { - QString pName = port.portName(); - if (!port.description().isEmpty()) pName += QString(" ") + port.description(); - if (port.hasProductIdentifier()) - { - QString vID = QString("%1").arg(port.vendorIdentifier(), 4, 16, QChar('0')); - QString pID = QString("%1").arg(port.productIdentifier(), 4, 16, QChar('0')); - pName = pName + " [" + vID + ":" + pID + "]"; - } - ui->cbPortList->addItem(pName); - discoveredPorts << port.portName(); - } - - ui->cbPortList->addItems(userEnteredPorts); - - // find current selection in the new list, maybe it doesn't exist anymore? - int currentSelectionIndex = ui->cbPortList->findText(currentSelection); - if (currentSelectionIndex >= 0) - { - ui->cbPortList->setCurrentIndex(currentSelectionIndex); - } - else // our port doesn't exist anymore, close port if it's open - { - if (serialPort->isOpen()) togglePort(); + ui->cbPortList->setCurrentIndex(index); + tbPortList.setCurrentIndex(index); } } @@ -230,11 +217,26 @@ void PortControl::togglePort() } else { - // port name may contain description - QString portName = ui->cbPortList->currentText().split(" ")[0]; - keepPortName(portName); + // we get the port name from the edit text, which may not be + // in the portList if user hasn't pressed Enter + // Also note that, portText may not be the `portName` + QString portText = ui->cbPortList->currentText(); + QString portName; + int portIndex = portList.indexOf(portText); + if (portIndex < 0) // not in list, add to model and update the selections + { + portList.appendRow(new PortListItem(portText)); + ui->cbPortList->setCurrentIndex(portList.rowCount()-1); + tbPortList.setCurrentIndex(portList.rowCount()-1); + portName = portText; + } + else + { + // get the port name from the data field + portName = static_cast(portList.item(portIndex))->portName(); + } - serialPort->setPortName(portName); + serialPort->setPortName(ui->cbPortList->currentData(PortNameRole).toString()); // open port if (serialPort->open(QIODevice::ReadWrite)) @@ -276,24 +278,6 @@ void PortControl::enableSkipByte(bool en ui->pbSkipByte->setDisabled(enabled); } -void PortControl::keepPortName(QString portName) -{ - if(!discoveredPorts.contains(portName) && - !userEnteredPorts.contains(portName)) - { - userEnteredPorts << portName; - } - if(ui->cbPortList->findText(portName) < 0) - { - ui->cbPortList->addItem(portName); - } -} - -void PortControl::onPortNameChanged(QString portName) -{ - keepPortName(portName); -} - QToolBar* PortControl::toolBar() { return &portToolBar; @@ -303,3 +287,13 @@ void PortControl::openActionTriggered(bo { togglePort(); } + +void PortControl::onCbPortListActivated(int index) +{ + tbPortList.setCurrentIndex(index); +} + +void PortControl::onTbPortListActivated(int index) +{ + ui->cbPortList->setCurrentIndex(index); +} diff --git a/portcontrol.h b/portcontrol.h --- a/portcontrol.h +++ b/portcontrol.h @@ -26,6 +26,9 @@ #include #include #include +#include + +#include "portlist.h" namespace Ui { class PortControl; @@ -53,10 +56,8 @@ private: QToolBar portToolBar; QAction openAction; - - QStringList discoveredPorts; // list of port names returned by availablePorts - QStringList userEnteredPorts; // list of port names entered by user - void keepPortName(QString portName); // if it's not already in userEn. or discv. list + QComboBox tbPortList; + PortList portList; public slots: void loadPortList(); @@ -72,9 +73,11 @@ public slots: void selectFlowControl(int flowControl); // flowControl must be one of QSerialPort::FlowControl private slots: - void onPortNameChanged(QString portName); void openActionTriggered(bool checked); + void onCbPortListActivated(int index); + void onTbPortListActivated(int index); + signals: void skipByteRequested(); void portToggled(bool open); diff --git a/portlist.cpp b/portlist.cpp new file mode 100644 --- /dev/null +++ b/portlist.cpp @@ -0,0 +1,117 @@ +/* + Copyright © 2015 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#include +#include +#include + +#include "portlist.h" + +PortListItem::PortListItem(QString name, QString description, quint16 vid, quint16 pid) +{ + construct(name, description, vid, pid); +} + +PortListItem::PortListItem(QSerialPortInfo* portInfo) +{ + if (portInfo->hasProductIdentifier()) + { + construct(portInfo->portName(), + portInfo->description(), + portInfo->vendorIdentifier(), + portInfo->productIdentifier()); + } + else + { + construct(portInfo->portName()); + } +} + +void PortListItem::construct(QString name, QString description, quint16 vid, quint16 pid) +{ + QString text = name; + if (!description.isEmpty()) + { + text += QString(" ") + description; + } + if (vid && pid) + { + text += QString("[%1:").arg(vid, 4, 16, QChar('0')); + text += QString("%1]").arg(pid, 4, 16, QChar('0')); + setIcon(QIcon(":/usb_icon.png")); + } + else if (name.contains("rfcomm")) + { + setIcon(QIcon(":/bluetooth_icon.png")); + } + setText(text); + setData(name, PortNameRole); +} + +QString PortListItem::portName() +{ + return data(PortNameRole).toString(); +} + +PortList::PortList(QObject* parent) : + QStandardItemModel(parent) +{ + loadPortList(); + + // we have to use macro based notation to be able to disconnect + QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(onRowsInserted(QModelIndex, int, int))); +} + +void PortList::loadPortList() +{ + clear(); + + disconnect(this, SLOT(onRowsInserted(QModelIndex,int,int))); + for (auto portInfo : QSerialPortInfo::availablePorts()) + { + appendRow(new PortListItem(&portInfo)); + } + for (auto portName : userEnteredPorts) + { + appendRow(new PortListItem(portName)); + } + QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(onRowsInserted(QModelIndex, int, int))); +} + +int PortList::indexOf(QString portText) +{ + for (int i = 0; i < rowCount(); i++) + { + if (item(i)->text() == portText) + { + return i; + } + } + return -1; // not found +} + +void PortList::onRowsInserted(QModelIndex parent, int start, int end) +{ + PortListItem* newItem = static_cast(item(start)); + QString portName = newItem->text(); + newItem->setData(portName, PortNameRole); + userEnteredPorts << portName; +} diff --git a/portlist.h b/portlist.h new file mode 100644 --- /dev/null +++ b/portlist.h @@ -0,0 +1,62 @@ +/* + Copyright © 2015 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#ifndef PORTLIST_H +#define PORTLIST_H + +#include +#include +#include +#include + +enum PortListRoles +{ + PortNameRole = Qt::UserRole+1 // portName as QString +}; + +class PortListItem : public QStandardItem +{ +public: + PortListItem(QString name, QString description="", quint16 vid=0, quint16 pid=0); + PortListItem(QSerialPortInfo* portInfo); + + QString portName(); // returns only the port name + +private: + // common constructor + void construct(QString name, QString description="", quint16 vid=0, quint16 pid=0); +}; + +class PortList : public QStandardItemModel +{ + Q_OBJECT +public: + PortList(QObject* parent=0); + + void loadPortList(); + int indexOf(QString portText); // return -1 if not found + +private: + QStringList userEnteredPorts; + +private slots: + void onRowsInserted(QModelIndex parent, int start, int end); +}; + +#endif // PORTLIST_H diff --git a/serialplot.pro b/serialplot.pro --- a/serialplot.pro +++ b/serialplot.pro @@ -42,6 +42,7 @@ SOURCES += main.cpp\ framebuffer.cpp \ scalepicker.cpp \ scalezoomer.cpp + portlist.cpp HEADERS += mainwindow.h \ utils.h \ @@ -53,6 +54,7 @@ HEADERS += mainwindow.h \ framebuffer.h \ scalepicker.h \ scalezoomer.h + portlist.h FORMS += mainwindow.ui \ about_dialog.ui \ @@ -61,3 +63,6 @@ FORMS += mainwindow.ui \ INCLUDEPATH += qmake/ CONFIG += c++11 + +RESOURCES += \ + misc/icons.qrc