OBS 源的菜单创建 及添加源的处理
OBS添加各种源的操作
集中在 void OBSBasic::AddSource(const char *id)
这是通过菜单项ACTION 对应的槽函数 跳转到这里的
void OBSBasic::AddSourceFromAction()
{
QAction *action = qobject_cast<QAction*>(sender());
if (!action)
return;
AddSource(QT_TO_UTF8(action->data().toString()));
}
一 各个源的菜单项
OBS中 创建源的菜单是在 QMenu *OBSBasic::CreateAddSourcePopupMenu()
在这里遍历各个源,并将其加入菜单中
while (obs_enum_input_types(idx++, &type)) {
const char *name = obs_source_get_display_name(type);
uint32_t caps = obs_get_source_output_flags(type);
//...
if (g_bMyCustom)
{
if (strcmp(type, "image_source") == 0 || strcmp(type, "monitor_capture") == 0 || strcmp(type, "dshow_input") == 0) //...
{
if ((caps & OBS_SOURCE_DEPRECATED) == 0) {
addSource(popup, type, name);
}
else {
addSource(deprecated, type, name);
foundDeprecated = true;
}
foundValues = true;
}
}
else{
if ((caps & OBS_SOURCE_DEPRECATED) == 0) {
addSource(popup, type, name);
}
else {
addSource(deprecated, type, name);
foundDeprecated = true;
}
foundValues = true;
}
}
/**
* Enumerates all available inputs source types.
*
* Inputs are general source inputs (such as capture sources, device sources,
* etc).
*/
EXPORT bool obs_enum_input_types(size_t idx, const char **id);
我这里遍历项
type name
image_source 图像
color_source 色源
slideshow 图像幻灯片放映
ffmpeg_source 媒体源
img_TransImg 透明图
text_gdiplus 文本 (GDI+)
text_ft2_source 文本 (FreeType 2)
monitor_capture 显示器捕获
window_capture 窗口捕获
game_capture 游戏捕获
dshow_input 视频捕获设备
wasapi_input_capture 音频输入捕获
wasapi_output_capture 音频输出捕获
在这里将每个源添加到菜单中的函数为 addSource(popup, type, name);
QMenu *popup = new QMenu(QTStr("Add"), this);
QMenu *deprecated = new QMenu(QTStr("Deprecated"), popup);
auto getActionAfter = [] (QMenu *menu, const QString &name)
{
if (g_bMyCustom){
return (QAction*)nullptr;
}
QList<QAction*> actions = menu->actions();
for (QAction *menuAction : actions) {
if (menuAction->text().compare(name) >= 0)
return menuAction;
}
return (QAction*)nullptr;
};
auto addSource = [this, getActionAfter] (QMenu *popup,
const char *type, const char *name)
{
QString qname = QT_UTF8(name);
if (strcmp(type, "image_source") == 0){
qname = QStringLiteral("背景图片"); //...
}
QAction *popupItem = new QAction(qname, this);
popupItem->setData(QT_UTF8(type));
connect(popupItem, SIGNAL(triggered(bool)),
this, SLOT(AddSourceFromAction()));
QAction *after = getActionAfter(popup, qname);
popup->insertAction(after, popupItem);
};
其中,创建菜单 并关联槽函数为以下代码
QAction *popupItem = new QAction(qname, this);
popupItem->setData(QT_UTF8(type));
connect(popupItem, SIGNAL(triggered(bool)),
this, SLOT(AddSourceFromAction()));
槽函数的处理 :
void OBSBasic::AddSourceFromAction()
{
QAction *action = qobject_cast<QAction*>(sender());
if (!action)
return;
AddSource(QT_TO_UTF8(action->data().toString()));
}
//... 源属性 新添源
void OBSBasic::AddSource(const char *id) //这里的id 就是上面遍历各源时的type 如 image_source 、dshow_input、monitor_capture
二 通过OBSBasicSourceSelect 对话框,往场景中添加新源
右键添加源时,会先跳出 创建或选择窗口
这个窗口是 OBSBasicSourceSelect
OBS ui文件中,也有这个窗口
class OBSBasic;
class OBSBasicSourceSelect : public QDialog {
Q_OBJECT
private:
std::unique_ptr<Ui::OBSBasicSourceSelect> ui;
const char *id;
static bool EnumSources(void *data, obs_source_t *source);
static void OBSSourceRemoved(void *data, calldata_t *calldata);
static void OBSSourceAdded(void *data, calldata_t *calldata);
private slots:
void on_buttonBox_accepted();
void on_buttonBox_rejected();
void SourceAdded(OBSSource source);
void SourceRemoved(OBSSource source);
public:
OBSBasicSourceSelect(OBSBasic *parent, const char *id);
OBSSource newSource;
static void SourcePaste(const char *name, bool visible, bool duplicate);
};
看看这个类的构造函数
OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_)
: QDialog (parent),
ui (new Ui::OBSBasicSourceSelect),
id (id_)
{
ui->setupUi(this);
ui->sourceList->setAttribute(Qt::WA_MacShowFocusRect, false);
QString placeHolderText{QT_UTF8(GetSourceDisplayName(id))};
QString text{placeHolderText};
int i = 2;
obs_source_t *source = nullptr;
while ((source = obs_get_source_by_name(QT_TO_UTF8(text)))) {
obs_source_release(source);
text = QString("%1 %2").arg(placeHolderText).arg(i++);
}
ui->sourceName->setText(text);
ui->sourceName->setFocus(); //Fixes deselect of text.
ui->sourceName->selectAll();
installEventFilter(CreateShortcutFilter());
if (strcmp(id_, "scene") == 0) {
OBSBasic *main = reinterpret_cast<OBSBasic*>(
App()->GetMainWindow());
OBSSource curSceneSource = main->GetCurrentSceneSource();
ui->selectExisting->setChecked(true);
ui->createNew->setChecked(false);
ui->createNew->setEnabled(false);
ui->sourceName->setEnabled(false);
int count = main->ui->scenes->count();
for (int i = 0; i < count; i++) {
QListWidgetItem *item = main->ui->scenes->item(i);
OBSScene scene = GetOBSRef<OBSScene>(item);
OBSSource sceneSource = obs_scene_get_source(scene);
if (curSceneSource == sceneSource)
continue;
const char *name = obs_source_get_name(sceneSource);
ui->sourceList->addItem(QT_UTF8(name));
}
} else {
obs_enum_sources(EnumSources, this);
}
if (g_bMyCustom)
on_buttonBox_accepted(); //... 默认创建窗口 ,不让窗口显示
}
比如,我们选择添加 视频捕获设备时候,
void OBSBasic::AddSource(const char *id)
{
if (id && *id) {
OBSBasicSourceSelect sourceSelect(this, id);
这里会将视频捕获设备的id dshow_input传递进去,
构造函数中,获得此ID对应的名字
QString placeHolderText{QT_UTF8(GetSourceDisplayName(id))};
static inline const char *GetSourceDisplayName(const char *id)
{
if (strcmp(id, "scene") == 0)
return Str("Basic.Scene");
return obs_source_get_display_name(id);
}
比如,dshow_input对应的名字为 视频捕获设备
然后,根据名字,获得源,进行遍历
source = obs_get_source_by_name(QT_TO_UTF8(text)
while ((source = obs_get_source_by_name(QT_TO_UTF8(text)))) {
obs_source_release(source);
text = QString("%1 %2").arg(placeHolderText).arg(i++);
}
源选择窗口中,会枚举以存在的同类型源名称
这是通过,obs_enum_sources(EnumSources, this); 实现的
/**
* Enumerates all input sources
*
* Callback function returns true to continue enumeration, or false to end
* enumeration.
*
* Use obs_source_get_ref or obs_source_get_weak_source if you want to retain
* a reference after obs_enum_sources finishes
*/
EXPORT void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*),
void *param);
bool OBSBasicSourceSelect::EnumSources(void *data, obs_source_t *source)
{
OBSBasicSourceSelect *window = static_cast<OBSBasicSourceSelect*>(data);
const char *name = obs_source_get_name(source);
const char *id = obs_source_get_id(source);
if (strcmp(id, window->id) == 0)
window->ui->sourceList->addItem(QT_UTF8(name));
return true;
}
buttonBox使用的默认的槽函数
private slots:
void on_buttonBox_accepted();
void on_buttonBox_rejected();
void SourceAdded(OBSSource source);
void SourceRemoved(OBSSource source);
void OBSBasicSourceSelect::on_buttonBox_accepted()
{
bool useExisting = ui->selectExisting->isChecked();
bool visible = ui->sourceVisible->isChecked();
if (useExisting) {
QListWidgetItem *item = ui->sourceList->currentItem();
if (!item)
return;
AddExisting(QT_TO_UTF8(item->text()), visible, false);
} else {
if (ui->sourceName->text().isEmpty()) {
OBSMessageBox::information(this,
QTStr("NoNameEntered.Title"),
QTStr("NoNameEntered.Text"));
return;
}
if (!AddNew(this, id, QT_TO_UTF8(ui->sourceName->text()),
visible, newSource))
return;
}
done(DialogCode::Accepted);
}
看看 加入已经存在这个名字的 源 的方法,是如何处理的
static void AddExisting(const char *name, bool visible, bool duplicate)
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
OBSScene scene = main->GetCurrentScene();
if (!scene)
return;
obs_source_t *source = obs_get_source_by_name(name);
if (source) {
if (duplicate) {
obs_source_t *from = source;
char *new_name = get_new_source_name(name);
source = obs_source_duplicate(from, new_name, false);
bfree(new_name);
obs_source_release(from);
if (!source)
return;
}
AddSourceData data;
data.source = source;
data.visible = visible;
obs_scene_atomic_update(scene, AddSource, &data);
obs_source_release(source);
}
}
看看 创建新的源是如何处理的
bool AddNew(QWidget *parent, const char *id, const char *name,
const bool visible, OBSSource &newSource)
{
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
OBSScene scene = main->GetCurrentScene();
bool success = false;
if (!scene)
return false;
obs_source_t *source = obs_get_source_by_name(name);
if (source) {
OBSMessageBox::information(parent,
QTStr("NameExists.Title"),
QTStr("NameExists.Text"));
} else {
source = obs_source_create(id, name, NULL, nullptr); //创建新源1
if (source) {
AddSourceData data;
data.source = source;
data.visible = visible;
obs_scene_atomic_update(scene, AddSource, &data);
newSource = source;
success = true;
}
}
obs_source_release(source);
return success;
}
这里使用了
/**
* Creates a source of the specified type with the specified settings.
*
* The "source" context is used for anything related to presenting
* or modifying video/audio. Use obs_source_release to release it.
*/
EXPORT obs_source_t *obs_source_create(const char *id, const char *name,
obs_data_t *settings, obs_data_t *hotkey_data);
source = obs_source_create(id, name, NULL, nullptr); //创建初始新源 类型为id 名字name 其它默认
然后,将这个源添加到 AddSourceData 结构体中
struct AddSourceData {
obs_source_t *source;
bool visible;
};
通过 obs_scene_atomic_update 来更新场景
typedef void (*obs_scene_atomic_update_func)(void *, obs_scene_t *scene);
EXPORT void obs_scene_atomic_update(obs_scene_t *scene,
obs_scene_atomic_update_func func, void *data);
static void AddSource(void *_data, obs_scene_t *scene)
{
AddSourceData *data = (AddSourceData *)_data;
obs_sceneitem_t *sceneitem;
sceneitem = obs_scene_add(scene, data->source);
obs_sceneitem_set_visible(sceneitem, data->visible);
}
/** Adds/creates a new scene item for a source */
EXPORT obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source);
使用obs_scene_add往场景中添加一个新的场景项item
使用obs_sceneitem_set_visible 设置该项是否可见
这样通过 OBSBasicSourceSelect 对话框,就往场景中添加了一个新源
三 属性窗口 设置源的属性
我们回看下,OBSBasic添加源的部分
void OBSBasic::AddSource(const char *id)
OBSBasicSourceSelect sourceSelect(this, id);
sourceSelect.exec();
if (sourceSelect.newSource)
{
CreatePropertiesWindow(sourceSelect.newSource, true); //创建属性窗口
}
class OBSPropertiesView;
class OBSBasic;
class OBSBasicProperties : public QDialog {
Q_OBJECT
private:
QPointer<OBSQTDisplay> preview;
OBSBasic *main;
bool acceptClicked;
OBSSource source;
OBSSignal removedSignal;
OBSSignal renamedSignal;
OBSSignal updatePropertiesSignal;
OBSData oldSettings;
OBSPropertiesView *view;
QDialogButtonBox *buttonBox;
QSplitter *windowSplitter;
static void SourceRemoved(void *data, calldata_t *params);
static void SourceRenamed(void *data, calldata_t *params);
static void UpdateProperties(void *data, calldata_t *params);
static void DrawPreview(void *data, uint32_t cx, uint32_t cy);
bool ConfirmQuit();
int CheckSettings();
void Cleanup();
private slots:
void on_buttonBox_clicked(QAbstractButton *button);
public:
OBSBasicProperties(QWidget *parent, OBSSource source_);
~OBSBasicProperties();
void Init();
protected:
virtual void closeEvent(QCloseEvent *event) override;
virtual void reject() override;
};
这个属性窗口不是UI文件生成的,而是通过代码动态生成的
OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_)
: QDialog (parent),
preview (new OBSQTDisplay(this)),
main (qobject_cast<OBSBasic*>(parent)),
acceptClicked (false),
source (source_),
removedSignal (obs_source_get_signal_handler(source),
"remove", OBSBasicProperties::SourceRemoved,
this),
renamedSignal (obs_source_get_signal_handler(source),
"rename", OBSBasicProperties::SourceRenamed,
this),
oldSettings (obs_data_create()),
buttonBox (new QDialogButtonBox(this))
{
int cx = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow",
"cx");
int cy = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow",
"cy");
QPushButton *b;
b = buttonBox->addButton(QTStr("OK"), QDialogButtonBox::AcceptRole);
buttonBox->addButton(QTStr("Cancel"), QDialogButtonBox::RejectRole);
buttonBox->addButton(QTStr("Defaults"), QDialogButtonBox::ResetRole);
buttonBox->setObjectName(QStringLiteral("buttonBox"));
b->setDefault(true);
if (cx > 400 && cy > 400)
resize(cx, cy);
else
resize(720, 580);
QMetaObject::connectSlotsByName(this);
/* The OBSData constructor increments the reference once */
obs_data_release(oldSettings);
OBSData settings = obs_source_get_settings(source);
obs_data_apply(oldSettings, settings);
obs_data_release(settings);
view = new OBSPropertiesView(settings, source,
(PropertiesReloadCallback)obs_source_properties,
(PropertiesUpdateCallback)obs_source_update);
view->setMinimumHeight(150);
preview->setMinimumSize(20, 150);
preview->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding));
// Create a QSplitter to keep a unified workflow here.
windowSplitter = new QSplitter(Qt::Orientation::Vertical, this);
windowSplitter->addWidget(preview);
windowSplitter->addWidget(view);
windowSplitter->setChildrenCollapsible(true);
//windowSplitter->setSizes(QList<int>({ 16777216, 150 }));
windowSplitter->setStretchFactor(0, 3);
windowSplitter->setStretchFactor(1, 1);
setLayout(new QVBoxLayout(this));
layout()->addWidget(windowSplitter);
layout()->addWidget(buttonBox);
layout()->setAlignment(buttonBox, Qt::AlignRight | Qt::AlignBottom);
view->show();
installEventFilter(CreateShortcutFilter());
const char *name = obs_source_get_name(source);
setWindowTitle(QTStr("Basic.PropertiesWindow").arg(QT_UTF8(name)));
obs_source_inc_showing(source);
updatePropertiesSignal.Connect(obs_source_get_signal_handler(source),
"update_properties",
OBSBasicProperties::UpdateProperties,
this);
auto addDrawCallback = [this] ()
{
obs_display_add_draw_callback(preview->GetDisplay(),
OBSBasicProperties::DrawPreview, this);
};
enum obs_source_type type = obs_source_get_type(source);
uint32_t caps = obs_source_get_output_flags(source);
bool drawable_type = type == OBS_SOURCE_TYPE_INPUT ||
type == OBS_SOURCE_TYPE_SCENE;
if (drawable_type && (caps & OBS_SOURCE_VIDEO) != 0)
connect(preview.data(), &OBSQTDisplay::DisplayCreated,
addDrawCallback);
}
分析下这些动态窗口生成过程
1)按钮组
int cx = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow",
"cx");
int cy = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow",
"cy");
QPushButton *b;
b = buttonBox->addButton(QTStr("OK"), QDialogButtonBox::AcceptRole);
buttonBox->addButton(QTStr("Cancel"), QDialogButtonBox::RejectRole);
buttonBox->addButton(QTStr("Defaults"), QDialogButtonBox::ResetRole);
buttonBox->setObjectName(QStringLiteral("buttonBox"));
b->setDefault(true);
QMetaObject::connectSlotsByName(this);
connectSlotsByName给控件建立缺省的信号和槽的连接
2)源settings
初始化列表中,生成一个默认settings
oldSettings (obs_data_create()),
获得源的设置,更新此值
obs_data_release(oldSettings);
OBSData settings = obs_source_get_settings(source);
obs_data_apply(oldSettings, settings);
obs_data_release(settings);
3) 创建属性视图
view = new OBSPropertiesView(settings, source,
(PropertiesReloadCallback)obs_source_properties,
(PropertiesUpdateCallback)obs_source_update);
view->setMinimumHeight(150);
class VScrollArea : public QScrollArea {
Q_OBJECT
public:
inline VScrollArea(QWidget *parent = nullptr)
: QScrollArea(parent)
{
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
protected:
virtual void resizeEvent(QResizeEvent *event) override;
};
class OBSPropertiesView : public VScrollArea {
Q_OBJECT
friend class WidgetInfo;
using properties_delete_t = decltype(&obs_properties_destroy);
using properties_t =
std::unique_ptr<obs_properties_t, properties_delete_t>;
private:
QWidget *widget = nullptr;
properties_t properties;
OBSData settings;
void *obj = nullptr;
std::string type;
PropertiesReloadCallback reloadCallback;
PropertiesUpdateCallback callback = nullptr;
int minSize;
std::vector<std::unique_ptr<WidgetInfo>> children;
std::string lastFocused;
QWidget *lastWidget = nullptr;
bool deferUpdate;
QWidget *NewWidget(obs_property_t *prop, QWidget *widget,
const char *signal);
QWidget *AddCheckbox(obs_property_t *prop);
QWidget *AddText(obs_property_t *prop, QFormLayout *layout,
QLabel *&label);
void AddPath(obs_property_t *prop, QFormLayout *layout, QLabel **label);
void AddInt(obs_property_t *prop, QFormLayout *layout, QLabel **label);
void AddFloat(obs_property_t *prop, QFormLayout *layout,
QLabel**label);
QWidget *AddList(obs_property_t *prop, bool &warning);
void AddEditableList(obs_property_t *prop, QFormLayout *layout,
QLabel *&label);
QWidget *AddButton(obs_property_t *prop);
void AddColor(obs_property_t *prop, QFormLayout *layout, QLabel *&label);
void AddFont(obs_property_t *prop, QFormLayout *layout, QLabel *&label);
void AddFrameRate(obs_property_t *prop, bool &warning,
QFormLayout *layout, QLabel *&label);
void AddProperty(obs_property_t *property, QFormLayout *layout);
void resizeEvent(QResizeEvent *event) override;
void GetScrollPos(int &h, int &v);
void SetScrollPos(int h, int v);
public slots:
void ReloadProperties();
void RefreshProperties();
void SignalChanged();
signals:
void PropertiesResized();
void Changed();
public:
OBSPropertiesView(OBSData settings, void *obj,
PropertiesReloadCallback reloadCallback,
PropertiesUpdateCallback callback,
int minSize = 0);
OBSPropertiesView(OBSData settings, const char *type,
PropertiesReloadCallback reloadCallback,
int minSize = 0);
inline obs_data_t *GetSettings() const {return settings;}
inline void UpdateSettings() {callback(obj, settings);}
inline bool DeferUpdate() const {return deferUpdate;}
};
由属性视图的定义可见,属性视图是一个可垂直滚动的区域
4)预览窗口
初始化列表中
preview (new OBSQTDisplay(this)),
preview->setMinimumSize(20, 150);
preview->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding));
看看预览视图的定义
class OBSQTDisplay : public QWidget {
Q_OBJECT
OBSDisplay display;
void CreateDisplay();
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *event) override;
signals:
void DisplayCreated(OBSQTDisplay *window);
void DisplayResized();
public:
OBSQTDisplay(QWidget *parent = 0, Qt::WindowFlags flags = 0);
virtual QPaintEngine *paintEngine() const override;
inline obs_display_t *GetDisplay() const {return display;}
};
//...................................................................
OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
{
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_StaticContents);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_OpaquePaintEvent);
setAttribute(Qt::WA_DontCreateNativeAncestors);
setAttribute(Qt::WA_NativeWindow);
auto windowVisible = [this] (bool visible)
{
if (!visible)
return;
if (!display) {
CreateDisplay();
} else {
QSize size = GetPixelSize(this);
obs_display_resize(display, size.width(), size.height());
}
};
auto sizeChanged = [this] (QScreen*)
{
CreateDisplay();
QSize size = GetPixelSize(this);
obs_display_resize(display, size.width(), size.height());
};
connect(windowHandle(), &QWindow::visibleChanged, windowVisible);
connect(windowHandle(), &QWindow::screenChanged, sizeChanged);
}
void OBSQTDisplay::CreateDisplay()
{
if (display || !windowHandle()->isExposed())
return;
QSize size = GetPixelSize(this);
gs_init_data info = {};
info.cx = size.width();
info.cy = size.height();
info.format = GS_RGBA;
info.zsformat = GS_ZS_NONE;
QTToGSWindow(winId(), info.window);
display = obs_display_create(&info);
emit DisplayCreated(this);
}
void OBSQTDisplay::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
CreateDisplay();
if (isVisible() && display) {
QSize size = GetPixelSize(this);
obs_display_resize(display, size.width(), size.height());
}
emit DisplayResized();
}
void OBSQTDisplay::paintEvent(QPaintEvent *event)
{
CreateDisplay();
QWidget::paintEvent(event);
}
QPaintEngine *OBSQTDisplay::paintEngine() const
{
return nullptr;
}
值得注意的是: obs场景中的视图,也是以此为基类的
5)将预览视图和属性视图加入分离器中
// Create a QSplitter to keep a unified workflow here.
windowSplitter = new QSplitter(Qt::Orientation::Vertical, this);
windowSplitter->addWidget(preview);
windowSplitter->addWidget(view);
windowSplitter->setChildrenCollapsible(true);
//windowSplitter->setSizes(QList<int>({ 16777216, 150 }));
windowSplitter->setStretchFactor(0, 3);
windowSplitter->setStretchFactor(1, 1);
setLayout(new QVBoxLayout(this));
layout()->addWidget(windowSplitter);
layout()->addWidget(buttonBox);
layout()->setAlignment(buttonBox, Qt::AlignRight | Qt::AlignBottom);
view->show();
const char *name = obs_source_get_name(source);
setWindowTitle(QTStr("Basic.PropertiesWindow").arg(QT_UTF8(name)));
/**
* Increments the 'showing' reference counter to indicate that the source is
* being shown somewhere. If the reference counter was 0, will call the 'show'
* callback.
*/
obs_source_inc_showing(source);
6)信号槽
OBS定义了一个信号槽类
/* signal handler connection */
class OBSSignal {
signal_handler_t *handler;
const char *signal;
signal_callback_t callback;
void *param;
public:
inline OBSSignal()
: handler (nullptr),
signal (nullptr),
callback (nullptr),
param (nullptr)
{}
inline OBSSignal(signal_handler_t *handler_,
const char *signal_,
signal_callback_t callback_,
void *param_)
: handler (handler_),
signal (signal_),
callback (callback_),
param (param_)
{
signal_handler_connect(handler, signal, callback, param);
}
inline void Disconnect()
{
signal_handler_disconnect(handler, signal, callback, param);
handler = nullptr;
signal = nullptr;
callback = nullptr;
param = nullptr;
}
inline ~OBSSignal() {Disconnect();}
inline void Connect(signal_handler_t *handler_,
const char *signal_,
signal_callback_t callback_,
void *param_)
{
Disconnect();
handler = handler_;
signal = signal_;
callback = callback_;
param = param_;
signal_handler_connect(handler, signal, callback, param);
}
OBSSignal(const OBSSignal&) = delete;
OBSSignal(OBSSignal &&other)
: handler (other.handler),
signal (other.signal),
callback(other.callback),
param (other.param)
{
other.handler = nullptr;
other.signal = nullptr;
other.callback = nullptr;
other.param = nullptr;
}
OBSSignal &operator=(const OBSSignal &) = delete;
OBSSignal &operator=(OBSSignal &&other)
{
Disconnect();
handler = other.handler;
signal = other.signal;
callback = other.callback;
param = other.param;
other.handler = nullptr;
other.signal = nullptr;
other.callback = nullptr;
other.param = nullptr;
return *this;
}
};
在属性窗口中,定义了信号类成员变量
OBSSignal updatePropertiesSignal;
/** Returns the signal handler for a source */
EXPORT signal_handler_t *obs_source_get_signal_handler(
const obs_source_t *source);
updatePropertiesSignal.Connect(obs_source_get_signal_handler(source),
"update_properties",
OBSBasicProperties::UpdateProperties,
this);
将 属性窗口的更新属性信号,对应到属性视图VIEW的ReloadProperties 重加载属性这
void OBSBasicProperties::UpdateProperties(void *data, calldata_t *)
{
QMetaObject::invokeMethod(static_cast<OBSBasicProperties*>(data)->view,
"ReloadProperties");
}
void OBSPropertiesView::ReloadProperties()
{
if (obj) {
properties.reset(reloadCallback(obj));
} else {
properties.reset(reloadCallback((void*)type.c_str()));
obs_properties_apply_settings(properties.get(), settings);
}
uint32_t flags = obs_properties_get_flags(properties.get());
deferUpdate = (flags & OBS_PROPERTIES_DEFER_UPDATE) != 0;
RefreshProperties();
}
void OBSPropertiesView::RefreshProperties()
{
int h, v;
GetScrollPos(h, v);
children.clear();
if (widget)
widget->deleteLater();
widget = new QWidget();
QFormLayout *layout = new QFormLayout;
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
widget->setLayout(layout);
QSizePolicy mainPolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred);
//widget->setSizePolicy(policy);
layout->setLabelAlignment(Qt::AlignRight);
obs_property_t *property = obs_properties_first(properties.get());
bool hasNoProperties = !property;
while (property) {
AddProperty(property, layout);
obs_property_next(&property);
}
setWidgetResizable(true);
setWidget(widget);
SetScrollPos(h, v);
setSizePolicy(mainPolicy);
lastFocused.clear();
if (lastWidget) {
lastWidget->setFocus(Qt::OtherFocusReason);
lastWidget = nullptr;
}
if (hasNoProperties) {
QLabel *noPropertiesLabel = new QLabel(NO_PROPERTIES_STRING);
layout->addWidget(noPropertiesLabel);
}
}
四 自定义各种源的添加处理
void OBSBasic::AddSource(const char *id)
比如不必显示选择窗口,不必显示属性窗口
根据视频还是显示器等分别处理等。
推荐阅读