欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OBS 源窗口 自定义SourceListWidget 列表分析

程序员文章站 2022-07-08 22:19:06
...

前面分析了源窗口 信号槽的关联 及触发机制

下面这篇文章主要分析下UI层 自定义的源列表

OBS 源窗口 自定义SourceListWidget 列表分析

 

OBS 源窗口 自定义SourceListWidget 列表分析

 

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按钮

OBS 源窗口 自定义SourceListWidget 列表分析

然后又定义了一个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(&params, stack, sizeof(stack));
    calldata_set_ptr(&params, "scene", item->parent);
    calldata_set_ptr(&params, "item", item);

    signal_handler_signal(item->parent->source->context.signals,
            "item_remove", &params);
}
 

最后,是关联两个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的相关信息,比如源名字信息

 

OBS 源窗口 自定义SourceListWidget 列表分析

 

比如,遍历源

	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