The Property Browser Framework

Материал из Wiki.crossplatform.ru

(Различия между версиями)
Перейти к: навигация, поиск
 
(10 промежуточных версий не показаны.)
Строка 14: Строка 14:
Фреймворк Property Browser включает в себя виджеты браузера, менеджеры свойств, фабрики свойств и сами свойства.  
Фреймворк Property Browser включает в себя виджеты браузера, менеджеры свойств, фабрики свойств и сами свойства.  
-
Виджеты браузера - это пользовательские интерфейсы, позволяющие пользователю редактировать заданный набор иерархически структурированных свойств:<tt>QtTreePropertyBrowser</tt> presents the properties using a treestructure and <tt>QtGroupBoxPropertyBrowser</tt> displays the propertieswithin a group box.
+
Виджеты браузера - это пользовательские интерфейсы, позволяющие пользователю редактировать заданный набор иерархически структурированных свойств: '''''QtTreePropertyBrowser''''' представляет свойства с использованием древовидных структур и '''''QtGroupBoxPropertyBrowser''''' отображает свойства в group box.
[[Image:qq18-propertybrowser-widgets.png|center]]
[[Image:qq18-propertybrowser-widgets.png|center]]
-
In addition, it is possible to extend the framework by writing customproperty browser widgets derived from the<tt>QtAbstractPropertyBrowser</tt> class.
+
Кроме того, можно расширить фреймворк, написав собственные виджеты браузера, производные от класса '''''QtAbstractPropertyBrowser'''''.
-
Custom editor widgets for propertiesare created by factories derived from the [[Qt:Документация_4.3.2/qabstracteditorfactory  | QAbstractEditorFactory]] class.
+
Настраиваемые виджеты редактора свойств, создаваемые фабриками, производными от класса [[Qt:Документация_4.3.2/qabstracteditorfactory  | QAbstractEditorFactory]].
-
Properties only need to be encapsulated in <tt>QtProperty</tt> instances beforethey can be used in browser widgets. These instances are created andmanaged by property managers derived from the <tt>QtAbstractPropertyManager</tt>class.
+
Свойства должны быть инкапсулированы только в экземплярах '''<tt>QtProperty</tt>''', прежде чем они могут быть использованы в виджетах браузера. Эти экземпляры создаются и управляются менеджерами свойств, производными от класса '''''QtAbstractPropertyManager'''''.
<div id="populatingthepropertybrowser"></div>
<div id="populatingthepropertybrowser"></div>
-
== Populating the Property Browser ==
+
== Заполнение браузера свойств (Populating the Property Browser) ==
-
When using a property browser widget, managers must be created foreach of the required property types if we want the user to be able to editthe properties.
+
При использовании виджета браузера свойств, менеджеры должны быть созданы в соответствии с требуемыми типами свойств, если мы хотим, чтобы пользователь имел возможность редактировать свойства.
-
The framework provides managers for the most commontypes. For example, to create an integer property we first instantiatethe <tt>QtIntPropertyManager</tt> class, then we use its <tt>addProperty()</tt>function to create the property:
+
Этот фреймворк предоставляет менеджерам наиболее часто встречающиеся типы. Например, для создания целочисленного свойства мы сначала создаём экземпляр класса '''''QtIntPropertyManager''''', а затем используем его функцию '''''addProperty()''''' для создания свойства:
<source lang="cpp-qt">
<source lang="cpp-qt">
QtIntPropertyManager *intManager;
QtIntPropertyManager *intManager;
Строка 41: Строка 41:
intManager->setValue(priority, 3);
intManager->setValue(priority, 3);
</source>  
</source>  
-
The <tt>QtProperty</tt> class provides functions for setting a property's name,tooltip, status tip and "What's This?" text. To set and alter theproperty's value and range, each manager has its own type specificAPI. For example, here's how an enum property is set up and given adefault value:
+
Класс '''''QtProperty''''' предоставляет функции для установки имени свойства, всплывающей подсказки, подсказки состояния и сообщения "What's This?". Для установки и изменения значения свойства и диапазона, каждый менеджер имеет свой API, специфичный для определённого типа. Например, вот как устанавливается свойство enum и ему присваивается значение по умолчанию:
<source lang="cpp-qt">
<source lang="cpp-qt">
QtEnumPropertyManager *enumManager;
QtEnumPropertyManager *enumManager;
Строка 51: Строка 51:
enumManager->setValue(reportType, 1); // "Suggestion"
enumManager->setValue(reportType, 1); // "Suggestion"
</source>  
</source>  
-
Properties can be categorized into groups using the <tt>QtGroupPropertyManager</tt>class. For example:
+
Свойства можно разбить на группы, используя класс '''''QtGroupPropertyManager'''''. Например:
<source lang="cpp-qt">
<source lang="cpp-qt">
QtGroupPropertyManager *groupManager;
QtGroupPropertyManager *groupManager;
Строка 62: Строка 62:
task1->addSubProperty(reportType);
task1->addSubProperty(reportType);
</source>  
</source>  
-
In addition, each property can have zero or more subproperties. These are justnormal properties that are added to a property using its <tt>addSubProperty()</tt>function.
+
Кроме того, каждое свойство может иметь ноль и более под-свойств. Это обычные свойства, которые добавляются к свойству с помощью функции '''''addSubProperty()'''''.
-
The properties created by a group property manager don't have anyvalue, they are just provided as grouping elements in the propertyhierarchies and can be added to the browser in the same way as anyother property.
+
Свойства, созданные менеджером свойств группы (GroupPropertyManager), не имеют никакого значения, они просто предоставляются в качестве элементов группировки в иерархиях свойств и могут быть добавлены в браузер так же, как и любое другое свойство.
-
Once we have the properties, we associate each property manager with afactory for the preferred editor for that type. As with managers, the framework provides factoriesfor common widgets like [[Qt:Документация_4.3.2/qspinbox  | QSpinBox]] and [[Qt:Документация_4.3.2/qcombobox  | QComboBox]].
+
Получив свойства, мы связываем каждого менеджера свойств фабрикой для предпочтительного редактора данного типа. Как и в случае с менеджерами, фреймворк предоставляет фабрики для таких распространенных виджетов, как [[Qt:Документация_4.3.2/qspinbox  | QSpinBox]] and [[Qt:Документация_4.3.2/qcombobox  | QComboBox]].
<source lang="cpp-qt">
<source lang="cpp-qt">
QtSpinBoxFactory *spinBoxFactory;
QtSpinBoxFactory *spinBoxFactory;
Строка 79: Строка 79:
browser->setFactoryForManager(enumManager, enumFactory);
browser->setFactoryForManager(enumManager, enumFactory);
</source>  
</source>  
-
By relating a factory to a manager, we ensure that whenever a propertycreated by that manager appears in the browser, the specified factorywill be used to create an editor for it.
+
Привязывая фабрику к менеджеру, мы гарантируем, что всякий раз, когда в браузере появляется свойство, созданное этим менеджером, указанная фабрика будет использована для создания редактора для него.
<source lang="cpp-qt">
<source lang="cpp-qt">
browser->addProperty(task1);
browser->addProperty(task1);
browser->show();
browser->show();
</source>  
</source>  
-
Finally, it only remains to add our properties to the browserand ensure that the browser widget is shown.
+
Наконец, остается только добавить наши свойства в браузер и убедиться, что виджет браузера отображается.
[[Image:qq18-propertybrowser-example.png|center]]
[[Image:qq18-propertybrowser-example.png|center]]
-
Note that to provide different editor widgets for the same propertytype, we only need to call <tt>setFactoryForManager()</tt> with another instanceof the type-specific factory.
+
Обратите внимание, что для предоставления различных виджетов редактирования для одного и того же типа свойств, нам нужно только вызвать '''''setFactoryForManager()''''' с другой фабрикой, специфичной для данного типа.
-
Once the browser widget is populated, the user can interact with itand edit the properties. We can monitor the values of properties byconnecting to each property manager's signals, such as<tt>QtIntPropertyManager::valueChanged()</tt> and<tt>QtEnumPropertyManager::enumChanged()</tt>.
+
После заполнения виджета браузера пользователь может взаимодействовать с ним и редактировать его свойства. Мы можем отслеживать значения свойств, подключаясь к сигналам каждого менеджера свойств, таких как '''''QtIntPropertyManager::valueChanged()''''' и '''''QtEnumPropertyManager::enumChanged()'''''.
<div id="thevariantapproach"></div>
<div id="thevariantapproach"></div>
-
== The Variant Approach ==
+
== Вариантный подход (The Variant Approach) ==
-
In the previous section we populated the property browser usingspecialized classes for each property type. But the framework alsoprovides a convenient second approach that uses [[Qt:Документация_4.3.2/qvariant  | QVariant]] to holdall the properties' values and attributes.
+
В предыдущем разделе мы заполнили браузер свойств, используя специализированные классы для каждого типа свойств. Но фреймворк также предоставляет также и второй удобный подход, использующий [[Qt:Документация_4.3.2/qvariant  | QVariant]] для хранения всех значений и атрибутов свойств.
-
In this approach, the developer only needs one property manager classand one editor factory class. These are selected from the followingthree classes:
+
При таком подходе разработчику нужен только один класс менеджера свойств и один класс фабричного редактора. Они выбираются из следующих трех классов:
-
* <tt>QtVariantProperty</tt> inherits <tt>QtProperty</tt> but has anenriched API, allowing properties values and attributes to be directlyqueried and altered.  
+
* '''''QtVariantProperty''''' наследует '''''QtProperty''''', используя расширенный API, позволяя напрямую запрашивать и изменять значения свойств и атрибутов.
-
* <tt>QtVariantPropertyManager</tt> can be used for all propertytypes, and its additional API can be used to query for supported variant typesand their attribute lists.  
+
* '''''QtVariantPropertyManager''''' будет использоваться для всех типов свойств, а его вспомогательный API позволит запрашивать поддерживаемые варианты типов и их перечни атрибутов.  
-
* <tt>QtVariantEditorFactory</tt> is capable of creating variouseditor widgets for the types supported by <tt>QtVariantPropertyManager</tt>.  
+
* '''''QtVariantEditorFactory''''' предоставляет возможность создавать различные виджеты редактора для типов, поддерживаемых '''''QtVariantPropertyManager'''''.
-
Using this approach, the code from the previous section to manage aninteger property now looks like this:
+
При таком подходе код из предыдущего раздела для управления целочисленным свойством теперь выглядит следующим образом:
<source lang="cpp-qt">
<source lang="cpp-qt">
QtVariantPropertyManager *variantManager;
QtVariantPropertyManager *variantManager;
Строка 110: Строка 110:
priority->setValue(3);
priority->setValue(3);
</source>  
</source>  
-
Note the additional argument in the <tt>QtVariantManager::addProperty()</tt>function. It is the type of property we want to create. This can beany of values defined in the [[Qt:Документация_4.3.2/qvariant  | QVariant]]::Type enum plus additional uservalues obtained using the <tt>qMetaTypeId()</tt> function.  
+
Обратите внимание на дополнительный аргумент в функции '''''QtVariantManager::addProperty()'''''. Это тип свойства, которое мы хотим создать. Это может быть любое из значений, определенных в перечислении [[Qt:Документация_4.3.2/qvariant  | QVariant]]::Type enum плюс дополнительные пользовательские значения, полученные с помощью функции '''''qMetaTypeId()'''''.  
-
Subsequently, instead of calling <tt>setRange()</tt> on<tt>QtIntPropertyManager</tt>, we use the <tt>setAttribute()</tt> function of<tt>QtVariantProperty</tt> class twice, passing minimum and maximum valuesfor the property. Finally, we call the <tt>setValue()</tt> function directlyon the <tt>QtVariantProperty</tt> instance.  
+
Впоследствии, вместо вызова '''''setRange()''''' на '''''QtIntPropertyManager''''', мы используем функцию '''''setAttribute()''''' класса '''''QtVariantProperty''''' дважды, передавая минимальное и максимальное значения для свойства. Наконец, мы вызываем функцию '''''setValue()''''' непосредственно на экземпляре '''''QtVariantProperty'''''.
-
The ID of an enum property is not built into [[Qt:Документация_4.3.2/qvariant  | QVariant]], and must beobtained using the static <tt>QtVariantPropertyManager::enumTypeId()</tt>function.
+
Идентификатор свойства enum не встроен в [[Qt:Документация_4.3.2/qvariant  | QVariant]], и должен быть получен с помощью статической функции '''''QtVariantPropertyManager::enumTypeId()'''''.
<source lang="cpp-qt">
<source lang="cpp-qt">
QtVariantProperty *reportType = variantManager->addProperty(
QtVariantProperty *reportType = variantManager->addProperty(
Строка 123: Строка 123:
reportType->setValue(1); // "Suggestion"
reportType->setValue(1); // "Suggestion"
</source>  
</source>  
-
Then we can call <tt>setAttribute()</tt> for the property,passing <tt>"enumNames"</tt> as the attribute name, to set the possible enumvalues that the property can have.
+
После чего для свойства можно вызвать функцию '''''setAttribute()''''', передав в качестве имени атрибута '''''"enumNames"''''', чтобы установить возможные значения enum, которые может иметь это свойство.
-
Group properties are created in the same way as enum properties, but withan ID retrieved using the <tt>QtVariantPropertyManager::groupTypeId()</tt>function.
+
Групповые свойства создаются таким же образом, как и свойства enum, но с идентификатором, полученным с помощью функции '''''QtVariantPropertyManager::groupTypeId()'''''.
-
In the end we create an instance of the <tt>QtVariantEditorFactory</tt> class,and use it with our variant manager in our chosen property browser:
+
В конце мы создаем экземпляр класса '''''QtVariantEditorFactory''''' и используем его с нашим менеджером вариантов в выбранном нами браузере свойств:
<source lang="cpp-qt">  
<source lang="cpp-qt">  
QtTreePropertyBrowser *browser;
QtTreePropertyBrowser *browser;
Строка 136: Строка 136:
<div id="extendingtheframework"></div>
<div id="extendingtheframework"></div>
-
== Extending the Framework ==
+
== Расширение фреймворка (Extending the Framework) ==
-
So far we have used the existing properties, managers and factories providedby the Property Browser framework. But the framework also allows thedeveloper to create and support custom property types.
+
До сих пор мы использовали существующие свойства, менеджеры и фабрики, предоставленные в рамках Property Browser. Но фреймворк также позволяет разработчику создавать и поддерживать пользовательские типы свойств.
-
Let's say we want to extend our previous example, and support a customfile path property. The value of our custom property can be a[[Qt:Документация_4.3.2/qstring  | QString]], and the property can in addition possess afiltering attribute. We can also create a custom editor for our type,<tt>FileEdit</tt>, a simple composition of [[Qt:Документация_4.3.2/qlineedit  | QLineEdit]] and[[Qt:Документация_4.3.2/qtoolbutton  | QToolButton]]. We want to let the line edit show the current file path,and show a file browser dialog when the tool button is pressed (allowing theuser to choose a file from the files that match our filtering attribute).
+
Допустим, мы хотим расширить наш предыдущий пример и добавить поддержку свойств пользовательского пути к файлу. Значением нашего пользовательского свойства может быть [[Qt:Документация_4.3.2/qstring  | QString]], и в добавок это свойство может обладать атрибутом фильтрации. Мы также можем создать пользовательский редактор для нашего типа, '''''FileEdit''''', несложную комбинацию [[Qt:Документация_4.3.2/qlineedit  | QLineEdit]] и[[Qt:Документация_4.3.2/qtoolbutton  | QToolButton]]. Мы хотим, чтобы lineEdit показывал текущий путь к файлу и показывал окно браузера файлов при нажатии кнопки toolButton (позволяя пользователю выбрать файл из файлов, соответствующих нашему атрибуту фильтрации).
-
The editor class provides getter and setter functions for its fields, aswell as a signal that can be emitted whenever the file path changes.
+
Класс редактора предоставляет функции геттера и сеттера для своих полей, а также сигнал, который может быть выдан всякий раз, когда меняется путь к файлу.
[[Image:qq18-propertybrowser-example-extension.png|center]]
[[Image:qq18-propertybrowser-example-extension.png|center]]
-
We have described two approaches for populating a browser widget:One uses type specific property managers and editor factories; the otheruses the framework's variant base classes. Both approaches support customtypes.
+
Мы описали два подхода к заполнению виджета браузера: В одном используются менеджеры свойств и фабрики редакторов, в другом - базовые классы вариантов фреймворка. Оба подхода поддерживают пользовательские типы.
-
If we choose the type specific approach, we must provide a managerthat is able to produce properties of our custom type, and that canstore the state of the properties it creates; i.e., their values andattributes. Such a manager looks like this:
+
Если мы выбираем подход, ориентированный на конкретный тип, мы должны предоставить такой менеджер, который способен создавать свойства нашего пользовательского типа, и который может хранить состояние создаваемых им свойств, т.е. их значения и атрибуты. Такой менеджер выглядит следующим образом:
<source lang="cpp-qt">
<source lang="cpp-qt">
class FilePathManager : public QtAbstractPropertyManager
class FilePathManager : public QtAbstractPropertyManager
Строка 175: Строка 175:
};
};
</source>  
</source>  
-
Our custom <tt>FilePathManager</tt> is derived from the<tt>QtAbstractPropertyManager</tt>, enriching the interface with its attributes,and providing the required getter and setter functions for our custom property.The <tt>valueChanged()</tt> and<tt>filterChanged()</tt> signals are used to communicate with custom editorfactories (shown later), and can also be generally used to monitor properties.We define a simple data structure to store the state ofthe value and filter for each file path property, and record each file pathproperty in a private map.
+
Наш пользовательский '''''FilePathManager''''' является производным от '''''QtAbstractPropertyManager''''', расширяя интерфейс своими атрибутами и предоставляя в наше пользовательское свойство необходимые функции геттера и сеттера. Сигналы '''''valueChanged()''''' и '''''filterChanged()''''' используются для связи с фабриками пользовательских редакторов ( будут показаны позже), а также могут быть в общем использованы для мониторинга свойств. Мы определяем простую структуру данных для хранения текущего значения и фильтра для каждого свойства пути к файлу, а также записываем каждое свойство пути в private map.
-
The <tt>FilePathManager</tt> must also reimplement several protectedfunctions provided by its base class that are needed by theframework: <tt>valueText()</tt> returns a string representation of the property'svalue, <tt>initializeProperty()</tt> initializes a new property, and<tt>uninitializeProperty()</tt> is used to clean up when a property is deleted.
+
'''''FilePathManager''''' также должен переопределить несколько protected функций, предоставляемых его базовым классом, которые необходимы фреймворку: функция '''''valueText()''''' возвращает строковое представление значения свойства, функция '''''initializeProperty()''''' инициализирует новое свойство, а функция '''''uninitializeProperty()''''' используется для очистки при удалении свойства.
-
We return empty data from getter functions if an unknown property issupplied; otherwise we return the data from the property map:
+
Мы возвращаем пустые данные из функций геттера, когда указано неизвестное свойство, в противном случае мы возвращаем данные из property map:
<source lang="cpp-qt">
<source lang="cpp-qt">
QString FilePathManager::value(const QtProperty *property)
QString FilePathManager::value(const QtProperty *property)
Строка 188: Строка 188:
}
}
</source>  
</source>  
-
In the setter functions, we ignore attempts to set the values of unknownproperties, and only commit new data to the property map. We emit thebase class's <tt>propertyChanged()</tt> signal to notify other components of anychanges. However, note that the <tt>setFilter()</tt> does not need to do thisbecause it does not change the property's value.
+
В функциях сеттера мы игнорируем попытки установить значения неизвестных свойств и фиксируем только новые данные в property map. Мы посылаем сигнал '''''propertyChanged()''''' базового класса, чтобы уведомить другие компоненты о любых изменениях. Однако, обратите внимание, что функция '''''setFilter()''''' не обязана этого делать, так как не изменяет значение свойства.
<source lang="cpp-qt">
<source lang="cpp-qt">
void FilePathManager::setValue(QtProperty *property, const QString &amp;val)
void FilePathManager::setValue(QtProperty *property, const QString &amp;val)
Строка 201: Строка 201:
}
}
</source>  
</source>  
-
Before we can add instances of our custom property to the browserwidget, we must also provide a factory which is able to produce<tt>FileEdit</tt> editors for our property manager:
+
Прежде чем мы сможем добавить экземпляры нашего пользовательского свойства в виджет браузера, мы также должны предоставить фабрику, которая сможет создавать редакторы '''''FileEdit''''' для нашего менеджера свойств:
<source lang="cpp-qt">
<source lang="cpp-qt">
class FileEditFactory
class FileEditFactory
Строка 219: Строка 219:
};
};
</source>  
</source>  
-
The <tt>FileEditFactory</tt> is derived from the <tt>QtAbstractEditorFactory</tt>template class, using our <tt>FilePathManager</tt> class as the templateargument. This means that our factory is only able to create editorsfor our custom manager while allowing it to access the manager's enrichedinterface.
+
'''''FileEditFactory''''' является производным от шаблонного класса '''''QtAbstractEditorFactory''''', используя наш класс '''''FilePathManager''''' в качестве аргумента шаблона. Это означает, что наша фабрика может создавать редакторы только для нашего пользовательского менеджера, в то же время позволяя ему получить доступ к расширенному интерфейсу менеджера.
-
As with the manager, the custom factory must reimplement some protectedfunctions. For example, the <tt>connectPropertyManager()</tt> function iscalled when the factory is set up for use with a <tt>FilePathManager</tt>,and connects the manager's <tt>valueChanged()</tt> and <tt>filterChanged()</tt>signals to the corresponding <tt>slotPropertyChanged()</tt> and<tt>slotFilterChanged()</tt> slots. These connections keep the state of theeditors up to date. Similarly, <tt>disconnectPropertyManager()</tt> is used toremove these connections.
+
Как и в случае с менеджером, пользовательская фабрика должна переопределить некоторые закрытые функции. Например, функция '''''connectPropertyManager()''''' вызывается, когда фабрика настраивается на использование с '''''FilePathManager''''', и соединяет сигналы '''''valueChanged()''''' и '''''filterChanged()''''' менеджера с соответствующими слотами '''''slotPropertyChanged()''''' и '''''slotFilterChanged()'''''. Эти связи позволяют поддерживать состояние редакторов в актуальном состоянии. Аналогично, для удаления этих соединений используется функция '''''disconnectPropertyManager()'''''.
-
The <tt>createEditor()</tt> function creates and initializes a <tt>FileEdit</tt>widget, adds the editor to the internal maps:
+
Функция '''''createEditor()''''' создает и инициализирует виджет '''''FileEdit''''', добавляет редактор во внутренние map-ы:
<source lang="cpp-qt">
<source lang="cpp-qt">
FileEdit *editor = new FileEdit(parent);
FileEdit *editor = new FileEdit(parent);
Строка 237: Строка 237:
return editor;
return editor;
</source>  
</source>  
-
Before returning the new editor widget, we connect its <tt>filePathChanged()</tt>signal to the factory's <tt>slotSetValue()</tt> slot. We also connect its<tt>destroyed()</tt> signal to the factory's <tt>slotEditorDestroyed()</tt> slot toensure that we are notified if it is destroyed.
+
Прежде чем вернуть новый виджет редактора, мы подключаем его сигнал '''''filePathChanged()''''' к фабричному слоту '''''slotSetValue()'''''. Мы также подключаем его сигнал '''''destroyed()''''' к фабричному слоту '''''slotEditorDestroyed()''''', чтобы быть уверенными в том, что мы получим оповещение, когда он будет уничтожен.
-
Internally, the task of each factory is to synchronize the state of theeditors with the state of the properties. Whenever an editor changesits value we need to tell the framework about it, and vice versa.In our editor factory's <tt>editorToProperty</tt> map, we relate every editorcreated by the factory to the property it was created to edit.
+
Задача каждой фабрики - синхронизировать состояние редакторов с состоянием свойств. Всякий раз, когда редактор изменяет своё значение, нам необходимо сообщить об этом фреймворку, и наоборот. Каждый редактор, созданный фабрикой, в нашей мапе '''''editorToProperty''''', мы связываем со свойством, для редактирования которого он был создан.
-
The opposite relation is kept in the <tt>createdEditors</tt> map, whereproperties are related to editors. Since it is possible that two or moreproperty browsers are displaying the same instance of a property, we mayneed to create many editors for the same property instance, so we keep alist of editors for each property.
+
Противоположная связь сохраняется в map '''''createEditors''''', где свойства связаны с редакторами. Поскольку возможно, что два или более браузеров свойств отображают один и тот же экземпляр свойства, нам может потребоваться создать множество редакторов для одного и того же экземпляра свойства, поэтому мы ведем список редакторов для каждого свойства.
-
Combined with the private slots, these data structures allow us toinform the framework that an editor value has been changed, and let usupdate all relevant editors whenever the framework changes any of theproperties.
+
В сочетании с приватными слотами эти структуры данных позволяют нам информировать фреймворк о том, что значение редактора было изменено, и позволяют нам обновлять все соответствующие редакторы всякий раз, когда фреймворк изменяет какое-либо из свойств.
-
When the factory is destroyed, the state of the properties is no longersynchronized, and the data shown by the editors is no longer valid. For thatreason, all the editors created by the factory are deleted in the factory'sdestructor. Therefore, when an editor is destroyed, we must ensure that itis removed from the internal maps.
+
Когда фабрика уничтожается, состояние свойств больше не синхронизируется, и данные, отображаемые редакторами, перестают быть действительными. По этой причине все редакторы, созданные фабрикой, удаляются в деструкторе фабрики. Поэтому при уничтожении редактора необходимо убедиться, что он удален из внутренних maps.
-
The process of adding a custom property is equivalent to adding any otherproperty:
+
Процесс добавления пользовательского свойства эквивалентен добавлению любого другого свойства:
<source lang="cpp-qt">
<source lang="cpp-qt">
FilePathManager *filePathManager;
FilePathManager *filePathManager;
Строка 266: Строка 266:
<div id="extendingwithvariants"></div>
<div id="extendingwithvariants"></div>
-
== Extending with Variants ==
+
== Расширение с помощью Вариантов (Extending with Variants) ==
-
The variant-based approach can also be used for custom types. Using thisapproach, we derive our manager from <tt>QtVariantPropertyManager</tt> and ourfactory from <tt>QtVariantPropertyFactory</tt>.
+
Вариантный подход также может быть использован на пользовательских типах. Используя этот подход, мы получаем менеджер из '''''QtVariantPropertyManager''''', а нашу фабрику - из '''''QtVariantPropertyFactory'''''.
In the previous example, the <tt>FilePathManager</tt> class handled the datainternally for our custom property type. In this case, we define theproperty type as a path value with an associated filter attribute. Both thepath and the filter are stored as strings, and we define a new property typefor the file path as a whole:
In the previous example, the <tt>FilePathManager</tt> class handled the datainternally for our custom property type. In this case, we define theproperty type as a path value with an associated filter attribute. Both thepath and the filter are stored as strings, and we define a new property typefor the file path as a whole:
Строка 347: Строка 347:
| align="right" | [http://www.trolltech.com/trademarks.html Trademarks]
| align="right" | [http://www.trolltech.com/trademarks.html Trademarks]
|}
|}
 +
 +
