Pages

Layout and widget synchronization

Another couple of Qt features and some widgets in this post.

We see how to use QHBoxLayout to nicely put the contained widgets in an horizontal box; how to set the title in a window; we get acquainted with the spin box and the slider widget, and we see how to connect them, so that changing the value of one automatically result in the adjustment of the value of the other.

All this stuff in such a short example:

#include <QtGui/QApplication>
#include <QtGui/QHBoxLayout>
#include <QtGui/QSlider>
#include <QtGui/QSpinBox>

namespace
{
const int MIN_AGE = 0;
const int MAX_AGE = 130;
const int DEF_AGE = 35;
}

int b103(int argc, char** argv)
{
QApplication app(argc, argv);

QSpinBox* sb = new QSpinBox(); // 1.
sb->setRange(MIN_AGE, MAX_AGE);

QSlider* sl = new QSlider(Qt::Horizontal); // 2.
sl->setRange(MIN_AGE, MAX_AGE);

QObject::connect(sb, SIGNAL(valueChanged(int)), sl, SLOT(setValue(int))); // 3.
QObject::connect(sl, SIGNAL(valueChanged(int)), sb, SLOT(setValue(int)));

// spin box and slider now are connected
sb->setValue(DEF_AGE); // 4.

QHBoxLayout* layout = new QHBoxLayout(); // 5.
layout->addWidget(sb);
layout->addWidget(sl);

QWidget* w = new QWidget(); // 6.
w->setWindowTitle("Enter Your Age");
w->setLayout(layout);
w->show();

return app.exec();
}

1. create and then specify a range for a spinbox widget.
2. create a (horizontal) slider and then specify its range.
3. the connection between two widgets is not different to the connection between a widget an the application. We just specify the object and the method for the emitting widget and then the object and method for the receiving one.
4. now that our two widget are connected we can set a default value for one of them relying on the signal-slot mechanism to let the other being consequentely adjusted.
5. we create a horizontal box and put in it our two widgets.
6. the widget w is the main (and only) window of our application, we set its title calling the setWindowTitle() method, then we set its layout using the one we have just created, and show the result to the user.

I wrote this post as homework while reading "C++ GUI Programming with Qt 4, Second Edition" by Jasmin Blanchette and Mark Summerfield.

Go to the full post

A button to quit

Here we are just getting started with Qt, so this post is not very clear. Is just a first look to how to write a button to close our application. It is not a complicated issue, but it is not such an easy task to be clarified in few lines.

The complication is in the fact that we should create a connection between the "signal" that is generated by the button when clicked and the "slot" (actually, a function) that we want to execute.

To to that we use a static method of the QObject class, connect(), and a couple of macros, SIGNAL and SLOT.

So, here is the code for our little application, a button that fill all our tiny window and that, when clicked, close it.

#include <QtGui/QApplication>
#include <QtGui/QPushButton>

int b102(int argc, char** argv)
{
QApplication app(argc, argv);
QPushButton* button = new QPushButton("Quit");
QObject::connect(button, SIGNAL(clicked()), &app, SLOT(quit()));
button->show();
return app.exec();
}

In our case, when the button is clicked - or the blank key is pressed - the quit() method of our QApplication is called.

I wrote this post as homework while reading "C++ GUI Programming with Qt 4, Second Edition" by Jasmin Blanchette and Mark Summerfield.

Go to the full post

Hello Qt with a color touch

Once installed both VC++ 2010 and Qt on my machine (not a top notch one, a lap top a couple of years old with the infamous Windows Vista on - but enough for having some fun), I started reading "C++ GUI Programming with Qt 4, Second Edition" by Jasmin Blanchette and Mark Summerfield. Looks like a good book on the matter.

I'm about to write down a few posts during my reading, hoping that that could be useful for someone else too. At least as an hint on wich book to buy to knowing more on Qt.

The first chapter of the book is all about writing an hello Qt program, so close to the one I wrote in the previous post on Qt that someone could even think it is just the same. Luckly it is shown also another variant, in which the label is set with a HTML formatted text. Quite cool, isn't it?

#include <QtGui/QApplication>
#include <QtGui/QLabel>

int main(int argc, char** argv)
{
QApplication app(argc, argv);
QLabel* label = new QLabel(("<h2><i>Hello</i><font color=red>Qt!</font></h2>"));
label->show();

return app.exec();
}

Actually, there is another difference in this hello Qt program, compared to the one in the previous post. Here we have a memory leak, since we put the label on the heap, and not on the stack, without deleting it when done with it. Is that a major problem? Well, no, it isn't, since we are just about to leave the main.

Go to the full post

Hello Qt

To use Qt with VC++ 2010 you should set a few prerequisite.

In Project Properties - Configuration Properties

add C:\Qt\4.6.2\lib; (or your actual Qt lib directory) to Linker - General - Additional Library Directories
add QtCore4.lib;QtGui4.lib to Linker - Input - Additional Dependencies
add C:\Qt\4.6.2\include; (or your actual Qt include directory) to C/C++ - General - Additional Include Directories

