当用户点击添加源时,OBS会弹出相应的源列表。代码如下:
void OBSBasic::AddSourcePopupMenu(const QPoint &pos)
{
if (!GetCurrentScene()) {
// Tell the user he needs a scene first (help beginners).
OBSMessageBox::information(
this, QTStr("Basic.Main.AddSourceHelp.Title"),
QTStr("Basic.Main.AddSourceHelp.Text"));
return;
}
QScopedPointer<QMenu> popup(CreateAddSourcePopupMenu());
if (popup)
popup->exec(pos);
}
QMenu *OBSBasic::CreateAddSourcePopupMenu()
{
const char *unversioned_type;
const char *type;
bool foundValues = false;
bool foundDeprecated = false;
size_t idx = 0;
QMenu *popup = new QMenu(QTStr("Add"), this);
QMenu *deprecated = new QMenu(QTStr("Deprecated"), popup);
auto getActionAfter = [](QMenu *menu, const QString &name) {
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);
QAction *popupItem = new QAction(qname, this);
popupItem->setData(QT_UTF8(type));
connect(popupItem, SIGNAL(triggered(bool)), this,
SLOT(AddSourceFromAction()));
QIcon icon;
if (strcmp(type, "scene") == 0)
icon = GetSceneIcon();
else
icon = GetSourceIcon(type);
popupItem->setIcon(icon);
QAction *after = getActionAfter(popup, qname);
popup->insertAction(after, popupItem);
};
while (obs_enum_input_types2(idx++, &type, &unversioned_type)) {
const char *name = obs_source_get_display_name(type);
uint32_t caps = obs_get_source_output_flags(type);
if ((caps & OBS_SOURCE_CAP_DISABLED) != 0)
continue;
if ((caps & OBS_SOURCE_DEPRECATED) == 0) {
addSource(popup, unversioned_type, name);
} else {
addSource(deprecated, unversioned_type, name);
foundDeprecated = true;
}
foundValues = true;
}
addSource(popup, "scene", Str("Basic.Scene"));
popup->addSeparator();
QAction *addGroup = new QAction(QTStr("Group"), this);
addGroup->setData(QT_UTF8("group"));
addGroup->setIcon(GetGroupIcon());
connect(addGroup, SIGNAL(triggered(bool)), this,
SLOT(AddSourceFromAction()));
popup->addAction(addGroup);
if (!foundDeprecated) {
delete deprecated;
deprecated = nullptr;
}
if (!foundValues) {
delete popup;
popup = nullptr;
} else if (foundDeprecated) {
popup->addSeparator();
popup->addMenu(deprecated);
}
return popup;
}
每个QAction对应一个AddSourceFromAction
auto addSource = [this, getActionAfter](QMenu *popup, const char *type,
const char *name) {
QString qname = QT_UTF8(name);
QAction *popupItem = new QAction(qname, this);
popupItem->setData(QT_UTF8(type));
connect(popupItem, SIGNAL(triggered(bool)), this,
SLOT(AddSourceFromAction()));
QIcon icon;
if (strcmp(type, "scene") == 0)
icon = GetSceneIcon();
else
icon = GetSourceIcon(type);
popupItem->setIcon(icon);
QAction *after = getActionAfter(popup, qname);
popup->insertAction(after, popupItem);
};
当点击您要创建的源时:
void OBSBasic::AddSource(const char *id)
{
if (id && *id) {
OBSBasicSourceSelect sourceSelect(this, id);
sourceSelect.exec();
if (sourceSelect.newSource && strcmp(id, "group") != 0)
CreatePropertiesWindow(sourceSelect.newSource);
}
}
会初始化OBSBasicSourceSelect。
你可以为新添加的源设置名字,当点击确定时,会调用AddNew,创建一个新的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 {
const char *v_id = obs_get_latest_input_type_id(id);
source = obs_source_create(v_id, name, NULL, nullptr);
if (source) {
AddSourceData data;
data.source = source;
data.visible = visible;
obs_enter_graphics();
obs_scene_atomic_update(scene, AddSource, &data);
obs_leave_graphics();
newSource = source;
/* set monitoring if source monitors by default */
uint32_t flags = obs_source_get_output_flags(source);
if ((flags & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0) {
obs_source_set_monitoring_type(
source,
OBS_MONITORING_TYPE_MONITOR_ONLY);
}
success = true;
}
}
obs_source_release(source);
return success;
}
然后去设置源的属性:
void OBSBasic::CreatePropertiesWindow(obs_source_t *source)
{
if (properties)
properties->close();
properties = new OBSBasicProperties(this, source);
properties->Init();
properties->setAttribute(Qt::WA_DeleteOnClose, true);
}
OBSBasicProperties这是源的属性类,这个类要好好研究,这里面涉及到每个源的属性界面,界面是由OBSPropertiesView这个类构造的。在OBSBasicProperties的构造函数里有这么一段代码:
view = new OBSPropertiesView(
settings, source,
(PropertiesReloadCallback)obs_source_properties,
(PropertiesUpdateCallback)obs_source_update);
然后设置每个源的属性:
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);
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);
}
emit PropertiesRefreshed();
}
void OBSPropertiesView::AddProperty(obs_property_t *property,
QFormLayout *layout)
{
const char *name = obs_property_name(property);
obs_property_type type = obs_property_get_type(property);
if (!obs_property_visible(property))
return;
QLabel *label = nullptr;
QWidget *widget = nullptr;
bool warning = false;
switch (type) {
case OBS_PROPERTY_INVALID:
return;
case OBS_PROPERTY_BOOL:
widget = AddCheckbox(property);
break;
case OBS_PROPERTY_INT:
AddInt(property, layout, &label);
break;
case OBS_PROPERTY_FLOAT:
AddFloat(property, layout, &label);
break;
case OBS_PROPERTY_TEXT:
widget = AddText(property, layout, label);
break;
case OBS_PROPERTY_PATH:
AddPath(property, layout, &label);
break;
case OBS_PROPERTY_LIST:
widget = AddList(property, warning);
break;
case OBS_PROPERTY_COLOR:
AddColor(property, layout, label);
break;
case OBS_PROPERTY_FONT:
AddFont(property, layout, label);
break;
case OBS_PROPERTY_BUTTON:
widget = AddButton(property);
break;
case OBS_PROPERTY_EDITABLE_LIST:
AddEditableList(property, layout, label);
break;
case OBS_PROPERTY_FRAME_RATE:
AddFrameRate(property, warning, layout, label);
break;
case OBS_PROPERTY_GROUP:
AddGroup(property, layout);
}
if (widget && !obs_property_enabled(property))
widget->setEnabled(false);
if (!label && type != OBS_PROPERTY_BOOL &&
type != OBS_PROPERTY_BUTTON && type != OBS_PROPERTY_GROUP)
label = new QLabel(QT_UTF8(obs_property_description(property)));
if (warning && label) //TODO: select color based on background color
label->setStyleSheet("QLabel { color: red; }");
if (label && minSize) {
label->setMinimumWidth(minSize);
label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
}
if (label && !obs_property_enabled(property))
label->setEnabled(false);
if (!widget)
return;
if (obs_property_long_description(property)) {
QString lStr = "<html>%1 <img src='%2' style=' \
vertical-align: bottom; \
' /></html>";
bool lightTheme = palette().text().color().redF() < 0.5;
QString file = lightTheme ? ":/res/images/help.svg"
: ":/res/images/help_light.svg";
if (label) {
label->setText(lStr.arg(label->text(), file));
label->setToolTip(
obs_property_long_description(property));
} else if (type == OBS_PROPERTY_BOOL) {
QWidget *newWidget = new QWidget();
QHBoxLayout *boxLayout = new QHBoxLayout(newWidget);
boxLayout->setContentsMargins(0, 0, 0, 0);
boxLayout->setAlignment(Qt::AlignLeft);
QCheckBox *check = qobject_cast<QCheckBox *>(widget);
QLabel *help =
new QLabel(lStr.arg(check->text(), file));
help->setToolTip(
obs_property_long_description(property));
check->setText("");
boxLayout->addWidget(check);
boxLayout->addWidget(help);
widget = newWidget;
}
}
layout->addRow(label, widget);
if (!lastFocused.empty())
if (lastFocused.compare(name) == 0)
lastWidget = widget;
}
最后弹出窗口