Qt之股票元件-自選股--列表可以拖拽、右鍵常用選單
目錄
- 一、開頭嘴一嘴
- 二、效果展示
- 三、自選股列表
- 1、列表初始化
- 2、新增Item
- 3、右鍵選單
- 4、拖拽Item
- 5、重新整理資料
- 四、相關文章
原文連結:Qt之股票元件-自選股--列表可以拖拽、右鍵常用選單
一、開頭嘴一嘴
上一篇文章Qt之股票元件-股票檢索--支援搜尋結果預覽、滑鼠、鍵盤操作講述了股票檢索功能,這篇文章我們來看看自選股列表的實現。
如果有需要的朋友可以加我好友,有償提供原始碼、或者也可以進一步提供功能定製
封裝的控制元件,或者demo都是沒有樣式的,所以看著會比較醜一些,不過加樣式也是分分鐘。。。這裡咱可以先看功能,需要即可定製
本篇文章的自選股和大多數炒股軟體一樣,每一條自選都是支援拖拽的,拖拽時滑鼠會跟隨一個拖拽映像,並且滑鼠移動時,會有拖拽提示,告知我們滑鼠釋放時拖拽項將會被插入到哪個位置。除過拖拽之外,自選股列表還支援右鍵選單,都是一樣常用的操作。
右鍵選單包括置頂、置低、刪除、下移一項、上移一項等
本篇文章中不包括的功能也可以提供定製,需求合理即可。
下面來具體說一說這個功能的實現思路,會公開大多數核心程式碼,有需要的同學可以根據思路自行完善整個程式碼。
二、效果展示
如下效果圖所示,是自選股使用上的一個展示效果,具有如下功能
- 搜尋編輯框,支援股票程式碼和股票名稱搜尋
- 搜尋預覽框支援滑鼠hover,並且可以使用鍵盤上下鍵進行當前項切換,單機時支援切換自選股
- 自選股列表,支援拖拽,拖拽時會有拖拽項映像,並示意將要拖拽到哪個位置
- 支援右鍵選單,可以對某一項進行移動,刪除等操作
如果覺著demo比較醜的話,可以看財聯社-產品展示這篇文章中的效果圖
三、自選股列表
接下來就是我們這篇文章的重頭戲了,也是比較複雜的一個內容。
自選股列表我選擇的是使用QListWidget來實現,然後每一個item上在放一個Widget即可,Widget就是我們定製窗體內容,
這裡我們主要講解幾個比較重要的核心內容
1、列表初始化
初始化StockList,實際上自選股列表應該從伺服器拉取,我們這裡作為demo測試,因此就自己模擬了5條資料進行插入。
//已選個股列表
d_ptr->m_pStockList = new StockList;
connect(d_ptr->m_pStockList, &StockList::RowClicked, this, [this](const QString & symbol){
emit RowClicked(symbol);
});
//測試資料 正常情況下 應該是列表自己拉取
OptionalMarketItem item;
for (int i = 1; i <= 5; ++i)
{
item.wstrSymbol = QString("0h000%1").arg(i).toStdWString();
item.wstrName = QString("%1%1%1").arg(i).toStdWString();
item.wstrIndustryName = QString("pingyin%1").arg(i).toStdWString();
d_ptr->m_pStockList->AddItem(item);
}
2、新增Item
往StockList中新增item項時,我們首先需要構造一個標準的QListWidgetItem結構,然後把我們自己定製的ListItem放到這個標準item結構上。
QListWidgetItem * StockList::AddItem(const OptionalMarketItem & data)
{
ListItem * itemWidget = new ListItem;
itemWidget->SetData(data);
QListWidgetItem * item = new QListWidgetItem;
addItem(item);
item->setSizeHint(QSize(0, 50));
setItemWidget(item, itemWidget);
return item;
}
ListItem就是一個普通的QWidget,上邊排列了一些QLabel,用於顯示我們的股票資料。
ListItem介面構造就不過多解釋了,唯一需要說明的就是,我們股票資料傳送變化時,介面上會有紅綠色框的動畫提示,這裡需要呼叫兩行程式碼來實現重新獲取控制元件qss程式碼,並刷洗介面。
this->style()->unpolish(this);
this->style()->polish(this);
3、右鍵選單
本篇文章和上一篇文章的右鍵選單實現方式一樣,都是參考我很早以前寫的Qt之自定義QLineEdit右鍵選單這篇文章,實現預設的contextMenuEvent函式即可。
右鍵選單已經說的很多了,這裡就一筆帶過了,需要的同學可以自己快速的瞅一眼,應該比較容易理解。
void StockList::contextMenuEvent(QContextMenuEvent * event)
{
if (d_ptr->m_AllowMenu == false)
{
return;
}
if (d_ptr->m_ContextMenu == nullptr)
{
d_ptr->m_ContextMenu = new QMenu(this);
d_ptr->m_ContextMenu->setObjectName(QStringLiteral("StockListMenu"));
d_ptr->m_ContextMenu->setFixedWidth(100);
QAction * delAct = new QAction(QStringLiteral("刪除自選股"), d_ptr->m_ContextMenu);
QAction * topAct = new QAction(QStringLiteral("置頂"), d_ptr->m_ContextMenu);
QAction * bottomAct = new QAction(QStringLiteral("置底"), d_ptr->m_ContextMenu);
QAction * upAct = new QAction(QStringLiteral("上移一位"), d_ptr->m_ContextMenu);
QAction * downAct = new QAction(QStringLiteral("下移一位"), d_ptr->m_ContextMenu);
connect(delAct, &QAction::triggered, this, &StockList::DeleteSotck);
connect(topAct, &QAction::triggered, this, &StockList::TopSotck);
connect(bottomAct, &QAction::triggered, this, &StockList::BottomSotck);
connect(upAct, &QAction::triggered, this, &StockList::UpSotck);
connect(downAct, &QAction::triggered, this, &StockList::DownSotck);
d_ptr->m_ContextMenu->addAction(delAct);
d_ptr->m_ContextMenu->addAction(topAct);
d_ptr->m_ContextMenu->addAction(bottomAct);
d_ptr->m_ContextMenu->addAction(upAct);
d_ptr->m_ContextMenu->addAction(downAct);
}
d_ptr->m_ContextMenu->exec(mapToGlobal(event->pos()));
QListWidget::contextMenuEvent(event);
}
以上5個選單,雖然看起來功能相差很多,但是其實處理邏輯基本都是一樣的,先是一個內容結構排序,然後進行重新整理資料到介面上。
為了節省篇幅,我這裡就只介紹置頂一隻股票的操作
置頂的邏輯看起來是這樣的
- 移除當前項
- 並且把當前項item插入到新位置
- 構造一個新的Widget,設定給item
- 把新位置的item設定為當前選中項
- 上傳最新列表到資料中心,或者伺服器
void StockList::TopSotck()
{
QListWidgetItem * item = currentItem();
if (item == nullptr)
{
return;
}
if (row(item) == 0)
{
return;
}
ListItem * itemWidget = ItemWidget(item);
QListWidgetItem * newItem = takeItem(row(item));
insertItem(0, newItem);
ListItem * topWidget = new ListItem;
topWidget->SetData(itemWidget->GetData());
setItemWidget(newItem, topWidget);
if (itemWidget)
{
itemWidget->close();
itemWidget = nullptr;
}
setCurrentItem(newItem);
StorageData();
}
4、拖拽Item
拖拽Item應該算是一個比較難一點兒功能,好在Qt已經為我們實現了一套QDrag事件的回撥方法,也比較好使,如下圖所示,重寫如下4個方法,基本的拖拽事件就能完成了。
但是這裡我麼有選擇預設的這個回撥函式來實現這個功能,其中最大的原因就是,他們的可定製性太侷限了。
我這裡採取的是自己模擬滑鼠拖拽功能,同過重寫如下幾個函式來達到我的目的
virtual void mousePressEvent(QMouseEvent * event) override;
virtual void mouseMoveEvent(QMouseEvent * event) override;
virtual void mouseReleaseEvent(QMouseEvent * event) override;
virtual void enterEvent(QEvent * event) override;
virtual void leaveEvent(QEvent * event) override;
- 滑鼠按下時,主要是記錄了一些內容狀態,方便在滑鼠移動時去做判斷,並決定是否啟用滑鼠拖拽功能
- 滑鼠移動就比較複雜了,進行了各種對比,還需要移動被拖拽項的映像位置,移動那一根水平線的位置
- 滑鼠釋放時,調整整個列表的內容
- 滑鼠進入窗體時,顯示水平標識線
- 滑鼠離開窗體時,隱藏水平標識線
上邊只是粗略的描述了這幾個函式的功能, 因為函式實現體都比較長,因此這裡我也是選擇幾個關鍵點來做以說明。
a、move函式
產生拖拽時,移動滑鼠,我們需要處理很多事件,比如
1、初始化水平表示線和拖拽項映像
if (d_ptr->m_ShotLine == nullptr)
{
InitShotLine();
}
if (d_ptr->m_ShotPicture == nullptr)
{
InitShotLabel();
}
2、拖拽時修改滑鼠狀態
根據拖拽啟動後,滑鼠是否還在當前拖拽項上,設定滑鼠的狀態。
if (ListItem * newWidget = ItemWidget(d_ptr->dragItem))
{
d_ptr->m_ShotPicture->move(QCursor::pos() - d_ptr->dragItemPos);
d_ptr->m_DragRect = visualItemRect(d_ptr->dragItem);
if (d_ptr->m_DragRect.contains(event->pos()) || event->pos().isNull())
{
if ((event->pos() - d_ptr->startPos).manhattanLength() > 5)
{
setCursor(Qt::ForbiddenCursor);
}
}
else
{
setCursor(Qt::ArrowCursor);
}
if (d_ptr->m_ShotPicture->isHidden())
{
d_ptr->m_ShotPicture->show();
}
}
b、release函式
滑鼠釋放時,把拖拽項移動到新的位置
if (ListItem * oldWidget = ItemWidget(d_ptr->dragItem))
{
QListWidgetItem * newItem = new QListWidgetItem;
ListItem * itemWidget = new ListItem;
itemWidget->SetData(oldWidget->GetData());
insertItem(insertPos, newItem);
newItem->setSizeHint(QSize(0, 50));
setItemWidget(newItem, itemWidget);
setCurrentItem(newItem);
oldWidget->deleteLater();
}
5、重新整理資料
全量重新整理資料。在原來的列表上重新整理資料
當原始列表行數不夠時,構造新的行
當原始列表函式多時,移除末尾多的行
void StockList::Update_p(OptionalMarketItemVector data)
{
d_ptr->m_bOnceLoad = true;
disconnect(this, &QListWidget::currentItemChanged, this, &StockList::CurrentItemChanged);
int i = 0;
for (auto iter = data.begin(); iter != data.end(); ++iter, ++i)
{
bool success = false;
if (QListWidgetItem * item = this->item(i))
{
if (ListItem * itemWidget = ItemWidget(item))
{
itemWidget->SetData(*iter);
success = true;
}
}
if (!success)
{
AddItem(*iter);
}
}
if (i < this->count())
{
QListWidgetItem * item = nullptr;
while (item = this->item(i))
{
if (ListItem * itemWidget = ItemWidget(item))
{
itemWidget->close();
itemWidget = nullptr;
}
item = takeItem(i);
delete item;
}
}
if (d_ptr->m_LeftPress == false)
{
RecoveryCurrentItem();
}
connect(this, &QListWidget::currentItemChanged, this, &StockList::CurrentItemChanged);
}
以上講解都是針對自選股列表的實現,內容差不多就這些了,如果有疑問歡迎提出
四、相關文章
財聯社-產品展示
Qt之自定義QLineEdit右鍵選單
Qt之股票元件-股票檢索--支援搜尋結果預覽、滑鼠、鍵盤操作
高仿富途牛牛-元件化(一)-支援頁籤拖拽、增刪、小工具
高仿富途牛牛-元件化(二)-磁力吸附
高仿富途牛牛-元件化(三)-介面美化
高仿富途牛牛-元件化(四)-優秀的時鐘
高仿富途牛牛-元件化(五)-如何去管理炒雞多的小視窗
高仿富途牛牛-元件化(六)-炒雞牛逼的佈局記憶功能(序列化和反序列化)
如果您覺得文章不錯,不妨給個打賞,寫作不易,感謝各位的支援。您的支援是我最大的動力,謝謝!!!很重要--轉載宣告
本站文章無特別說明,皆為原創,版權所有,轉載時請用連結的方式,給出原文出處。同時寫上原作者:朝十晚八 or Twowords
如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。