== Текст заголовка ==

Текущая версия на 10:25, 28 сентября 2020

Image:qt-logo_new.png Image:qq-title-article.png
Qt Quarterly | Выпуск 18 | Документация

by Jarek Kobus
Последний выпуск Qt Solutions включал решение Property Browser, конструкцию, предоставляющую набор графических редакторов для свойств Qt, похожих на те, что используются в Qt Designer.

Содержание

В этой статье мы подробно рассмотрим динамику классов Property Browser, представим два способа их использования в ваших приложениях, а также посмотрим, как фреймворк можно расширять с помощью типов и редакторов.

[source code]

[править] The Big Picture

Фреймворк Property Browser включает в себя виджеты браузера, менеджеры свойств, фабрики свойств и сами свойства.

Виджеты браузера - это пользовательские интерфейсы, позволяющие пользователю редактировать заданный набор иерархически структурированных свойств: QtTreePropertyBrowser представляет свойства с использованием древовидных структур и QtGroupBoxPropertyBrowser отображает свойства в group box.

center

Кроме того, можно расширить фреймворк, написав собственные виджеты браузера, производные от класса QtAbstractPropertyBrowser.

Настраиваемые виджеты редактора свойств, создаваемые фабриками, производными от класса QAbstractEditorFactory.

Свойства должны быть инкапсулированы только в экземплярах QtProperty, прежде чем они могут быть использованы в виджетах браузера. Эти экземпляры создаются и управляются менеджерами свойств, производными от класса QtAbstractPropertyManager.

