OBS 源窗口 自定义SourceListWidget 列表分析
前面分析了源窗口 信号槽的关联 及触发机制
下面这篇文章主要分析下UI层 自定义的源列表
1 首先,看看添加场景项的函数代码
void OBSBasic::AddSceneItem(OBSSceneItem item)
{
obs_scene_t *scene = obs_sceneitem_get_scene(item);
if (GetCurrentScene() == scene)
InsertSceneItem(item);
SaveProject();
/*
obs_source_t *sceneSource = obs_scene_get_source(scene); //场景 源
obs_source_t *itemSource = obs_sceneitem_get_source(item); //场景中的 子项 源
QString name1= obs_source_get_name(itemSource); //视频捕获设备
QString name2 = obs_source_get_id(itemSource); //dshow_input
QString name3 = obs_source_get_name(sceneSource);//场景
blog(LOG_INFO, "User added source '%s' (%s) to scene '%s'",
obs_source_get_name(itemSource),
obs_source_get_id(itemSource),
obs_source_get_name(sceneSource));
*/
if (!disableSaving) {
obs_source_t *sceneSource = obs_scene_get_source(scene);
obs_source_t *itemSource = obs_sceneitem_get_source(item);
blog(LOG_INFO, "User added source '%s' (%s) to scene '%s'",
obs_source_get_name(itemSource),
obs_source_get_id(itemSource),
obs_source_get_name(sceneSource));
}
}
其中,重要的是 如果是属于当前场景的场景项,则将其插入到源列表中
obs_scene_t *scene = obs_sceneitem_get_scene(item);
if (GetCurrentScene() == scene)
InsertSceneItem(item);
void OBSBasic::InsertSceneItem(obs_sceneitem_t *item)
{
QListWidgetItem *listItem = new QListWidgetItem();
SetOBSRef(listItem, OBSSceneItem(item));
ui->sources->insertItem(0, listItem);
ui->sources->setCurrentRow(0, QItemSelectionModel::ClearAndSelect);
SetupVisibilityItem(ui->sources, listItem, item);
}
QListWidgetItem *listItem = new QListWidgetItem(); 新创建列表项目
template <typename T>
static void SetOBSRef(QListWidgetItem *item, T &&val)
{
item->setData(static_cast<int>(QtDataRole::OBSRef),
QVariant::fromValue(val));
}
SetOBSRef(listItem, OBSSceneItem(item)); //将列表项data关联场景项
ui->sources->insertItem(0, listItem); //插入到列表项中
ui->sources->setCurrentRow(0, QItemSelectionModel::ClearAndSelect);
SetupVisibilityItem(ui->sources, listItem, item); //安装可见性ITem 这是重点
看看这个是怎么实现的
2 设置列表项 自定义widget
void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item,
obs_sceneitem_t *sceneItem)
{
VisibilityItemWidget *baseWidget = new VisibilityItemWidget(sceneItem);
item->setSizeHint(baseWidget->sizeHint());
list->setItemWidget(item, baseWidget);
}
这里,通过list->setItermWidget 将子项设置为一个自定义的widget
下面看看这个自定义的widget如何定义的
#pragma once
#include <QWidget>
#include <QStyledItemDelegate>
#include <obs.hpp>
class QLabel;
class QLineEdit;
class QListWidget;
class QListWidgetItem;
class VisibilityCheckBox;
class LockedCheckBox;
class VisibilityItemWidget : public QWidget {
Q_OBJECT
private:
OBSSceneItem item;
OBSSource source;
QLabel *label = nullptr;
VisibilityCheckBox *vis = nullptr;
LockedCheckBox *lock = nullptr;
QString oldName;
OBSSignal sceneRemoveSignal;
OBSSignal enabledSignal;
OBSSignal renamedSignal;
bool sceneRemoved = false;
bool active = false;
bool selected = false;
static void OBSSceneRemove(void *param, calldata_t *data);
static void OBSSceneItemRemove(void *param, calldata_t *data);
static void OBSSceneItemVisible(void *param, calldata_t *data);
static void OBSSceneItemLocked(void *param, calldata_t *data);
static void OBSSourceEnabled(void *param, calldata_t *data);
static void OBSSourceRenamed(void *param, calldata_t *data);
void DisconnectItemSignals();
private slots:
void VisibilityClicked(bool visible);
void LockClicked(bool locked);
void SourceEnabled(bool enabled);
void SourceRenamed(QString name);
void SourceLocked(bool locked);
public:
VisibilityItemWidget(obs_source_t *source);
VisibilityItemWidget(obs_sceneitem_t *item);
~VisibilityItemWidget();
void SetColor(const QColor &color, bool active, bool selected);
public://...by yujian
QString GetName();
};
class VisibilityItemDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
VisibilityItemDelegate(QObject *parent = nullptr);
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
};
void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item,
obs_source_t *source);
void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item,
obs_sceneitem_t *sceneItem);
看看其构造函数
VisibilityItemWidget::VisibilityItemWidget(obs_sceneitem_t *item_)
: item (item_),
source (obs_sceneitem_get_source(item)),
renamedSignal (obs_source_get_signal_handler(source), "rename",
OBSSourceRenamed, this)
{
const char *name = obs_source_get_name(source);
bool enabled = obs_sceneitem_visible(item);
bool locked = obs_sceneitem_locked(item);
obs_scene_t *scene = obs_sceneitem_get_scene(item);
obs_source_t *sceneSource = obs_scene_get_source(scene);
vis = new VisibilityCheckBox();
vis->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
/* Fix for non-apple systems where the spacing would be too big */
#ifndef __APPLE__
vis->setMaximumSize(16, 16);
#endif
vis->setChecked(enabled);
lock = new LockedCheckBox();
lock->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
/* Fix for non-apple systems where the spacing would be too big */
#ifndef __APPLE__
lock->setMaximumSize(16, 16);
#endif
lock->setChecked(locked);
label = new QLabel(QT_UTF8(name));
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
#ifdef __APPLE__
vis->setAttribute(Qt::WA_LayoutUsesWidgetRect);
lock->setAttribute(Qt::WA_LayoutUsesWidgetRect);
#endif
QHBoxLayout *itemLayout = new QHBoxLayout();
itemLayout->addWidget(vis);
itemLayout->addWidget(lock);
itemLayout->addWidget(label);
itemLayout->setContentsMargins(5, 2, 5, 2);
itemLayout->setSpacing(2);
setLayout(itemLayout);
setStyleSheet("background-color: rgba(255, 255, 255, 0);");
signal_handler_t *signal = obs_source_get_signal_handler(sceneSource);
signal_handler_connect(signal, "remove", OBSSceneRemove, this);
signal_handler_connect(signal, "item_remove", OBSSceneItemRemove,
this);
signal_handler_connect(signal, "item_visible", OBSSceneItemVisible,
this);
connect(vis, SIGNAL(clicked(bool)),
this, SLOT(VisibilityClicked(bool)));
connect(lock, SIGNAL(clicked(bool)),
this, SLOT(LockClicked(bool)));
}
分析下,这个构造函数
在VisibilityItemWidget(obs_sceneitem_t *item_)构造函数中,
vis = new VisibilityCheckBox();
lock = new LockedCheckBox();
构造了两个自定义的checkbox,对应于源的 眼睛和锁checkbox按钮
然后又定义了一个LABEL
label = new QLabel(QT_UTF8(name));
随后,将其放入水平布局中
QHBoxLayout *itemLayout = new QHBoxLayout();
itemLayout->addWidget(vis);
itemLayout->addWidget(lock);
itemLayout->addWidget(label);
itemLayout->setContentsMargins(5, 2, 5, 2);
itemLayout->setSpacing(2);
setLayout(itemLayout); // 设置整个widget布局为水平布局
setStyleSheet("background-color: rgba(255, 255, 255, 0);"); // 设置widget样式
下面,设置widget项对应的一些信号处理
signal_handler_t *signal = obs_source_get_signal_handler(sceneSource);
signal_handler_connect(signal, "remove", OBSSceneRemove, this);
signal_handler_connect(signal, "item_remove", OBSSceneItemRemove,
this);
signal_handler_connect(signal, "item_visible", OBSSceneItemVisible,
this);
在libobs中 进行移除等操作时,会同时发出响应信息,进行UI层列表项的处理
比如:LIBOBS中 场景项的删除,会发出item_remove信号,从而UI层同步删除列表中对应的子项
static inline void signal_item_remove(struct obs_scene_item *item)
{
struct calldata params;
uint8_t stack[128];
calldata_init_fixed(¶ms, stack, sizeof(stack));
calldata_set_ptr(¶ms, "scene", item->parent);
calldata_set_ptr(¶ms, "item", item);
signal_handler_signal(item->parent->source->context.signals,
"item_remove", ¶ms);
}
最后,是关联两个checkbox的点击信号
connect(vis, SIGNAL(clicked(bool)),
this, SLOT(VisibilityClicked(bool)));
connect(lock, SIGNAL(clicked(bool)),
this, SLOT(LockClicked(bool)));
3 自定义checbox的实现
#include <QCheckBox>
#include <QPixmap>
class QPaintEvernt;
class VisibilityCheckBox : public QCheckBox {
Q_OBJECT
QPixmap checkedImage;
QPixmap uncheckedImage;
public:
VisibilityCheckBox();
protected:
void paintEvent(QPaintEvent *event) override;
};
#include <QPaintEvent>
#include <QPixmap>
#include <QPainter>
#include "visibility-checkbox.hpp"
#include <util/c99defs.h>
VisibilityCheckBox::VisibilityCheckBox() : QCheckBox()
{
checkedImage =
QPixmap::fromImage(QImage(":/res/images/visible_mask.png"));
uncheckedImage =
QPixmap::fromImage(QImage(":/res/images/invisible_mask.png"));
setMinimumSize(16, 16);
setStyleSheet("outline: none;");
}
void VisibilityCheckBox::paintEvent(QPaintEvent *event)
{
UNUSED_PARAMETER(event);
QPixmap &pixmap = isChecked() ? checkedImage : uncheckedImage;
QImage image(pixmap.size(), QImage::Format_ARGB32);
QPainter draw(&image);
draw.setCompositionMode(QPainter::CompositionMode_Source);
draw.drawPixmap(0, 0, pixmap.width(), pixmap.height(), pixmap);
draw.setCompositionMode(QPainter::CompositionMode_SourceIn);
draw.fillRect(QRectF(QPointF(0.0f, 0.0f), pixmap.size()),
palette().color(foregroundRole()));
QPainter p(this);
p.drawPixmap(0, 0, image.width(), image.height(),
QPixmap::fromImage(image));
}
4 获得源列表项中自定义widget的相关信息,比如源名字信息
比如,遍历源
for (int i = 0; i < ui->sources->count(); i++) {
QListWidgetItem *listItem = ui->sources->item(i);
QWidget* pDisplayWidget = ui->sources->itemWidget(listItem);
VisibilityItemWidget* pOBSItemWidget = (VisibilityItemWidget*)pDisplayWidget;
QString itemName = pOBSItemWidget->GetName();
//QString itemText = listItem->text(); //空,因为列表项关联的是widget
if (GetOBSRef<OBSSceneItem>(listItem) == item) {
obs_scene_t *scene = obs_sceneitem_get_scene(item);
obs_source_t *sceneSource = obs_scene_get_source(scene);
obs_source_t *itemSource = obs_sceneitem_get_source(item);
obs_source_get_name(itemSource);
obs_source_get_id(itemSource);
obs_source_get_name(sceneSource);
DeleteListItem(ui->sources, listItem);
break;
}
}
上一篇: 出现了一个意外的情况,不能完成所有你在设置中所要求的更改
下一篇: OBS 添加自定义对话框