Difference between revisions of "Images (for developers)"

From Trinity Desktop Project Wiki
Jump to navigation Jump to search
(Added basic instructions for loading and displaying images)
m (Remove WIP marking (still has a few TODOs))
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{WIP|Blu256}}
 
 
 
Working with images is an important part of a graphical application, whether it's a simple application or a complex graphical suite. In TQt/TDE, this can be as simple as learning to use a few classes. This guide aims to be a good guide for developers who want to work with TQt/TDE.
 
Working with images is an important part of a graphical application, whether it's a simple application or a complex graphical suite. In TQt/TDE, this can be as simple as learning to use a few classes. This guide aims to be a good guide for developers who want to work with TQt/TDE.
   
Line 15: Line 13:
 
Usually you won't need more advanced classes.
 
Usually you won't need more advanced classes.
   
== Loading an image ==
+
== Basic usage ==
  +
=== Loading an image ===
The simplest way to load an image is to use the constructor of the {{TQt class|TQPixmap}} class with the path of the file you want to load. If the file is located in one of your application paths (e.g. the <tt>share</tt> folder) use the [http://trinitydesktop.org/docs/trinity/tdelibs/tdecore/html/classTDEStandardDirs.html TDEStandardDirs] class.
 
  +
The simplest way to load an image is to use the constructor of the {{TQt class|TQPixmap}} class with the path of the file you want to load. If the file is located in one of your application paths (e.g. the <tt>share</tt> folder) use the {{TDE class|tdelibs|tdecore|TDEStandardDirs}} class.
   
If you want to load an icon, the simplest (and by far the most preferred) way to do so is via the [http://trinitydesktop.org/docs/trinity/tdelibs/tdecore/html/classTDEIconLoader.html TDEIconLoader] class. For TDE applications you don't even have to explicitly initialize this object. You can access a TDEIconLoader instance from the [http://trinitydesktop.org/docs/trinity/tdelibs/tdecore/html/classTDEApplication.html TDEApplication] class. The TDEIconLoader class makes sure that recommended settings are used for the kind of icon you are loading, so you usually need to provide a `group` parameter of type [http://trinitydesktop.org/docs/trinity/tdelibs/tdecore/html/classTDEIcon.html#a8d521faaf8f00ecbe33314a08d386f91 TDEIcon::Group] which defines the type of icon that you are loading and will decide its size and whether any effects will be applied to it. For example, to load a folder icon with the default settings for a toolbar icon:
+
If you want to load an icon, the simplest (and by far the most preferred) way to do so is via the {{TDE class|tdelibs|tdecore|TDEIconLoader}} class. For TDE applications you don't even have to explicitly initialize this object. You can access an instance of this class through the `kapp` pointer of the {{TDE class|tdelibs|tdecore|TDEApplication}} class. The icon loader class makes sure that recommended settings are used for the kind of icon you are loading, so you usually need to provide a `group` parameter of type [http://trinitydesktop.org/docs/trinity/tdelibs/tdecore/html/classTDEIcon.html#a8d521faaf8f00ecbe33314a08d386f91 TDEIcon::Group] which defines the type of icon that you are loading and will decide its size and whether any effects will be applied to it. For example, to load a folder icon with the default settings for a toolbar icon:
   
 
<syntaxhighlight lang="c++">
 
<syntaxhighlight lang="c++">
Line 24: Line 23:
 
</syntaxhighlight>
 
</syntaxhighlight>
   
  +
If you want to let the user choose an image, you can make use of {{TDE class|tdelibs|tdeio/tdefile|KFileDialog}}. You might want to use '''KImageIO''' in your project to support more image formats.
== Displaying an image ==
 
  +
  +
=== Displaying an image ===
 
Images can be used in quite a few widgets, including labels, text views and buttons. These widgets offer constructors and/or functions where you can pass an image as an argument (usually as a {{TQt class|TQPixmap}} or a {{TQt class|TQIconSet}}.
 
Images can be used in quite a few widgets, including labels, text views and buttons. These widgets offer constructors and/or functions where you can pass an image as an argument (usually as a {{TQt class|TQPixmap}} or a {{TQt class|TQIconSet}}.
   
Line 35: Line 36:
   
 
If you want to display an image as standalone, you first need a widget where you will display it. Usually, a {{TQt class|TQLabel}} should be enough.
 
If you want to display an image as standalone, you first need a widget where you will display it. Usually, a {{TQt class|TQLabel}} should be enough.
  +
  +
=== Vector images (SVG) ===
  +
{{TODO}}
  +
  +
=== Applying effects ===
  +
{{TODO}}
  +
  +
== Advanced usage ==
  +
=== Loading KImageIO ===
  +
KImageIO is a library which allows applications that use the TQt library (i.e. TQImageIO, TQImage, TQPixmap and friends) to read and write images in extra formats. It essentially extends TQImageIO with support for more image formats.
  +
  +
To make use of KImageIO you only need to:
  +
# link your application with the <tt>tdeio</tt> library
  +
# Include the <kimageio.h> header
  +
# call KImageIO::registerFormats() once, somewhere in your code, before you load an image.
  +
  +
=== Saving an image ===
  +
Saving an image is a complex process which involves a lot of factors, such as image format, quality, remote vs. local destination etc. Fortunately it's made easy thanks to technologies such as [[TDEIO]] and KImageIO.
  +
  +
Let's start with the basics.
  +
  +
==== The Foundation: <tt>TQImage::save(...)</tt> ====
  +
{{TQt class|TQImage}} provides a convenient <code>save(const TQString &fileName, const char *format, int quality = -1)</code> method which, as you can tell, saves the image in memory to the selected destination (<tt>fileName</tt>).
  +
  +
The <tt>format</tt> argument is a string that represents the image format in which the file will be saved. The allowed values are determined by the file formats for which {{TQt class|TQImageIO}} has write support.
  +
  +
TQt natively supports PNG, BMP, XBM, XPM, PNM, JPEG, MNG and GIF formats (support for the latter three formats is configured at compile-time). For details on these formats refer to the {{TQt class|TQImageIO}} API documentation.
  +
  +
'''KImageIO''' adds support for more file formats, such as JPEG2000, EPS and TGA.
  +
  +
For example, to save an image <code>TQImage img</code> in PNG format, you could use the following call:
  +
  +
<syntaxhighlight lang="c++">
  +
img.save("/home/test/image.png", "PNG");
  +
</syntaxhighlight>
  +
  +
The <tt>quality</tt> parameter is pretty self-explanatory. Its default value is -1, which will make TQt use the default quality when saving.
  +
  +
You can combine the above code with {{TDE class|tdelibs|tdeio/tdefile|KFileDialog}} to get a decent save dialog which can save your PNG image locally:
  +
  +
<syntaxhighlight lang="c++">
  +
TQString saveFile = KFileDialog::getSaveFileName(TQString::null, "*.PNG|" + i18n("PNG image (*.png)"), this);
  +
if (!saveFile.isEmpty()) {
  +
img.save(saveFile, "PNG");
  +
}
  +
</syntaxhighlight>
  +
  +
But what if we want more? What if we want to support more file formats? And what about saving on another computer via (S)FTP or another protocol?
  +
  +
==== Better image saving (feat. KImageIO) ====
  +
Let's improve the situation with file formats by making use of some features of KImageIO.
  +
  +
We shall start by querying KImageIO for all the image formats it can write to:
  +
  +
<syntaxhighlight lang="c++">
  +
TQStringList mimeTypes = KImageIO::mimeTypes(KImageIO::Writing);
  +
if (mimeTypes.isEmpty()) {
  +
kdError() << "kimgio: no image formats support writing!" << endl;
  +
}
  +
</syntaxhighlight>
  +
  +
This example shows some basic error handling. You should probably properly inform the user about the failure and abort the save operation at that point.
  +
  +
Then we can construct a file dialog based on the MIME information that we obtained. It's worth noting that the convenient static methods of {{TDE class|tdelibs|tdeio/tdefile|KFileDialog}} cannot take a <tt>TQStringList mimeTypes</tt> as argument, so we will have to construct the dialog more or less manually:
  +
  +
<syntaxhighlight lang="c++">
  +
KFileDialog *saveDlg = new KFileDialog(TQString::null, TQString::null, this, "mysavedialog", true);
  +
saveDlg->setOperationMode(KFileDialog::Saving);
  +
saveDlg->setMimeFilter(mimeTypes);
  +
</syntaxhighlight>
  +
  +
Then we can execute the dialog:
  +
  +
<syntaxhighlight lang="c++">
  +
if (!saveDlg->exec()) {
  +
// The dialog was canceled
  +
}
  +
</syntaxhighlight>
  +
  +
And finally, after it has been closed, we can obtain the URL where we will save the file. It's a good idea to check the input thoroughly. Once again, you should implement proper error handling for when something goes wrong. I cannot stress enough the importance of sane error handling.
  +
  +
<syntaxhighlight lang="c++">
  +
KURL saveURL = saveDlg->selectedURL();
  +
  +
TQString type = KImageIO::type(saveURL.url());
  +
if (!KImageIO::canWrite(type)) {
  +
kdError() << "kimgio: writing to image format " << type << " is not supported" << endl;
  +
}
  +
</syntaxhighlight>
  +
  +
For simplicity's sake we will only accept local URLs for now.
  +
  +
<syntaxhighlight lang="c++">
  +
if (!saveURL.isLocalFile()) {
  +
kdError() << "Cannot save to remote location (yet!)" << endl;
  +
return;
  +
}
  +
</syntaxhighlight>
  +
  +
So, now that we have the URL, what's next? Easy, we actually save the image:
  +
  +
<syntaxhighlight lang="c++">
  +
bool ok = img.save(saveURL.path(), type.latin1());
  +
if (!ok) {
  +
kdError() << "Couldn't save!" << endl;
  +
}
  +
</syntaxhighlight>
  +
  +
Note how we use <tt>path()</tt> on saveURL instead of <tt>url()</tt>. It is because the function accepts a file path. By design it will not accept even local <tt>file://</tt> URLs.
  +
  +
So how do we save to a remote location?
  +
  +
==== Even better image saving (feat. TDEIO) ====
  +
At some point you might have wondered how TDE applications are able to save to a remote or network location almost as seamlessly as to a directory on your hard drive. The TDEIO component is responsible for all the behind-the-scenes magic. In other word, you don't need to make your application speak FTP in order to be able to save an image to a remote FTP server. TDEIO automatically infers the protocol from the URL and starts a subprogram which handles all the needed communication and hides the protocol specifics behind well-known file operations, such as copy or delete.
  +
  +
Most TDE applications do not make use of TDEIO itself, but rather <tt>TDEIO::NetAccess</tt>, an API meant to simplify network file operations. We will only need one of its methods, <tt>upload(...)</tt>, which takes a local file URL and a remote destination URL as arguments. Yes, you read that right. We need to create a temporary local file, which we will upload to the user's chosen destination, and which we will then remove. Fortunately, TDE provides just the class for the occasion; that is {{TDE class|tdelibs|tdecore|KTempFile}}.
  +
  +
Remember the mast two chunks of code we previously wrote? Time to rewrite them!
  +
  +
<syntaxhighlight lang="c++">
  +
if (saveURL.isLocalFile()) {
  +
bool ok = img.save(saveURL.path(), type.latin1());
  +
if (!ok) {
  +
kdError() << "Couldn't save!" << endl;
  +
}
  +
}
  +
else {
  +
</syntaxhighlight>
  +
  +
We have the local file handler from our previous effort. Let's proceed to write the handler for non-local files then. First, we'll create a new {{TDE class|tdelibs|tdecore|KTempFile}}:
  +
<syntaxhighlight lang="c++">
  +
KTempFile tempFile;
  +
tempFile.setAutoDelete(true);
  +
TQString tempFileName = tempFile.name();
  +
if (tempFileName.isEmpty()) {
  +
kdError() << "could not create a temp file!" << endl;
  +
return;
  +
}
  +
</syntaxhighlight>
  +
  +
Now let's save our image to that file:
  +
<syntaxhighlight lang="c++">
  +
bool ok = img.save(tempFileName, type.latin1());
  +
if (!ok) {
  +
kdError() << "Couldn't write temporary file: " << tempFileName << endl;
  +
return;
  +
}
  +
</syntaxhighlight>
  +
  +
And finally, let's upload that file to the remote location:
  +
<syntaxhighlight lang="c++">
  +
if (!TDEIO::NetAccess::upload(tempFileName, saveURL, this)) {
  +
kdError() << "Could not upload file to " << saveURL << endl;
  +
}
  +
</syntaxhighlight>
  +
  +
We now have a file dialog which can save in every image format that TQt and TDE support writing to, and which supports both local and non-local filesystems.
  +
  +
== TDE Developers' usage ==
  +
=== Adding image format support ===
  +
{{TODO}}
   
 
[[Category: Developers]]
 
[[Category: Developers]]

Latest revision as of 22:05, 25 November 2023

Working with images is an important part of a graphical application, whether it's a simple application or a complex graphical suite. In TQt/TDE, this can be as simple as learning to use a few classes. This guide aims to be a good guide for developers who want to work with TQt/TDE.

Image classes

  • TQPixmap API is one of the two basic image classes provided by TQt.
It is, essentially, a paint device, so it is optimized for drawing. You'll see this class used a lot (more than TQImage) because of this quality.
  • TQImage API is the other basic image class.
According to the API docs, it is "a hardware-independent pixmap representation" and it is optimized for I/O operations and direct pixel access and manipulation.
  • TQIconSet API is a helper class for icons.
Compared to the basic image classes, this class does not represent a single image, but a set of images which correspond to an icon. A folder icon can come in different sizes (a different image file corresponds to each size), but it is still considered to be the same icon.

Usually you won't need more advanced classes.

Basic usage

Loading an image

The simplest way to load an image is to use the constructor of the TQPixmap API class with the path of the file you want to load. If the file is located in one of your application paths (e.g. the share folder) use the TDEStandardDirs API class.

If you want to load an icon, the simplest (and by far the most preferred) way to do so is via the TDEIconLoader API class. For TDE applications you don't even have to explicitly initialize this object. You can access an instance of this class through the `kapp` pointer of the TDEApplication API class. The icon loader class makes sure that recommended settings are used for the kind of icon you are loading, so you usually need to provide a `group` parameter of type TDEIcon::Group which defines the type of icon that you are loading and will decide its size and whether any effects will be applied to it. For example, to load a folder icon with the default settings for a toolbar icon:

TQPixmap icon = kapp->iconLoader()->loadIcon("folder", TDEIcon::Toolbar);

If you want to let the user choose an image, you can make use of KFileDialog API. You might want to use KImageIO in your project to support more image formats.

Displaying an image

Images can be used in quite a few widgets, including labels, text views and buttons. These widgets offer constructors and/or functions where you can pass an image as an argument (usually as a TQPixmap API or a TQIconSet API.

The following example creates a push button with a folder icon from the icon theme:

TQIconSet icon = kapp->iconLoader()->loadIconSet("folder", TDEIcon::Toolbar);
TQPushButton *button = new TQPushButton(icon, i18n("Open folder..."), this);

If you want to display an image as standalone, you first need a widget where you will display it. Usually, a TQLabel API should be enough.

Vector images (SVG)

Konqi.png
To-do
This section has not been written/completed yet. You can contribute to Trinity by writing or finalizing this section.

Applying effects

Konqi.png
To-do
This section has not been written/completed yet. You can contribute to Trinity by writing or finalizing this section.

Advanced usage

Loading KImageIO

KImageIO is a library which allows applications that use the TQt library (i.e. TQImageIO, TQImage, TQPixmap and friends) to read and write images in extra formats. It essentially extends TQImageIO with support for more image formats.

To make use of KImageIO you only need to:

  1. link your application with the tdeio library
  2. Include the <kimageio.h> header
  3. call KImageIO::registerFormats() once, somewhere in your code, before you load an image.

Saving an image

Saving an image is a complex process which involves a lot of factors, such as image format, quality, remote vs. local destination etc. Fortunately it's made easy thanks to technologies such as TDEIO and KImageIO.

Let's start with the basics.

The Foundation: TQImage::save(...)

TQImage API provides a convenient save(const TQString &fileName, const char *format, int quality = -1) method which, as you can tell, saves the image in memory to the selected destination (fileName).

The format argument is a string that represents the image format in which the file will be saved. The allowed values are determined by the file formats for which TQImageIO API has write support.

TQt natively supports PNG, BMP, XBM, XPM, PNM, JPEG, MNG and GIF formats (support for the latter three formats is configured at compile-time). For details on these formats refer to the TQImageIO API API documentation.

KImageIO adds support for more file formats, such as JPEG2000, EPS and TGA.

For example, to save an image TQImage img in PNG format, you could use the following call:

img.save("/home/test/image.png", "PNG");

The quality parameter is pretty self-explanatory. Its default value is -1, which will make TQt use the default quality when saving.

You can combine the above code with KFileDialog API to get a decent save dialog which can save your PNG image locally:

TQString saveFile = KFileDialog::getSaveFileName(TQString::null, "*.PNG|" + i18n("PNG image (*.png)"), this);
if (!saveFile.isEmpty()) {
    img.save(saveFile, "PNG");
}

But what if we want more? What if we want to support more file formats? And what about saving on another computer via (S)FTP or another protocol?

Better image saving (feat. KImageIO)

Let's improve the situation with file formats by making use of some features of KImageIO.

We shall start by querying KImageIO for all the image formats it can write to:

TQStringList mimeTypes = KImageIO::mimeTypes(KImageIO::Writing);
if (mimeTypes.isEmpty()) {
    kdError() << "kimgio: no image formats support writing!" << endl;
}

This example shows some basic error handling. You should probably properly inform the user about the failure and abort the save operation at that point.

Then we can construct a file dialog based on the MIME information that we obtained. It's worth noting that the convenient static methods of KFileDialog API cannot take a TQStringList mimeTypes as argument, so we will have to construct the dialog more or less manually:

KFileDialog *saveDlg = new KFileDialog(TQString::null, TQString::null, this, "mysavedialog", true);
saveDlg->setOperationMode(KFileDialog::Saving);
saveDlg->setMimeFilter(mimeTypes);

Then we can execute the dialog:

if (!saveDlg->exec()) {
    // The dialog was canceled
}

And finally, after it has been closed, we can obtain the URL where we will save the file. It's a good idea to check the input thoroughly. Once again, you should implement proper error handling for when something goes wrong. I cannot stress enough the importance of sane error handling.

KURL saveURL = saveDlg->selectedURL();

TQString type = KImageIO::type(saveURL.url());
if (!KImageIO::canWrite(type)) {
    kdError() << "kimgio: writing to image format " << type << " is not supported" << endl;
}

For simplicity's sake we will only accept local URLs for now.

if (!saveURL.isLocalFile()) {
    kdError() << "Cannot save to remote location (yet!)" << endl;
    return;
}

So, now that we have the URL, what's next? Easy, we actually save the image:

bool ok = img.save(saveURL.path(), type.latin1());
if (!ok) {
    kdError() << "Couldn't save!" << endl;
}

Note how we use path() on saveURL instead of url(). It is because the function accepts a file path. By design it will not accept even local file:// URLs.

So how do we save to a remote location?

Even better image saving (feat. TDEIO)

At some point you might have wondered how TDE applications are able to save to a remote or network location almost as seamlessly as to a directory on your hard drive. The TDEIO component is responsible for all the behind-the-scenes magic. In other word, you don't need to make your application speak FTP in order to be able to save an image to a remote FTP server. TDEIO automatically infers the protocol from the URL and starts a subprogram which handles all the needed communication and hides the protocol specifics behind well-known file operations, such as copy or delete.

Most TDE applications do not make use of TDEIO itself, but rather TDEIO::NetAccess, an API meant to simplify network file operations. We will only need one of its methods, upload(...), which takes a local file URL and a remote destination URL as arguments. Yes, you read that right. We need to create a temporary local file, which we will upload to the user's chosen destination, and which we will then remove. Fortunately, TDE provides just the class for the occasion; that is KTempFile API.

Remember the mast two chunks of code we previously wrote? Time to rewrite them!

if (saveURL.isLocalFile()) {
    bool ok = img.save(saveURL.path(), type.latin1());
    if (!ok) {
        kdError() << "Couldn't save!" << endl;
    }
}
else {

We have the local file handler from our previous effort. Let's proceed to write the handler for non-local files then. First, we'll create a new KTempFile API:

    KTempFile tempFile;
    tempFile.setAutoDelete(true);
    TQString tempFileName = tempFile.name();
    if (tempFileName.isEmpty()) {
        kdError() << "could not create a temp file!" << endl;
        return;
    }

Now let's save our image to that file:

    bool ok = img.save(tempFileName, type.latin1());
    if (!ok) {
        kdError() << "Couldn't write temporary file: " << tempFileName << endl;
        return;
    }

And finally, let's upload that file to the remote location:

if (!TDEIO::NetAccess::upload(tempFileName, saveURL, this)) {
    kdError() << "Could not upload file to " << saveURL << endl;
}

We now have a file dialog which can save in every image format that TQt and TDE support writing to, and which supports both local and non-local filesystems.

TDE Developers' usage

Adding image format support

Konqi.png
To-do
This section has not been written/completed yet. You can contribute to Trinity by writing or finalizing this section.