Category Archives: Qt

PyQt Draggable Tree

This is a minimal test about how to do drap-and-drop within a QTreeWidget.

First let the tree setAcceptDrops. Then declare mouseMoveEvent, dropEvent, dragEnterEvent, and let dragMoveEvent = dragEnterEvent. The rest is to set mimeData, by which the information can be transferred between those events. Here only a string, name of the item, will be sent.

Click LMB on an item, drag-and-drop onto another, a new item will be created and attached to the latter. The drop event must hit an item.

 

Leave a comment

Filed under Python, Qt

Module And Resources

https://github.com/spinos/aphid/blob/master/pyqtmodule/iconLabel.py

Suppose you have a file named iconLable.py like above, and someone want to use it as a module in his/her program, like:

https://github.com/spinos/aphid/blob/master/pyqtmodule/testIconLabel.py

The problem is the above file only works when started from the same folder where iconLabel.py is saved. The module file should be put away into sys.path, so Python can find and import the module no matter where the program is started. Typically module files go to C:\Python26\Lib\site-packages

To test if it works, lauch Python command line, try:

import iconLabel

Another problem is those icon images, circle and square, are lost. Relative file path like:

image= QtGui.QPixmap('./circle.png')

only works when the file is started from the same path where the images are saved. Using absolute path:

image = QtGui.QPixmap('D:/somewhere/circle.png')

still cannot work because everyone to use the module should have a copy of those images in the same path.

Resources will be the solution. Save a .qrc file, iconLabel.qrc:

<!DOCTYPE RCC><RCC version="1.0">
 <qresource>
 <file>circle.png</file>
 <file>square.png</file>
 </qresource>
 </RCC>

Run command:

pyrcc4 iconLabel.qrc -o iconLabel_rc.py

Will create a module file, iconLabel_ry.py, contains those images. Inside iconLabel.py:

import iconLabel_rc

and change all file path to start with ‘:’, i.e.

desc.image_path = ':circle.png'

Run the module file again, Python will create binary code for both iconLabel and iconLabel_rc. Copy both .pyc files to sys.path, and this module is ready to go. You can remove iconLabel.py from sys.path, just use the .pyc files. This will be helpful in case you don’t want to give up your source code.

For more details about Qt resources, go here.

Leave a comment

Filed under Python, Qt

Hybriding Qt with PyQt

A hybrid Qt and PyQt application sounds attractive: within a Qt application, parts for duty calculation are written in C++ , and UI is written in PyQt, making the application scriptable and expandable. To create such a program, we need to know how to do two sets of things: one is to embed PyQt into a C++ application, the other is to send information from PyQt back to C++. Above is an example. First line is in C++, and second is in PyQt. When the dial or slider is changed, numbers related field will be updated.

So how does it work?

Let’s start with the central widget of the main window.

Center::Center()
{
    setObjectName("center");
    QVBoxLayout* box = new QVBoxLayout();
    LeftWidget *leftDial = new LeftWidget();
    box->addWidget(leftDial);
    ShakeHole *rightDial = new ShakeHole();
    box->addWidget(rightDial);
    setLayout(box);
}

It has two widgets: a LeftWidget and a ShakeHole. (Sorry for the confusing names.)

First, the ShakeHole:

class ShakeHole : public QWidget
{
public:
    ShakeHole();
    ~ShakeHole() {}   
};
ShakeHole::ShakeHole()
{

    int wnd = winId();
        cout<<"winid "<<wnd<<endl;

    cout<<"version "<<Py_GetVersion()<<endl;

    FILE *fin = fopen("/Users/jianzhang/aphid/hybrid_qtpyqt/foo.py","r+");
    PyRun_SimpleFile(fin,"foo");

        PyObject *mainDict = PyModule_GetDict(PyImport_Import(PyString_FromString("__main__")));

        PyObject *claus = PyDict_GetItemString(mainDict, "Ui_Form");

        PyObject* snakewig = Py_BuildValue("i", wnd);

        PyObject * pTuple = PyTuple_New (1);
        PyTuple_SetItem (pTuple, 0, snakewig);

        PyObject *clausInstance = PyObject_CallObject(claus, pTuple);

        cout<<"finished python ui!\n";

        PyErr_Print();

}

It is derived from QWidget, and hooks a PyQt widget within via Python C API.