Done that, it is quite easy to develop an hello Qt program:

#include <QtGui/QApplication>
#include <QtGui/QLabel>

int main(int argc, char** argv)
{
QApplication app(argc, argv);
QLabel label("Hello Qt!");
label.show();

return app.exec();
}

As any hello program, it is not doing much, just showing in a minimal window a greeting text. But we have created a real window that could be moved, resized, and even closed.

Go to the full post

System::IO

To access a file it is possible to use the System::IO::FileStream that provides access to the low level details of a file.

As example, we write a function, readFile(), that access a file and put its content in a managed array:

void readFile(System::String^ filename)
{
using System::IO::FileStream;
using System::IO::FileMode;

FileStream^ fs = gcnew FileStream(filename, FileMode::Open); // 1.

long long len = fs->Length;

System::Console::WriteLine("File length is {0}", len);

int size = static_cast(len); // 2.
array^ content = gcnew array(size);
fs->Read(content, 0, size);
fs->Close();
}

1. since we specify the mode FileMode::Open, if the file does not exists, an exception is thrown.
2. the length of a FileStream is defined as a long long, but the size of an array in just an int. Instead of simply casting to int, as we did here, we should add a bit of logic, to properly read all the file.

If we expect the file to be a text one, we could use StreamReader and TextWriter:

void readTextFile(System::String^ filename)
{
using System::IO::StreamReader;
using System::IO::TextWriter;

StreamReader^ sr = gcnew StreamReader(filename);
TextWriter^ tw = System::Console::Out;
tw->WriteLine("File {0}:", filename);

System::String^ line;
for(int i = 0; (line = sr->ReadLine()) != nullptr; ++i)
tw->WriteLine("Line {0}: {1}", i, line);

sr->Close();
}

This post is based on my reading on the book "Expert C++/CLI: .NET for Visual C++ Programmers" by Marcus Heege, APress.

Go to the full post

Command line arguments

It is commonly known that to output the command line arguments for a standard C++ program we could write a piece of code like that:

int main(int argc, char** argv)
{
for (int i = 0; i < argc; ++i)
std::cout << "Argument " << i << ": " << argv[i] << std::endl;

system("pause");
}

Where the first argument is the name of the program itself.

To achieve something similar in C++/CLI we write this:

int main(cli::array^ args)
{
for (int i = 0; i < args->Length; ++i)
System::Console::WriteLine("Argument {0}: {1}", i, args[i]);

system("pause");
}

I wrote "similar" not for the fact that we deal with System::Strings instead of raw arrays of chars - this is just an implementation details, after all - but because the program name is not in the argument lists.

To be a bit more pedantic, the managed version of the main function for a C++/CLI program accepts in input a managed array of managed System::String. The environment puts in this array any argument passed to the program.
This post is based on my reading on the book "Expert C++/CLI: .NET for Visual C++ Programmers" by Marcus Heege, APress.

Go to the full post

Using .NET classes

To use .NET classes that are defined in a specific dll we have to use a Microsoft extension to the preprocessor: the directive using.

The exception is the core library, mscorlib.dll, that requires no using directive to be available to our code. In this way we can use Console, Object, and many other basic stuff without explicit preprocessor using.

In this short example we use the class System::Uri that is defined in the System.dll:

#using

void exp03()
{
System::Uri^ uri = gcnew System::Uri("http://www.heege.net");
System::Console::WriteLine(uri->Host);
}

This post is based on my reading on the book "Expert C++/CLI: .NET for Visual C++ Programmers" by Marcus Heege, APress.

Go to the full post

Managed array

To create a manged array we should just specify the type and the number of dimensions. The default is a dimension of one. The array keyword, not a standard C++ keyword, is defined in the pseudo namespace cli, that could be used to avoid conflicts.

A managed array is initialized like a standard C++ array:

cli::array^ square = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};

cli::array^ square2 = gcnew array(3, 3);
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j)
square2[i, j] = (i * 3) + (j + 1) ;

The dimension of the array is not part of the array type specification, and, as for the other .NET object, once created an array can't change its size. The System::Array::Resize function does not actually resize the passed array, but just create a new one with the new dimensions and then copy the data from the original one.

The initialization of a managed array differs from the one of a standard C++ one in the way that all the element are always initialized to zero and then its default constructor is called, if available.

This post is based on my reading on the book "Expert C++/CLI: .NET for Visual C++ Programmers" by Marcus Heege, APress.

Go to the full post

CTS System::String and System::Text::StringBuilder

Any System::String object is immutable. That means, for instance, that calling String::Concat() does not change the passed object, but return a newly allocated System::String.

Here is a short example that should clarify how System::String works:

