Development/Tutorials/Services/Traders: Difference between revisions

    From KDE TechBase
    No edit summary
    No edit summary
    (13 intermediate revisions by 9 users not shown)
    Line 1: Line 1:
    {{TutorialBrowser|
    {{TutorialBrowser|


    Line 18: Line 20:
    == A Tale of Two Traders ==
    == A Tale of Two Traders ==


    KDE provides two classes that act as "traders". A trader takes a query and returns a set of services that match those constraints. There is one trader for plugins and other add-ons, {{class|KServiceTypeTrader}}, and one for mimetypes, {{class|KMimetypeTrader}}. Both use the same syntax for querying, offer a singleton pattern accessor and return {{class|KService}}::Ptrs, among other similarities. So while this tutorial concentrates primarily on the {{class|KServiceTypeTrader}}, much of the content is applicable to the mimetype trader as well.
    KDE provides two classes that act as "traders". A trader takes a query and returns a set of services that match those constraints. There is one trader for plugins and other add-ons: {{class|KServiceTypeTrader}}, and one for mimetypes: {{class|KMimetypeTrader}}. Characteristics for both are similar: they have same syntax for querying, offer a singleton pattern accessor, return {{class|KService}}::Ptrs, as well as other similarities. So while this tutorial concentrates primarily on the {{class|KServiceTypeTrader}}, much of the content is applicable to the mimetype trader as well.


    == Service Types ==
    == Service Types ==


    {{class|KServiceTypeTrader}} is used to locate individual components such as application plugins, screensavers and control panels that are registered with the system. The primary concept used is that of the "service type". Each set of services has a unique service type, which makes it very easy to locate exactly the sort of component needed.
    {{class|KServiceTypeTrader}} is used to locate individual components such as application plugins, screensavers, and control panels that are registered with the system. The primary concept used here, is that of the "service type". Each set of services has a unique service type, which makes it very easy to locate the sort of component needed.


    This means that each kind of application plugin is uniquely namespaced within the set of all services so it is trivial to locate plugins for a given application without having to worry about also getting another application's plugins in the list.
    This means that, each kind of application plugin is uniquely namespaced within the set of all services. So, it is trivial to locate plugins for a given application, without having to worry about getting another application's plugin in the list.


    Examples of service types include:
    Examples of service types include:
    Line 35: Line 37:
    *KDevelop/Plugin
    *KDevelop/Plugin


    There is no limit to the number of service types that a given application may use or register. Creating new service types is covered in the next tutorial in the series, [[Services/Creating and Loading Plugins Using KService]]. Of course, service types are not limited to plugins and may be used for any sort of data component.
    There is no limit to the number of service types that a given application may use or register. Of course, service types are not limited to plugins and may be used for any sort of data component.
     
    Creating new service types is covered in the next tutorial of this series:
    [[Development/Tutorials/Services/Plugins|Creating and Loading Plugins Using KService]]


    == Basic KServiceTypeTrader Usage ==
    == Basic KServiceTypeTrader Usage ==


    The service trader is always accessed via the <tt>self()</tt> singleton accessor. With the {{class|KServiceTypeTrader}} in hand, we can then do one of three things:
    The service trader is always accessed via the <tt>self()</tt> singleton accessor. With the {{class|KServiceTypeTrader}} in hand, we can then do one of three things:
    *'''query''':query for a list of services ordered according to the user's preferences (if any)
    *'''query''': query for a list of services ordered according to the user's preferences (if any)
    *'''defaultOffers''': request all services, unsorted
    *'''defaultOffers''': request all services, unsorted
    *'''preferredService''': request the preferred service of a given type
    *'''preferredService''': request the preferred service of a given type


    The code to do this is very straight forward:
    The code to do this is very straight forward:
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    KService::List services;
    KService::List services;
    KServiceTypeTrader* trader = KServiceTypeTrader::self();
    KServiceTypeTrader* trader = KServiceTypeTrader::self();
    Line 51: Line 56:
    services = trader->query("KParts/ReadWritePart");
    services = trader->query("KParts/ReadWritePart");
    foreach (KService::Ptr service, services) {
    foreach (KService::Ptr service, services) {
         kDebug() << "read write part " << service->name() << endl;
         kDebug() << "read write part" << service->name();
    }
    }


    services = trader->defaultOffers("ThumbCreator");
    services = trader->defaultOffers("ThumbCreator");
    if (services.isEmpty()) {
    if (services.isEmpty()) {
         kDebug() << "no services found for ThumbCreator!" << endl;
         kDebug() << "no services found for ThumbCreator!";
    }
    }


    KService::Ptr service = trader->preferredService("KDevelop/Plugin");
    KService::Ptr service = trader->preferredService("KDevelop/Plugin");
    if (!service) {
    if (!service) {
         kDebug() << "no preferred service found for KDevelop/Plugin"
         kDebug() << "no preferred service found for KDevelop/Plugin";
                << endl;
    }
    }
    </code>
    </syntaxhighlight>


    In each case zero or more services are returned that are now usable for locating, describing and loading the item it represents.
    In each case zero or more services are returned that are now usable for locating, describing and loading the item it represents.
    Line 70: Line 74:
    == The KTrader Query Language ==
    == The KTrader Query Language ==


    The above examples are quite simplistic and are not detailed enough for many application needs. For instance, one may want to list only plugins with a certain category, that are associated with a particular mimetype or that have a specific plugin name. This is where the query language comes in.
    The above examples are quite simplistic and are not detailed enough for many application needs. For instance, we may only want to list plugins for a certain category, that are associated with a particular mimetype, or that have a specific plugin name. This is where the query language comes in.


    The query language itself is designed to be human readable and flexible. <tt>{{class|KServiceTypeTrader}}::query</tt> optional takes a query in addition to the service type and uses that to provide more fine-grained searches. So example, we may modify the earlier example in this way:
    The query language itself is designed to be human readable and flexible. <tt>{{class|KServiceTypeTrader}}::query</tt> optionally takes a query, in addition to the service type, and uses that to provide more fine-grained searches. So, for example, we'll modify the earlier example in the following way:


    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    KService::List services ;
    KService::List services ;
    KServiceTypeTrader* trader = KServiceTypeTrader::self();
    KServiceTypeTrader* trader = KServiceTypeTrader::self();
    Line 84: Line 88:


    foreach (KService::Ptr service, services) {
    foreach (KService::Ptr service, services) {
         kDebug() << "read write part " << service->name() << endl;
         kDebug() << "read write part" << service->name();
    }
    }
    </code>
    </syntaxhighlight>


    This would look for a KPart capable of both reading and writing plain text files that is also a KOffice component or which can read ODF document formats.
    This code will look for a KPart that is both capable of reading/writing plain text files and is also a KOffice component, or can simply read ODF document formats.


    Errors in queries are reported via debug output to console at runtime.
    Errors in queries are reported via debug output to console at runtime.
    Line 94: Line 98:
    === Order of Operations ===
    === Order of Operations ===


    The query string is evaluated left to right one section at a time. Sections are divided by boolean operators (see below) or by parentheses ('()') which serve as grouping characters.
    The query string is evaluated left to right, one section at a time. Sections are divided by boolean operators (see below) or by parentheses ('()'), which serve as grouping characters.


    === Literals ===
    === Literals ===
    Line 106: Line 110:
    === Identifiers ===
    === Identifiers ===
       
       
    Identifiers in query string are mapped to entries listed in the service's <tt>.desktop</tt> file. For example, "Name" is the name of the service, "ServiceTypes" is a list of the service types it supports.  
    Identifiers in a query string are mapped to entries listed in the service's <tt>.desktop</tt> file. For example, "Name" is the name of the service, "ServiceTypes" is a list of the service types it supports.  


    Identifiers may contain only alphanumeric characters and the '-' character.
    Identifiers may only contain alphanumeric characters and the '-' character.
    Identifiers that do not start with an alphabetical character or contain non-alphanumeric characters must be enclosed in brackets, e.g. [X-KDE-Init].  
    Identifiers that do not start with an alphabetical character or that contain non-alphanumeric characters must be enclosed in brackets, e.g. [X-KDE-Init].  


    There are also three special identifiers:
    There are also three special identifiers:


    *'''DesktopEntryName''' stands for the filename of the service desktop entry without any extension. This can be useful to exclude some specific services.
    *'''DesktopEntryName''' stands for the filename of the service desktop entry without its extension. This can be useful to exclude some specific services.
    *'''DesktopEntryPath''' stands for the relative or full path to the .desktop file, see {{class|KService}}::desktopEntryPath.
    *'''DesktopEntryPath''' stands for the relative or full path to the .desktop file, see {{class|KService}}::desktopEntryPath.
    *'''Library''': a synonym for [X-KDE-Library] in the .desktop file.
    *'''Library''': a synonym for [X-KDE-Library] in the .desktop file.


    An identifier can be checked for existence with the '''exist''' operator, e.g. "exist [X-KDE-Library]". This is especially useful when checking for the value of an identifier with a default value. For example, if MyProp is a property with type boolean one might write "not exist MyProp or MyProp" to match MyProp with a default of "true".
    An identifier can be checked for existence with the '''exist''' operator, e.g. "exist [X-KDE-Library]". This is especially useful when checking for the value of an identifier with a default value. For example, if MyProp is a property with type boolean, one might write "not exist MyProp or MyProp" to match MyProp with a default of "true".
     
    <syntaxhighlight lang="cpp-qt">
    QString term = "konq";
    QString query = QString("exist Exec and ((exist Keywords and '%1' ~subin Keywords) or (exist GenericName and '%1' ~~ GenericName) or (exist Name and '%1' ~~ Name))").arg(term);
    KService::List services = KServiceTypeTrader::self()->query("Application", query);
    foreach (KService::Ptr service, services) {
        kDebug() << service->name();
    }
    </syntaxhighlight>


    === Comparison Operators ===
    === Comparison Operators ===


    The following comparison operators may be used to compare values of any of the supported data types:
    The following comparison operators can be used to compare values of any of the supported data types:


    *'''==''': equality
    *'''==''': equality
    Line 149: Line 162:
    === String Matching Operators ===
    === String Matching Operators ===


    The string matching operators all return a boolean value indicating success or failure.
    The string matching operators all return a boolean value, indicating success or failure.


    String comparisons are done using the following operators which:
    String comparisons are done using the following operators:


    *'''==''': equality, case sensitive
    *'''==''': equality, case sensitive
    Line 158: Line 171:
    *'''!~''': inequality, case insensitive
    *'''!~''': inequality, case insensitive


    Sub-string matching can be accomplished using the following operators:
    Sub-string matching can be accomplished by using the following operators:


    * '''~''': contains, e.g. "'Bar' ~ 'FooBarBaz'" is true
    * '''~''': contains, e.g. "'Bar' ~ 'FooBarBaz'" is true
    * '''~~''': contains with case insensitive matching, e.g. "'Bar' ~ 'FoobarBaz'" is true
    * '''~~''': contains with case insensitive matching, e.g. "'Bar' ~~ 'FoobarBaz'" is true


    Some properties, such as MimeTypes and ServiceTypes, are lists of strings. These lists can be search using the following two operators:
    Some properties, such as MimeTypes and ServiceTypes, are lists of strings. These lists can be searched using the following operators:


    * '''in'': the list contains the string, e.g. "'MyApp/Plugin' in ServiceTypes'"
    * '''in''': the list contains the string, e.g. "'MyApp/Plugin' in ServiceTypes'"
    * '''~in''': the list contains the string, with case insensitive matching
    * '''~in''': the list contains the string, with case insensitive matching
    * '''subin''': the list contains the string as a substring of one of the entries, e.g. "'Plugin' subin ServiceTypes'"
    * '''~subin''': the list contains the string as a substring of one of the entries, with case insensitive matching
    == Command line tool ==
    <pre>
    ktraderclient --mimetype <var>mimetype</var> --servicetype <var>servicetype</var> --constraint <var>constraint</var>
    </pre>

    Revision as of 09:15, 14 July 2012


    Finding Services Using Trader Queries
    Tutorial Series   Services
    Previous   Introduction to the Services Framework
    What's Next   Creating and Loading Plugins Using KService
    Further Reading   n/a


    Abstract

    It is often desirable to be able to find specific types of services or services with specific features or designations. KDE provides a simple yet powerful query language to accomplish this called the KTrader Query Language.

    A Tale of Two Traders

    KDE provides two classes that act as "traders". A trader takes a query and returns a set of services that match those constraints. There is one trader for plugins and other add-ons: KServiceTypeTrader, and one for mimetypes: KMimetypeTrader. Characteristics for both are similar: they have same syntax for querying, offer a singleton pattern accessor, return KService::Ptrs, as well as other similarities. So while this tutorial concentrates primarily on the KServiceTypeTrader, much of the content is applicable to the mimetype trader as well.

    Service Types

    KServiceTypeTrader is used to locate individual components such as application plugins, screensavers, and control panels that are registered with the system. The primary concept used here, is that of the "service type". Each set of services has a unique service type, which makes it very easy to locate the sort of component needed.

    This means that, each kind of application plugin is uniquely namespaced within the set of all services. So, it is trivial to locate plugins for a given application, without having to worry about getting another application's plugin in the list.

    Examples of service types include:

    • KParts/ReadWritePart
    • KTextEditor/Document
    • ScreenSaver
    • TerminalEmulator
    • UserAgentStrings
    • ThumbCreator
    • KDevelop/Plugin

    There is no limit to the number of service types that a given application may use or register. Of course, service types are not limited to plugins and may be used for any sort of data component.

    Creating new service types is covered in the next tutorial of this series: Creating and Loading Plugins Using KService

    Basic KServiceTypeTrader Usage

    The service trader is always accessed via the self() singleton accessor. With the KServiceTypeTrader in hand, we can then do one of three things:

    • query: query for a list of services ordered according to the user's preferences (if any)
    • defaultOffers: request all services, unsorted
    • preferredService: request the preferred service of a given type

    The code to do this is very straight forward:

    KService::List services;
    KServiceTypeTrader* trader = KServiceTypeTrader::self();
    
    services = trader->query("KParts/ReadWritePart");
    foreach (KService::Ptr service, services) {
        kDebug() << "read write part" << service->name();
    }
    
    services = trader->defaultOffers("ThumbCreator");
    if (services.isEmpty()) {
        kDebug() << "no services found for ThumbCreator!";
    }
    
    KService::Ptr service = trader->preferredService("KDevelop/Plugin");
    if (!service) {
        kDebug() << "no preferred service found for KDevelop/Plugin";
    }
    

    In each case zero or more services are returned that are now usable for locating, describing and loading the item it represents.

    The KTrader Query Language

    The above examples are quite simplistic and are not detailed enough for many application needs. For instance, we may only want to list plugins for a certain category, that are associated with a particular mimetype, or that have a specific plugin name. This is where the query language comes in.

    The query language itself is designed to be human readable and flexible. KServiceTypeTrader::query optionally takes a query, in addition to the service type, and uses that to provide more fine-grained searches. So, for example, we'll modify the earlier example in the following way:

    KService::List services ;
    KServiceTypeTrader* trader = KServiceTypeTrader::self();
    
    QString constraint = "'text/plain' in MimeTypes and "
                         "('KOfficePart' in ServiceTypes or "
                         " 'oasis' ~ [X-KDE-ExtraNativeMimeTypes])";
    services = trader->query("KParts/ReadWritePart", constraint);
    
    foreach (KService::Ptr service, services) {
        kDebug() << "read write part" << service->name();
    }
    

    This code will look for a KPart that is both capable of reading/writing plain text files and is also a KOffice component, or can simply read ODF document formats.

    Errors in queries are reported via debug output to console at runtime.

    Order of Operations

    The query string is evaluated left to right, one section at a time. Sections are divided by boolean operators (see below) or by parentheses ('()'), which serve as grouping characters.

    Literals

    Three types of literals are supported by the KTrader Query Language:

    • strings: character strings, which must be enclosed in single quotes (')
    • booleans: TRUE or FALSE
    • signed integers
    • signed floating point numbers

    Identifiers

    Identifiers in a query string are mapped to entries listed in the service's .desktop file. For example, "Name" is the name of the service, "ServiceTypes" is a list of the service types it supports.

    Identifiers may only contain alphanumeric characters and the '-' character. Identifiers that do not start with an alphabetical character or that contain non-alphanumeric characters must be enclosed in brackets, e.g. [X-KDE-Init].

    There are also three special identifiers:

    • DesktopEntryName stands for the filename of the service desktop entry without its extension. This can be useful to exclude some specific services.
    • DesktopEntryPath stands for the relative or full path to the .desktop file, see KService::desktopEntryPath.
    • Library: a synonym for [X-KDE-Library] in the .desktop file.

    An identifier can be checked for existence with the exist operator, e.g. "exist [X-KDE-Library]". This is especially useful when checking for the value of an identifier with a default value. For example, if MyProp is a property with type boolean, one might write "not exist MyProp or MyProp" to match MyProp with a default of "true".

    QString term = "konq";
    QString query = QString("exist Exec and ((exist Keywords and '%1' ~subin Keywords) or (exist GenericName and '%1' ~~ GenericName) or (exist Name and '%1' ~~ Name))").arg(term);
    KService::List services = KServiceTypeTrader::self()->query("Application", query);
    foreach (KService::Ptr service, services) {
        kDebug() << service->name();
    }
    

    Comparison Operators

    The following comparison operators can be used to compare values of any of the supported data types:

    • ==: equality
    • !=: inequality
    • <: less than
    • <=: less than or equal to
    • >: greater than
    • >=: greater than or equal to

    Mathematical Operators

    The following mathematical operators can be used with numerical types:

    • +
    • -
    • *
    • /

    Boolean Operators

    The following boolean operators are supported:

    • and
    • or
    • not

    String Matching Operators

    The string matching operators all return a boolean value, indicating success or failure.

    String comparisons are done using the following operators:

    • ==: equality, case sensitive
    • =~: equality, case insensitive
    • !=: inequality, case sensitive
    • !~: inequality, case insensitive

    Sub-string matching can be accomplished by using the following operators:

    • ~: contains, e.g. "'Bar' ~ 'FooBarBaz'" is true
    • ~~: contains with case insensitive matching, e.g. "'Bar' ~~ 'FoobarBaz'" is true

    Some properties, such as MimeTypes and ServiceTypes, are lists of strings. These lists can be searched using the following operators:

    • in: the list contains the string, e.g. "'MyApp/Plugin' in ServiceTypes'"
    • ~in: the list contains the string, with case insensitive matching
    • subin: the list contains the string as a substring of one of the entries, e.g. "'Plugin' subin ServiceTypes'"
    • ~subin: the list contains the string as a substring of one of the entries, with case insensitive matching

    Command line tool

    ktraderclient --mimetype <var>mimetype</var> --servicetype <var>servicetype</var> --constraint <var>constraint</var>