[править] Заполнение браузера свойств (Populating the Property Browser)

При использовании виджета браузера свойств, менеджеры должны быть созданы в соответствии с требуемыми типами свойств, если мы хотим, чтобы пользователь имел возможность редактировать свойства.

Этот фреймворк предоставляет менеджерам наиболее часто встречающиеся типы. Например, для создания целочисленного свойства мы сначала создаём экземпляр класса QtIntPropertyManager, а затем используем его функцию addProperty() для создания свойства:

QtIntPropertyManager *intManager;
QtProperty *priority:
 
intManager = new QtIntPropertyManager;
priority = intManager->addProperty("Priority");
 
priority->setToolTip("Task Priority");
intManager->setRange(priority, 1, 5);
intManager->setValue(priority, 3);

Класс QtProperty предоставляет функции для установки имени свойства, всплывающей подсказки, подсказки состояния и сообщения "What's This?". Для установки и изменения значения свойства и диапазона, каждый менеджер имеет свой API, специфичный для определённого типа. Например, вот как устанавливается свойство enum и ему присваивается значение по умолчанию:

QtEnumPropertyManager *enumManager;
QtProperty *reportType;
QStringList types;   
...
types << "Bug" << "Suggestion" << "To Do";
enumManager->setEnumNames(reportType, types);
enumManager->setValue(reportType, 1); // "Suggestion"

