一、前言
攝像機管理在整個系統中是數據庫部分最核心的,只有添加了攝像機信息,才能進行加載,生成樹狀列表等,攝像機表信息的字段內容改過好幾次,有可能后期還需要修改,目前攝像機表信息主要包括攝像機編號、攝像機名稱、所屬錄像機、廠家、設備地址、媒體地址、雲台地址、主碼流地址、子碼流地址、經緯度、背景地圖、X坐標、Y坐標、用戶姓名、用戶密碼、啟用狀態、安裝位置。其中設備地址、媒體地址、雲台地址、主碼流地址、子碼流地址這五個是在通過onvif搜索設備的時候拿到,碼流地址是必須的,其他三個可以為空,這三個用來雲台控制。背景地圖表示該攝像機對應的圖片地圖模塊的圖片文件名稱,默認在可執行文同一目錄下的map文件夾,XY坐標為對應地圖上的位置,經緯度為在線地圖和離線地圖中的位置,用戶名和密碼為對應onvif登錄到該攝像機的用戶信息,用戶名和密碼信息必須正確,否則在雲台控制的時候會失敗,每次雲台控制下發的數據都帶了用戶信息,用戶信息不正確的話會執行失敗,這個用戶和攝像機的web界面的登錄用戶不一樣,一般都是在攝像機的后台onvif用戶信息的地方進行設置。
分頁組件開源地址:https://gitee.com/feiyangqingyun/QWidgetDemo https://github.com/feiyangqingyun/QWidgetDemo
文件名稱:dbpage
體驗地址:https://gitee.com/feiyangqingyun/QWidgetExe https://github.com/feiyangqingyun/QWidgetExe
文件名稱:bin_video_system.zip
二、功能特點
- 支持16畫面切換,全屏切換等,包括1+4+6+8+9+13+16畫面切換。
- 支持alt+enter全屏,esc退出全屏。
- 自定義信息框+錯誤框+詢問框+右下角提示框。
- 17套皮膚樣式隨意更換,所有樣式全部統一,包括菜單等。
- 雲台儀表盤鼠標移上去高亮,八個方位精准識別。
- 底部畫面工具欄(畫面分割切換+截圖聲音等設置)移上去高亮。
- 可在配置文件更改左上角logo+中文軟件名稱+英文軟件名稱。
- 封裝了百度地圖,三維切換,設備點位,鼠標按下獲取經緯度等。
- 堆棧窗體,每個窗體都是個單獨的qwidget,方便編寫自己的代碼。
- 頂部鼠標右鍵菜單,可動態控制時間CPU+左上角面板+左下角面板+右上角面板+右下角面板的顯示和隱藏,支持恢復默認布局。
- 工具欄可以放置多個小圖標和關閉圖標。
- 左側右側可拖動拉伸,並自動記憶寬高位置,重啟后恢復。
- 雙擊攝像機節點自動播放視頻,雙擊節點自動依次添加視頻,會自動跳到下一個,雙擊父節點自動添加該節點下的所有視頻。
- 攝像機節點拖曳到對應窗體播放視頻,同時支持拖曳本地文件直接播放。
- 視頻畫面窗體支持拖曳交換,瞬間響應。
- 雙擊節點+拖曳節點+拖曳窗體交換位置,均自動更新url.txt。
- 支持從url.txt中加載16通道視頻播放,自動記憶最后通道對應的視頻,軟件啟動后自動打開播放。
- 右下角音量條控件,失去焦點自動隱藏,音量條帶靜音圖標。
- 集成百度地圖,可以添加設備對應位置,自動生成地圖,支持縮放和三維地圖,提供地圖風格選擇,共12種風格。
- 視頻拖動到通道窗體外自動刪除視頻。
- 鼠標右鍵可刪除當前+所有視頻,截圖當前+所有視頻。
- 錄像機管理、攝像機管理,可添加刪除修改導入導出打印信息,立即應用新的設備信息生成樹狀列表,不需重啟。
- 在pro文件中可以自由開啟是否加載地圖。
- 視頻播放可選四種內核自由切換,vlc+ffmpeg+easyplayer+海康sdk,均可在pro中設置。
- 可設置1+4+9+16畫面輪詢,可設置輪詢間隔以及輪詢碼流類型等,直接在主界面底部工具欄右側單擊啟動輪詢按鈕即可,再次單擊停止輪詢。
- 默認超過10秒鍾未操作自動隱藏鼠標指針。
- 支持onvif搜素設備,支持任意onvif攝像機,包括但不限於海康大華宇視天地偉業華為等,支持onvif雲台控制。
- 高度可定制化,用戶可以很方便的在此基礎上衍生自己的功能,支持linux系統。
三、效果圖