Now LeftWidget:

class LeftWidget : public HWidget
{
public:
    LeftWidget();
    void setAttribute(const char *attributeName, int value);
private:
    QLineEdit *attributeEdit;
    QLineEdit *attribute1Edit;
};

LeftWidget::LeftWidget()
{
    setObjectName("SnakeHole");
    QHBoxLayout* box = new QHBoxLayout();
    box->addWidget(new QLabel("qt host"));
    attributeEdit = new QLineEdit();
    box->addWidget(attributeEdit);
    attribute1Edit = new QLineEdit();
    box->addWidget(attribute1Edit);
    setLayout(box);

}

void LeftWidget::setAttribute(const char *attributeName, int value)
{
    QString attr(attributeName);
    if(attr == "fieldNumber")
    {
        QString str = QString("%1")
             .arg(value);
        attributeEdit->setText(str);
    }
    else if(attr == "sliderNumber")
    {
        QString str = QString("%1")
             .arg(value);
        attribute1Edit->setText(str);
    }
}

It is a derived HWidget, and it has two QLineEdit, which will be updated by setAttribute(). OK, so what is HWidget?

class HWidget : public QWidget
{
public:
    HWidget();
    virtual ~HWidget();
    virtual void setAttribute(const char *attributeName, int value);
};
void HWidget::setAttribute(const char *attributeName, int value)
{
    qDebug()<<"beep";
}

It is a derived QWidget as well, and it has a virtual function setAttribute() to override.

Go to the PyQt side:

from PyQt4 import QtCore, QtGui
import hoatzin

class Ui_Form(QtGui.QWidget):
    def __init__(self, parentId):      
        Form = QtGui.QWidget.find(parentId)
        super(Ui_Form, self).__init__(Form)
        self.resize(400, 100)
        self.host = hoatzin.Hoatzin()
        self.boxLayout = QtGui.QHBoxLayout()
        self.boxLayout.setObjectName('ui_form_box')
        self.boxLayout.setObjectName("boxLayout")
        self.boxLayout.addWidget(QtGui.QLabel('pyqt widget'))
        self.dial = QtGui.QDial()
        self.boxLayout.addWidget(self.dial)

        self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
        self.boxLayout.addWidget(self.slider)

        self.setLayout(self.boxLayout)

        self.dial.valueChanged.connect(self.dialTest)
        self.slider.valueChanged.connect(self.sliderTest)

    def dialTest(self):
        w = self.host.set_attribute_int('SnakeHole', 'fieldNumber', self.dial.value() )
        if w != 1:
            print 'SnakeHole not found'

    def sliderTest(self):
        w = self.host.set_attribute_int('SnakeHole', 'sliderNumber', self.slider.value() )
        if w != 1:
            print 'SnakeHole not found'

OK, it is a QWidget and will be attached to a specific parent. It has a dial and a slider, each of them will trigger a function by the signal of valueChanged. Wait a second! What on earth is hoatzin?

It is a Python extension to send information back to the host C++ program:

class Hoatzin
{
public:
    Hoatzin();

    int set_attribute_int(const char *widgetName, const char *attributeName, int value);

private:

};
int Hoatzin::set_attribute_int(const char *widgetName, const char *attributeName, int value)
{
    foreach (QWidget *widget, QApplication::allWidgets())
    {
            if(widget->objectName() == QString(widgetName))
            {
                HWidget *h = (HWidget *)widget;
                h->setAttribute(attributeName, value);
                return 1;        
            }          
    }
    return 0;
}

In set_attribute_int() function, it will search the specific QWidget by objectName(). If found, cast to HWidget, and call its setAttribute(). That is why the target widget should have a specific object name. When it calls “SnakeHole” to setAttribute(), the derived LeftWidget will be called.

Now you know all the tricks necessary for this stunt. The rest is how to compile and test.

First you will need file in https://github.com/spinos/aphid/tree/master/hoatzin

Follow the readme to build the Python module of hoatzin.

Second you need files in https://github.com/spinos/aphid/tree/master/hybrid_qtpyqt

Qmake will create the project for you. It links the compiled library of hoatzin, so no need to compile the source of HWidget again.

Just a confusing example, may be helpful.

Leave a comment

Filed under C++, Python, Qt

PyQt MenuBar Outside MainWindow