Свойства можно разбить на группы, используя класс QtGroupPropertyManager. Например:

QtGroupPropertyManager *groupManager;
QtProperty *task1;
 
groupManager = new QtGroupPropertyManager;
task1 = groupManager->addProperty("Task 1");
 
task1->addSubProperty(priority);
task1->addSubProperty(reportType);

Кроме того, каждое свойство может иметь ноль и более под-свойств. Это обычные свойства, которые добавляются к свойству с помощью функции addSubProperty().

Свойства, созданные менеджером свойств группы (GroupPropertyManager), не имеют никакого значения, они просто предоставляются в качестве элементов группировки в иерархиях свойств и могут быть добавлены в браузер так же, как и любое другое свойство.

Получив свойства, мы связываем каждого менеджера свойств фабрикой для предпочтительного редактора данного типа. Как и в случае с менеджерами, фреймворк предоставляет фабрики для таких распространенных виджетов, как QSpinBox and QComboBox.

QtSpinBoxFactory *spinBoxFactory;
QtEnumEditorFactory *enumFactory;
 
spinBoxFactory = new QtSpinBoxFactory;
enumFactory = new QtEnumEditorFactory;
 
QtTreePropertyBrowser *browser;
browser = new QtTreePropertyBrowser;
browser->setFactoryForManager(intManager, spinBoxFactory);
browser->setFactoryForManager(enumManager, enumFactory);