四、核心代碼
void frmConfigIpc::on_btnAdd_clicked()
{
int count = model->rowCount();
model->insertRow(count);
int ipcID = model->index(count - 1, 0).data().toInt() + 1;
QString ipcName = model->index(count - 1, 1).data().toString();
QString nvrName = model->index(count - 1, 2).data().toString();
QString ipcType = model->index(count - 1, 3).data().toString();
QString onvifAddr = model->index(count - 1, 4).data().toString();
QString mediaAddr = model->index(count - 1, 5).data().toString();
QString ptzAddr = model->index(count - 1, 6).data().toString();
QString rtspMain = model->index(count - 1, 7).data().toString();
QString rtspSub = model->index(count - 1, 8).data().toString();
QString ipcPosition = model->index(count - 1, 9).data().toString();
QString ipcImage = model->index(count - 1, 10).data().toString();
int ipcX = model->index(count - 1, 11).data().toInt();
int ipcY = model->index(count - 1, 12).data().toInt();
QString userName = model->index(count - 1, 13).data().toString();
QString userPwd = model->index(count - 1, 14).data().toString();
QString ipcEnable = model->index(count - 1, 15).data().toString();
QString ipcAddr = model->index(count - 1, 16).data().toString();
if (ipcName.startsWith("攝像機#")) {
ipcName = QString("攝像機#%1").arg(ipcID);
}
ipcX += App::DeviceWidth;
if (ipcX > App::MapWidth - 100) {
ipcX = 5;
ipcY += App::DeviceHeight;
}
if (count == 0) {
ipcID = 1;
ipcName = "攝像機#1";
nvrName = "錄像機#1";
ipcType = "other";
//onvifAddr = "http://192.168.1.128/onvif/device_service";
//mediaAddr = "http://192.168.1.128//onvif/media_service";
//ptzAddr = "http://192.168.1.128//onvif/ptz_service";
rtspMain = "rtsp://192.168.1.128:554/0";
rtspSub = "rtsp://192.168.1.128:554/1";
ipcPosition = "121.534942,31.307706";
ipcImage = "無";
ipcX = 5;
ipcY = 5;
userName = "admin";
userPwd = "admin";
ipcEnable = "啟用";
ipcAddr = "錄像機的安裝位置";
}
//設置新增加的行默認值
model->setData(model->index(count, 0), ipcID);
model->setData(model->index(count, 1), ipcName);
model->setData(model->index(count, 2), nvrName);
model->setData(model->index(count, 3), ipcType);
model->setData(model->index(count, 4), onvifAddr);
model->setData(model->index(count, 5), mediaAddr);
model->setData(model->index(count, 6), ptzAddr);
model->setData(model->index(count, 7), rtspMain);
model->setData(model->index(count, 8), rtspSub);
model->setData(model->index(count, 9), ipcPosition);
model->setData(model->index(count, 10), ipcImage);
model->setData(model->index(count, 11), ipcX);
model->setData(model->index(count, 12), ipcY);
model->setData(model->index(count, 13), userName);
model->setData(model->index(count, 14), userPwd);
model->setData(model->index(count, 15), ipcEnable);
model->setData(model->index(count, 16), ipcAddr);
ui->tableView->setCurrentIndex(model->index(count, 0));
}
void frmConfigIpc::on_btnSave_clicked()
{
model->database().transaction();
if (model->submitAll()) {
model->database().commit();
DBHelper::loadIpcInfo();
DeviceHelper::initDeviceTree();
DeviceHelper::initVideoIcon();
DeviceHelper::initDeviceMap();
DeviceHelper::initDeviceButton();
AppEvent::Instance()->slot_saveIpcInfo();
} else {
model->database().rollback();
QUIHelper::showMessageBoxError("保存信息失敗,信息不能為空,請重新填寫!");
}
}
void frmConfigIpc::on_btnDelete_clicked()
{
if (ui->tableView->currentIndex().row() < 0) {
QUIHelper::showMessageBoxError("請選擇要刪除的攝像機!");
return;
}
if (QUIHelper::showMessageBoxQuestion("確定要刪除該攝像機嗎? 刪除后不能恢復!") == QMessageBox::Yes) {
int row = ui->tableView->currentIndex().row();
model->removeRow(row);
model->submitAll();
ui->tableView->setCurrentIndex(model->index(model->rowCount() - 1, 0));
DBHelper::loadIpcInfo();
DeviceHelper::initDeviceTree();
DeviceHelper::initVideoIcon();
AppEvent::Instance()->slot_saveIpcInfo();
}
}
void frmConfigIpc::on_btnReturn_clicked()
{
model->revertAll();
}
void frmConfigIpc::on_btnClear_clicked()
{
if (model->rowCount() <= 0) {
return;
}
if (QUIHelper::showMessageBoxQuestion("確定要清空所有攝像機信息嗎?") == QMessageBox::Yes) {
DBHelper::clearIpcInfo();
model->select();
DBHelper::loadIpcInfo();
DeviceHelper::initDeviceTree();
DeviceHelper::initVideoIcon();
AppEvent::Instance()->slot_saveIpcInfo();
}
}