import sys, os
from PyQt4 import QtCore, QtGui
class AddressBook(QtGui.QWidget):
    def __init__(self, parent=None):
        super(AddressBook, self).__init__(parent)
        self.create_actions()
        mainLayout = QtGui.QVBoxLayout()
        toolBar = QtGui.QToolBar()
        simpleButton = QtGui.QToolButton()
        simpleButton.setDefaultAction(self.simpleAct)
        fileButton = QtGui.QToolButton()
        fileButton.setText('File')
        fileButton.setPopupMode(QtGui.QToolButton.MenuButtonPopup)
        fileButton.setMenu(self.pop_file_menu())
        editButton = QtGui.QToolButton()
        editButton.setText('Edit')
        editButton.setPopupMode(QtGui.QToolButton.MenuButtonPopup)
        editButton.setMenu(self.pop_edit_menu())
        toolBar.addWidget(simpleButton)
        toolBar.addWidget(fileButton)
        toolBar.addWidget(editButton)
        mainLayout.addWidget(toolBar)
        mainLayout.addWidget(QtGui.QGroupBox())
        self.setLayout(mainLayout)
        self.setWindowTitle("Menu By Tool Bar")
    def pop_file_menu(self):
        aMenu = QtGui.QMenu(self)
        aMenu.addAction(self.fileOpenAct)
        aMenu.addAction(self.fileCloseAct)
        return aMenu
    def pop_edit_menu(self):
        aMenu = QtGui.QMenu(self)
        aMenu.addAction(self.editCopyAct)
        aMenu.addAction(self.filePasteAct)
        return aMenu
    def create_actions(self):
        self.simpleAct = QtGui.QAction('Simple', self, triggered=self.do_nothing)
        self.fileOpenAct = QtGui.QAction('Open', self, triggered=self.do_nothing)
        self.fileCloseAct = QtGui.QAction('Close', self, triggered=self.do_nothing)
        self.editCopyAct = QtGui.QAction('Copy', self, triggered=self.do_nothing)
        self.filePasteAct = QtGui.QAction('Paste', self, triggered=self.do_nothing)
    def do_nothing(self):
        print 'do nothing'
if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    addressBook = AddressBook()
    addressBook.setGeometry(100, 100, 640, 480)
    addressBook.show()
    sys.exit(app.exec_())

How to add a QMenuBar to a QWidget instead of QMainWindow? I have no idea. But I really need a menu bar floating above all those small windows! Fortunately, QToolBar comes to save the day. A QToolButton can have a nice popup menu as well. This is a simple example.

Leave a comment

Filed under Python, Qt

QImage Basic

QImage is the Qt class for image I/O. It conveniently supports a few usual image formats. The following is about some basic usage of QImage.

First include the header:

#include <QtGui/QImage.h>

To compile it on Mac, better use -framework flag like:

g++ -o test main.cpp -framework QtCore -framework QtGui

QtCore.framework and QtGui.framework should be found in /Library/Frameworks/

To load an image:

QImage in_img( “foo.tif” );

If it failed to open the image, result will be null:
if(in_img.isNull())
{
cout<<“failed to load image, do nothing”<<endl;
return 0;
}
A few properties about the image:

cout<<”  size: “<<in_img.width()<<” x “<<in_img.height()<<endl;
cout<<”  depth: “<<in_img.depth()<<” bits-per-pixel”<<endl;

Any 8-bit Tiff image with alpha channel should return format as ARGB32:
if(in_img.format() == QImage::Format_ARGB32)
cout<<”  format: ARGB32″<<endl;

if(in_img.hasAlphaChannel())
cout<<”  alpha channel: yes”<<endl;
else
cout<<”  alpha channel: no”<<endl;

Get the pixels from the image:

const uchar *imgbits = in_img.bits();

Because it is unsigned char, I think QImage only supports 8-bit images.

Create a RGB image from pixels:

uchar* image_data = new uchar[img_w*img_h*3];

// set the pixel colors

QImage out_img(image_data, img_w, img_h, QImage::Format_RGB888);

And save it in Jpeg:

out_img.save(“foo.jpg”);

There should be enough bits in correct order. For RGB888 format, 3 char for each pixel as RED|GREEN|BLUE. For ARGB32, 4 char as BLUE|GREEN|RED|ALPHA.

Leave a comment

Filed under Qt