Привязывая фабрику к менеджеру, мы гарантируем, что всякий раз, когда в браузере появляется свойство, созданное этим менеджером, указанная фабрика будет использована для создания редактора для него.

browser->addProperty(task1);
browser->show();

Наконец, остается только добавить наши свойства в браузер и убедиться, что виджет браузера отображается.

center

Обратите внимание, что для предоставления различных виджетов редактирования для одного и того же типа свойств, нам нужно только вызвать setFactoryForManager() с другой фабрикой, специфичной для данного типа.

После заполнения виджета браузера пользователь может взаимодействовать с ним и редактировать его свойства. Мы можем отслеживать значения свойств, подключаясь к сигналам каждого менеджера свойств, таких как QtIntPropertyManager::valueChanged() и QtEnumPropertyManager::enumChanged().

[править] Вариантный подход (The Variant Approach)

В предыдущем разделе мы заполнили браузер свойств, используя специализированные классы для каждого типа свойств. Но фреймворк также предоставляет также и второй удобный подход, использующий QVariant для хранения всех значений и атрибутов свойств.

При таком подходе разработчику нужен только один класс менеджера свойств и один класс фабричного редактора. Они выбираются из следующих трех классов:

  • QtVariantProperty наследует QtProperty, используя расширенный API, позволяя напрямую запрашивать и изменять значения свойств и атрибутов.
  • QtVariantPropertyManager будет использоваться для всех типов свойств, а его вспомогательный API позволит запрашивать поддерживаемые варианты типов и их перечни атрибутов.
  • QtVariantEditorFactory предоставляет возможность создавать различные виджеты редактора для типов, поддерживаемых QtVariantPropertyManager.

