How to create a component for QtEZ
1.1) First of all, I'll assume you have the QtEZ sources,
if so your first task is to:
#include "BaseComp.h"
#include "main.h"
1.2) You will also need to do your includes here for the
component this will be replicating, if you are just doing one that will
not have another widget that it looks like it may not be necesary.
2.1) Then you will create a class which inherits your base
class BaseComponent or some from Qt classes.
All Qt components in qtez
are inherited by real Qt inheritance hierarchy with 'z' prefix (zQWidget,
zQButton, zQLabel.....)
class
YourClass : public BaseComponent
{
.
.
.
};
or from QButton
class YourClass : public zQButton
{
.
.
};
2.2) Now you need to now declare the kind of component you
will be replicating, as a convention I typically call this 'look':
QLabel *look;
2.3.1) Now just the normal constructor, it must accept a
QWidget pointer as it's parameter, and it will additionally pass to the
base initializer your top name:
YourClass::YourClass(QWidget *parent,
const char *name = "component name",
bool createLook = TRUE) : BaseComponent(parent, name, FALSE)
This will result in a series of your components being called label1,
label2, label3, etc. This will be handled by QtEZ.
2.3.2) Instantiate 'look' here, set it to look as you want
it to defaultly. I typically leave this to be the Qt default, so if they
change what it looks like defaultly I won't have to edit to match. Then
finally register your widget with QtEZ:
look = new QLabel(this);
setLook(look);
2.4) You can have a constructor also to delete away strings
and stuff you may use later, but make sure you DO NOT DELETE 'LOOK' IN
YOUR CONSTRUCTOR! It may be used later by QtEZ upon destruction of your
component so leave it there and it will be taken care of by QtEZ.
3.1) Now comes the customization parts, there are several
virtual functions that will be called at different points by QtEZ that
you can impliment to customize your component.
3.2) Almost every component should fill in the following:
-
virtual void fillAttributes();
-
This will be called when the attributes table requests your attributes
you will use this function with the macro ADDATTRIB().
The first argument to ADDATTRIB is the caption (char *).
-
The second argument is the 'current value'. This value will just need to
be used as appropriate (as per the next paragraph):
-
It will be a QColor if you user W_COLOR.
-
It can be a string or an integer if you use W_TEXT or W_LIST.
-
It can be a boolean or integer value if you use W_TRUE.
-
It will be a a string if you use W_FILE.
The third argument can be one of:
-
W_LIST
-
Will give the user multiple choice list of options. If you give this you
will follow it by the number of items in the list, then follow it by that
many _strings_ seperated by columns.
-
W_FILE
-
This will allow the user to select a file, it requires no other parameters.
-
W_TEXT
-
This will allow the user to type in an arbitrary value, it requires no
other paramteters.
-
W_COLOR
-
This will allow the user to select/blend a colour.
-
W_TRUE
-
This will allow the user to select a boolean value.
There is a general function you can call, genericFillAttrib(). Almost
every component should call this, it will include such basic attributes
as width, height, name, etc. It should be a very special case indeed that
your component doesn't call this.
-
virtual void interpretAttributes();
-
This function will be called when it comes time to store your attributes
into your component. You do this by making a call to the series of functions
to the Attribute object, the only argument you must pass is the caption
of the attribute you are asking for.
-
attrib->giveValue(char *caption);
-
You can use this for a string value, if you used type W_FILE, W_TEXT, or
W_LIST. It will return a QString.
-
attrib->giveIntValue(char *caption);
-
You can use this to ask for an integer value that you put into the table
if it's of type W_TEXT, or W_LIST. It will return an int.
-
attrib->giveBoolValue(char *caption);
-
You can use this to get the bool value selected if you put in a W_TRUE.
It will return a bool.
-
attrib->giveColor(char *caption);
-
You can use this if you put a color into the Attribute table with W_COLOR.
It will return a QColor.
There is a general function you can call, genericInterpAttrib().
If you have it's match, genericFillAttrib() called in the fillAttributes,
then you should have this function here to match it.
-
virtual void fillMembInfo();
-
This function is used to fill your component with the base functions it
will have. This will normally just be called on creation, though be prepared
for it to be called at other times. You should at least put a constructor/destructor
pair into the structure. As is the case with inside the QtEZ environment
call the constructor function constructor, and destructor destructor. If
you intend on giving a base init to the constructor, call that function
init, that way the real names will be replaced by QtEZ when the time is
right.
To fill the structure you use the function addMember() it's arguments
are:
-
The function prototype. (char *)
-
The base initializer. (char *)
-
The source code, this will normally just be "". (char *)
-
If the function is virtual, 1 for yes, 0 for no. (int)
-
The protection mode as follows: (M_MODE)
-
M_PRIVATE, is private
-
M_PROTECTED, is protected
-
M_PUBLIC, is public
-
M_SIGNAL, is signal
-
M_PUB_SLOT, is public slot
-
Is the function defined here, or externally, 1 for here, 0 for external
(int)
There is a general function, genericFillMembInfo(), you can use
this as a shortcut to have QtEZ include all the virtual functions provided
by QWidget include closeEvent, mouseMoveEvent, mouseClickEvent, etc.
-
virtual void writeInits();
-
This function is called when it comes time to write the class to file.
The only thing asked of the component is to write out all the functions
necesary to make the widget in the code look like this one, signal connections,
function writing etc will be done by QtEZ.
This is done with a pair of functions:
-
writeInParent(QString toWrite);
-
writeLine(QString toWrite);
Only different between the two is writeInParent() will make this
a call to your object as necesary, ie it will prepend 'yourClassInstance->'
to toWrite as necesary, if this should never happen then use writeLine(),
most should be writeInParent() though. It should also be noted you should
put the ending semi-colon and \n onto the the line as you want it to look
in the final output, do not worry about putting tabs before your lines
though this will be handled by QtEZ.
It is often the case with writing these that the person hasn't changed
the attribute in question to cause this line to be written, for example
if they don't change the caption from it's default, why should you write
out 'setCaption("the new caption");\n'? This being the case there is a
facility for you, you will want to check if isWritten("The Attribute Caption
in Question") == 1. for example:
if(isWritten("Caption"))
{
i.sprintf("setCaption(\"%s\");\n",look->caption());
writeInparent(i);
}
This should allow you to write only the necesary calls, and keep the generated
source code to a minimum, again this isn't necesary but it will speed up
compilation on your component and in the long run safe you and users of
the component time, so it is a Good Thing (tm). There is a macro to help
this a little it is:
i.sprintf("setCaption(\"%s\");\n",look->caption());
WRITEATTRIB("Caption",i);
This is approximatly the same thing, and will make it a little easier on
you, though if you have multiple attributes you depend on, for example
width and height, before writing the setGeometry() call, then you will
need to use the first construct.
-
virtual char *queryQType();
-
This is used to 'show' what type of component this is, for example if the
component you are creating is a QWidget, you will want to return "QWidget"
there, that will result in that type being shown in QtEZ environment, it
will also be used as the class type if the specialInher()
function isn't defined. The main reason for this function is for a consistant
look, for example if you are implimenting a component that is either a
QMenuBar or a KMenubar depending on what kind of application the user has
selected it is recommended you return one string in the queryQType(), something
like "Menubar", and then return either QMenubar, or KMenuBar in specialInher()
as required, this allows for a more consistant look in QtEZ.
3.3) These functions are more auxiliary then anything. None
of them are required by anything, and if left undefined will fall back
to default. They are as follows:
-
virtual int canResize();
-
This will say if the widget can be resized after creation by the user.
Return 1 if it can resize.
Return 0 if it cannot.
Default is 1.
-
virtual int copyable();
-
This will say if the widget can be copied, and more importantly pasted
back onto the dialog, this will mainly be used if you only want ONE of
these components on the dialog.
Return 1 if it is allowed to be copied.
Return 0 if it is not.
Default is 1.
-
virtual char *specialInher();
-
This will override queryQType() as the classtype for your class type, the
main purpose is described above in the queryQType()
explanation.
-
virtual int invisableComponent();
-
This will determine if the component is invisable, this is so it will show
up in QtEZ's dialogs, but will not be visible as a dialog, or a widget,
for example a Application component.
Return 1 if the component is to be invisable
Return 0 if it is not.
Default is 0.
-
virtual int canHaveChildren();
-
You can use this to say if your component is allowed to have children.
Return 1 if it can have children.
Return 0 if it cannot.
Default is 1.
-
virtual int externalLinkage();
-
You can use this if upon compilation the component is going to require
another library to work, this will mainly be provided by the means of a
dynamic component that is not a QWidget, dynamic component creation will
follow as an appendix.
Return 1 if it does not require anything extra.
Return 0 if it just needs Qt.
Default is 0.
-
virtual int instantiate();
-
Use this if you do not want your component instantiated, this will also
mean that writeInits() will not be called.
Return 1 if it is to be instantiated.
Return 0 if it is not.
Default is 1.
-
virtual char *specialInit();
-
Use this if you do not want the default constructor to be used as '(this,name)'
for example the QComboBox I ask to use the '(QWidget *, char *, bool)'
constructor.
Return 0 if it is just to use '(this,name)'.
Return a string that will be used during instantiation otherwise.
Default is 0.
-
virtual QString parseInitSpecial(QString p);
-
This function can be a little esoteric, it's basic use is to pase out special
variables in the base initializer, for example if you want to pass something
that will be user defined (an attribute) to your parent in the baseinit,
you can set aside a variable, let's say '$USER_VAR', if the programmer
had a basinit that looked like 'init(parent,name,$USER_VAR)' you would
be passed that and you would change $USER_VAR to whatever will be appropriate
before the actually function call is written to the .cpp file.
Return p if you don't have any variables defined.
Return a string with your variables replace in p if you need to.
Default is to just return p.
-
virtual char *aboutComp();
-
This will allow you to put your name on the 'about' screen of the component
if you so choose, you can put the original widget developer's name as well.
Return a string you want to go into the about box if you wish.
Return 0 if you want no about screen.
Default is to have a QtEZ defined about screen.
That should be all there is to creating a component, there
will be an appendix at the bottom that will illustrate
how to generate a dynamic component, in that it will be included at runtime,
rather than in the QtEZ binary. However I would like it if people will
send in components created dynamicly to me so I can perhaps make a listing
available, and more importantly include in the standard QtEZ distribution
if it seems to be a widely used one.
APPENDIX: DYNAMIC COMPONENTS
On ELF and AOUT systems it is possible to include components
dynamically, this is done through a wrapper function, which I suggest wrapping
in an extern "C" as:
extern "C"
{
YourClass *yourself(QWidget *parent);
}
This is the prototype for your function, you will need to replace YourClass
with whatever you call the class definition for example 'zDialog'. The
implimentation should look like:
extern "C"
{
YourClass *yourself(QWidget *parent)
{
return (new YourClass(parent));
}
}
I recomend you compile your -fpic and -shared and make sure you link in
everything of yours you need, it shouldn't be necesary to link against
Qt, or anything in QtEZ. Current convention is to name your shared object
with the extention .qtl, the end user will put a configuration of:
Descriptive String;/path/to/yourclass.qtl
If you turn externalLinkage() on in your
class, make sure you have a /path/to/yourclass.a that will be an 'ar' of
the external classes you had, this will be linked to upon compiling the
final program the user makes. You may also put a /path/to/yourclass.img
this can be any image that a QImage will read.
This should be all required to make a component dynamic.