System::String^ str1 = System::String::Empty; // 1.
str1 += "a";
str1 = str1 + "a";
str1 = System::String::Concat(str1, "a");
System::Console::WriteLine(str1); // 2.
System::String^ str2 = str1->ToUpper();
System::Console::WriteLine(str2); // 3.

1. overkilling, Concat() is smart enough to manage nullptr System::String in input
2. the three lines before have the same effect, so now we have "aaa" in str1
3. notice that ToUpper() does not change str1, it just creates a new System::String

To avoid the creation of many unncecessary temporary strings is often useful using System::Text::StringBuilder, a sort of mutable string. It coulb be used in this way:

using System::Text::StringBuilder;
StringBuilder^ sb = gcnew StringBuilder(1024);
sb->Append("Rows: \n");
for (int i = 0; i < 100; ++i)
sb->AppendFormat("Row {0}\n", i);
System::String^ str3 = sb->ToString();
System::Console::WriteLine(str3);


This post is based on my reading on the book "Expert C++/CLI: .NET for Visual C++ Programmers" by Marcus Heege, APress.

Go to the full post

Managed Heap

The heap memory management in .NET is based on the concept of garbage collection, this has many advantages but a huge problem: it doesn't match with the standard C++ model.

So, C++/CLI introduces a specific operator, gcnew, to allocate memory on the managed heap. This operator returns something like a pointer to the managed memory, that is called tracking handle to stress the fact that it is not a native C++ pointer, and is denotated by ^ (caret) instead of * (asterisk). Instead of NULL, the invalid value for a tracking handle is nullptr. To access members through a tracking handle is used the same arrow operator used to access members through a standard C++ pointer. Curiously enough, to dereference a tracking handle a * (asterisk) is used.

Here is piece of code that should clarify the matter:

int^ i; // 1.

if(i == nullptr)
std::cout << "i is initialized to nullptr" << std::endl;

i = gcnew int(42); // 2.
if(i != nullptr)
{
System::Console::WriteLine(i->GetType()); // 3.
System::Console::WriteLine(i->ToString()); // 4.

std::cout << "i has value " << *i << std::endl; // 5.
}

1. define a tracking handle, implicitly initialized to nullptr.
2. an int object is created on the managed heap, its tracking handle is returned, and stored in the local variable
3. the tracking handle is dereferenced to call the GetType() method. System.Int32 should be printed, since int is converted to that type on the managed memory.
4. this instruction would print the value of the int object on the managed heap, that means 42.
5. here we print the derefenced value of i, again 42.

This post is based on my reading on the book "Expert C++/CLI: .NET for Visual C++ Programmers" by Marcus Heege, APress.

Go to the full post

CTS and primitive types

There is a hierarchy of classes that should be used to help language interoperability in .NET, it is called CTS (Common Type System) and it is based on the System::Object class. So, anything in CTS is a System::Object.

That means that we can call on any CTS object the System::Object method:
  • ToString;
  • GetType, provides RunTime Type Information (RTTI) in .NET;
  • GetHashCode;
  • Equals.

C++/CLI it is smart enough to use primitive types as managed types, so having a way to work on them as System::Objects.

So this code would output to console System.Int32:

System::Console::WriteLine((42).GetType()); // System.Int32

Here is the mapping the primitive C++ type and the CTS classes that wrap each of them, and some examples of its literal values:

bool System::Boolean (true)
unsigned char System::Byte
signed char System::SByte ('a')
short System::Int16
unsigned short System::UInt16
int System::Int32
unsigned int System::UInt32 (1U)
long long System::Int64 (1LL)
unsigned long long System::UInt64 (1ULL)
float System::Single (1.0f)
double System::Double (1.0)
wchar_t System::Char (L'a')
void System::Void

Even cooler, C++/CLI could use a CTS object as a primitive, when required so, this piece of code works properly:

System::Double PI = 3.14159265358979;
std::cout << PI << std:: endl;

PI is passed to cout as if it were declared as double.

This post is based on my reading on the book "Expert C++/CLI: .NET for Visual C++ Programmers" by Marcus Heege, APress. I suggest you to read it too, to have more details on the matter

Go to the full post

Hello CLI

The code we are talking about here is not proper C++, it is C++/CLI, where CLI stands for Common Language Infrastructure, and that means .NET, so we are basically talking about the C++ twisted by Microsoft to fit in its propertary architecture.

The cool thing about C++/CLI is that it is a superset of the standard C++, so we could, in principle, just avoid CLI and developing (more or less) pure C++ for our Windows platform.

Here is the usual hello program written in a way to show how C++/CLI could use C, C++ and CLI features:

#include <stdio.h>
#include <iostream>

int main()
{
printf("hello"); // C
std::cout << ", "; // C++
System::Console::WriteLine("world"); // CLI

system("pause");
}

Isn't that too bad, is it?

This post is based on my reading on the book "Expert C++/CLI: .NET for Visual C++ Programmers" by Marcus Heege, APress. I suggest you to read it too, to have more details on the matter

Go to the full post