При таком подходе код из предыдущего раздела для управления целочисленным свойством теперь выглядит следующим образом:

QtVariantPropertyManager *variantManager;
variantManager = new QtVariantPropertyManager;
 
QtVariantProperty *priority = variantManager->addProperty(QVariant::Int, "Priority");
priority->setAttribute("minimum", 1);
priority->setAttribute("maximum", 5);
priority->setValue(3);

Обратите внимание на дополнительный аргумент в функции QtVariantManager::addProperty(). Это тип свойства, которое мы хотим создать. Это может быть любое из значений, определенных в перечислении QVariant::Type enum плюс дополнительные пользовательские значения, полученные с помощью функции qMetaTypeId().

Впоследствии, вместо вызова setRange() на QtIntPropertyManager, мы используем функцию setAttribute() класса QtVariantProperty дважды, передавая минимальное и максимальное значения для свойства. Наконец, мы вызываем функцию setValue() непосредственно на экземпляре QtVariantProperty.

Идентификатор свойства enum не встроен в QVariant, и должен быть получен с помощью статической функции QtVariantPropertyManager::enumTypeId().

QtVariantProperty *reportType = variantManager->addProperty(
    QtVariantPropertyManager::enumTypeId(), "Report Type");
QStringList types;
types << "Bug" << "Suggestion" << "To Do";
reportType->setAttribute("enumNames", types);
reportType->setValue(1); // "Suggestion"

После чего для свойства можно вызвать функцию setAttribute(), передав в качестве имени атрибута "enumNames", чтобы установить возможные значения enum, которые может иметь это свойство.

Групповые свойства создаются таким же образом, как и свойства enum, но с идентификатором, полученным с помощью функции QtVariantPropertyManager::groupTypeId().

В конце мы создаем экземпляр класса QtVariantEditorFactory и используем его с нашим менеджером вариантов в выбранном нами браузере свойств:

 
QtTreePropertyBrowser *browser;
QtVariantEditorFactory *factory;
...
browser->setFactoryForManager(variantManager, factory);

[править] Расширение фреймворка (Extending the Framework)

До сих пор мы использовали существующие свойства, менеджеры и фабрики, предоставленные в рамках Property Browser. Но фреймворк также позволяет разработчику создавать и поддерживать пользовательские типы свойств.

Допустим, мы хотим расширить наш предыдущий пример и добавить поддержку свойств пользовательского пути к файлу. Значением нашего пользовательского свойства может быть QString, и в добавок это свойство может обладать атрибутом фильтрации. Мы также можем создать пользовательский редактор для нашего типа, FileEdit, несложную комбинацию QLineEdit и QToolButton. Мы хотим, чтобы lineEdit показывал текущий путь к файлу и показывал окно браузера файлов при нажатии кнопки toolButton (позволяя пользователю выбрать файл из файлов, соответствующих нашему атрибуту фильтрации).

Класс редактора предоставляет функции геттера и сеттера для своих полей, а также сигнал, который может быть выдан всякий раз, когда меняется путь к файлу.

center

Мы описали два подхода к заполнению виджета браузера: В одном используются менеджеры свойств и фабрики редакторов, в другом - базовые классы вариантов фреймворка. Оба подхода поддерживают пользовательские типы.

Если мы выбираем подход, ориентированный на конкретный тип, мы должны предоставить такой менеджер, который способен создавать свойства нашего пользовательского типа, и который может хранить состояние создаваемых им свойств, т.е. их значения и атрибуты. Такой менеджер выглядит следующим образом:

class FilePathManager : public QtAbstractPropertyManager
{
    ...
    QString value(const QtProperty *property) const;
    QString filter(const QtProperty *property) const;
public slots:
    void setValue(QtProperty *, const QString &amp;);
    void setFilter(QtProperty *, const QString &amp;);
signals:
    void valueChanged(QtProperty *, const QString &amp;);
    void filterChanged(QtProperty *, const QString &amp;);
protected:
    QString valueText(const QtProperty *property) const
        { return value(property); }
    void initializeProperty(QtProperty *property)
        { theValues[property] = Data(); }
    void uninitializeProperty(QtProperty *property)
        { theValues.remove(property); }
private:
    struct Data {
        QString value;
        QString filter;
    };
    QMap<const QtProperty *, Data> theValues;
};

Наш пользовательский FilePathManager является производным от QtAbstractPropertyManager, расширяя интерфейс своими атрибутами и предоставляя в наше пользовательское свойство необходимые функции геттера и сеттера. Сигналы valueChanged() и filterChanged() используются для связи с фабриками пользовательских редакторов ( будут показаны позже), а также могут быть в общем использованы для мониторинга свойств. Мы определяем простую структуру данных для хранения текущего значения и фильтра для каждого свойства пути к файлу, а также записываем каждое свойство пути в private map.

FilePathManager также должен переопределить несколько protected функций, предоставляемых его базовым классом, которые необходимы фреймворку: функция valueText() возвращает строковое представление значения свойства, функция initializeProperty() инициализирует новое свойство, а функция uninitializeProperty() используется для очистки при удалении свойства.

Мы возвращаем пустые данные из функций геттера, когда указано неизвестное свойство, в противном случае мы возвращаем данные из property map:

QString FilePathManager::value(const QtProperty *property)
        const
{
    if (!theValues.contains(property)) return "";
    return theValues[property].value;
}

В функциях сеттера мы игнорируем попытки установить значения неизвестных свойств и фиксируем только новые данные в property map. Мы посылаем сигнал propertyChanged() базового класса, чтобы уведомить другие компоненты о любых изменениях. Однако, обратите внимание, что функция setFilter() не обязана этого делать, так как не изменяет значение свойства.

void FilePathManager::setValue(QtProperty *property, const QString &amp;val)
{
    if (!theValues.contains(property)) return;
    Data data = theValues[property];
    if (data.value == val) return;
    data.value = val;
    theValues[property] = data;
    emit propertyChanged(property);
    emit valueChanged(property, data.value);
}

Прежде чем мы сможем добавить экземпляры нашего пользовательского свойства в виджет браузера, мы также должны предоставить фабрику, которая сможет создавать редакторы FileEdit для нашего менеджера свойств:

class FileEditFactory
    : public QtAbstractEditorFactory<FilePathManager>
{
    ...
private slots:
    void slotPropertyChanged(QtProperty *property,
                             const QString &amp;value);
    void slotFilterChanged(QtProperty *property,
                           const QString &amp;filter);
    void slotSetValue(const QString &amp;value);
    void slotEditorDestroyed(QObject *object);
private:
    QMap<QtProperty *, QList<FileEdit *> > createdEditors;
    QMap<FileEdit *, QtProperty *> editorToProperty;
};

FileEditFactory является производным от шаблонного класса QtAbstractEditorFactory, используя наш класс FilePathManager в качестве аргумента шаблона. Это означает, что наша фабрика может создавать редакторы только для нашего пользовательского менеджера, в то же время позволяя ему получить доступ к расширенному интерфейсу менеджера.

Как и в случае с менеджером, пользовательская фабрика должна переопределить некоторые закрытые функции. Например, функция connectPropertyManager() вызывается, когда фабрика настраивается на использование с FilePathManager, и соединяет сигналы valueChanged() и filterChanged() менеджера с соответствующими слотами slotPropertyChanged() и slotFilterChanged(). Эти связи позволяют поддерживать состояние редакторов в актуальном состоянии. Аналогично, для удаления этих соединений используется функция disconnectPropertyManager().

Функция createEditor() создает и инициализирует виджет FileEdit, добавляет редактор во внутренние map-ы:

FileEdit *editor = new FileEdit(parent);
editor->setFilePath(manager->value(property));
editor->setFilter(manager->filter(property));
createdEditors[property].append(editor);
editorToProperty[editor] = property;
 
connect(editor, SIGNAL(filePathChanged(const QString &amp;)),
        this, SLOT(slotSetValue(const QString &amp;)));
connect(editor, SIGNAL(destroyed(QObject *)),
        this, SLOT(slotEditorDestroyed(QObject *)));
return editor;

Прежде чем вернуть новый виджет редактора, мы подключаем его сигнал filePathChanged() к фабричному слоту slotSetValue(). Мы также подключаем его сигнал destroyed() к фабричному слоту slotEditorDestroyed(), чтобы быть уверенными в том, что мы получим оповещение, когда он будет уничтожен.

Задача каждой фабрики - синхронизировать состояние редакторов с состоянием свойств. Всякий раз, когда редактор изменяет своё значение, нам необходимо сообщить об этом фреймворку, и наоборот. Каждый редактор, созданный фабрикой, в нашей мапе editorToProperty, мы связываем со свойством, для редактирования которого он был создан.

Противоположная связь сохраняется в map createEditors, где свойства связаны с редакторами. Поскольку возможно, что два или более браузеров свойств отображают один и тот же экземпляр свойства, нам может потребоваться создать множество редакторов для одного и того же экземпляра свойства, поэтому мы ведем список редакторов для каждого свойства.

В сочетании с приватными слотами эти структуры данных позволяют нам информировать фреймворк о том, что значение редактора было изменено, и позволяют нам обновлять все соответствующие редакторы всякий раз, когда фреймворк изменяет какое-либо из свойств.

Когда фабрика уничтожается, состояние свойств больше не синхронизируется, и данные, отображаемые редакторами, перестают быть действительными. По этой причине все редакторы, созданные фабрикой, удаляются в деструкторе фабрики. Поэтому при уничтожении редактора необходимо убедиться, что он удален из внутренних maps.

Процесс добавления пользовательского свойства эквивалентен добавлению любого другого свойства:

FilePathManager *filePathManager;
FileEditFactory *fileEditFactory 
QtProperty *example;
 
filePathManager = new FilePathManager;
example = filePathManager->addProperty("Example");
 
filePathManager->setValue(example, "main.cpp");
filePathManager->setFilter(example, 
                           "Source files (*.cpp *.c)");
 
fileEditFactory = new FileEditFactory;
browser->setFactoryForManager(filePathManager, fileEditFactory);
task1->addSubProperty(example);

[править] Расширение с помощью Вариантов (Extending with Variants)

Вариантный подход также может быть использован на пользовательских типах. Используя этот подход, мы получаем менеджер из QtVariantPropertyManager, а нашу фабрику - из QtVariantPropertyFactory.

In the previous example, the FilePathManager class handled the datainternally for our custom property type. In this case, we define theproperty type as a path value with an associated filter attribute. Both thepath and the filter are stored as strings, and we define a new property typefor the file path as a whole:

class FilePathPropertyType
{
};
Q_DECLARE_METATYPE(FilePathPropertyType)

The custom manager must reimplement several inherited functions andprovide a static function to return the new property type:

int VariantManager::filePathTypeId()
{
    return qMetaTypeId<FilePathPropertyType>();
}

This function returns a unique identifier for our file path property typethat can be passed to QtVariantPropertyManager::addProperty() tocreate new file path properties.

Unlike the FilePathManager class, all the functions inherited fromthe base class already have an implementation; all we have to do is tointercept the process whenever our custom property type occurs. Forexample, when reimplementing the isPropertyTypeSupported()function we should return true for our custom type, and callthe base class implementation for all other types:

bool VariantManager::isPropertyTypeSupported(
     int propertyType) const
{
    if (propertyType == filePathTypeId())
        return true;
    return QtVariantPropertyManager::
        isPropertyTypeSupported(propertyType);
}

The VariantFactory class, like every other editor factory, doesnot have to provide any new members. Basically, the implementationis very similar to FileEditFactory in the previous section. The onlydifference in the connectPropertyManager() function is that we mustconnect the manager's attributeChanged() signal to a generalslotPropertyAttributeChanged() slot instead of connecting a specificsignal for the filter to a specific slot to handle changes.

We also need to check that the property can handle our custom type in thecreateEditor() function:

if (manager->propertyType(property) == VariantManager::filePathTypeId()) {
  ...
  connect(editor, SIGNAL(filePathChanged(const QString&amp;)),
          this, SLOT(slotSetValue(const QString &amp;)));
  connect(editor, SIGNAL(destroyed(QObject *)),
          this, SLOT(slotEditorDestroyed(QObject *)));
  return editor;
}
...

We also monitor the editor via its destroyed() and customfilePathChanged() signals.

Using custom variant managers and factories is no different thanusing those delivered by the framework:

QtVariantPropertyManager *variantManager;
variantManager = new VariantManager;
QtVariantEditorFactory *variantFactory;
variantFactory = new VariantFactory;
 
QtVariantProperty *example;
example = variantManager->addProperty(
          VariantManager::filePathTypeId(), "Example");
example->setValue("main.cpp");
example->setAttribute("filter", "Source files (*.cpp *.c)");
 
QtVariantProperty *task1;
task1 = variantManager->addProperty(
    QtVariantPropertyManager::groupTypeId(), "Task 1");
task1->addSubProperty(example);

All that's left to do is to set up the browser to use the propertymanager and factory in the same way as before.

[править] Summary

The Property Browser Solution provides a framework for creating propertyeditors that can be used and extended with either a type-specificor a variant-based API.

The type-specific approach provides a specialized API for each property thatmakes compile-time checking possible. In this approach, the managers andfactories can easily be subclassed and used in other projects. In addition,the developer can customize the property browser with new editorfactories without subclassing.

The variant-based approach provides immediate convenience for the developer,allowing simple, extensible code to be written. However, it can be difficultto integrate into a more complex code base, and QtVariantEditorFactorymust be subclassed to change the default editors.

The first release of the Property Browser framework contained a comprehensive set of examples and documentation. A new version, incorporatingseveral new improvements is expected in the next Qt Solutions release.


Copyright © 2006 Trolltech Trademarks

[править] Текст заголовка