Showing posts with label python3. Show all posts
Showing posts with label python3. Show all posts

Tuesday, 11 March 2014

Excellent QT Drag and Drop Tutorial

This link is what got me started in understanding how QT works with drag and drop. I will post a link to the file in my github account that will use the results of this tutorial (although it is subject to change over time), but with code samples in-page.

http://rowinggolfer.blogspot.ca/2010/04/pyqt4-modelview-drag-drop-example.html

Here is my github source file with drag and drop classes in them:
https://github.com/NucleaPeon/QTCIDE/blob/master/src/view/main/widget.py

Note the following sections:

class Droppable(QtGui.QLabel):
    
    def __init__(self):
        super(Droppable, self).__init__()
        self.mime = QtCore.QMimeData()
        # ... more

    def mousePressEvent(self, event):
        mime = QtCore.QMimeData()
        mime.setText(self.mime.text())
        hotSpot = event.pos()
        mime.setData("application/x-hotspot", str(hotSpot.x()))
        
        # Create a pixmap of size of self
        pixmap = QtGui.QPixmap(self.size())
        self.render(pixmap)
        drag = QtGui.QDrag(self)
        drag.setMimeData(mime)
        drag.setPixmap(pixmap)
        drag.setHotSpot(hotSpot)
        
        dropAction = drag.exec_(QtCore.Qt.CopyAction|QtCore.Qt.MoveAction, QtCore.Qt.CopyAction)
Important things to note is that droppable object is a QtGui object, therefore has a visual place in the UI, has methods such as mousePressEvent in them.

Another odd thing you may notice, is that __init__ has a variable named self.mime, whereas mousePressEvent has just mime. Through trial and error, I found that the mousePressEvent method requires an instantiation *each* time the mouse is pressed, otherwise it fails to work. I set a variable in __init__ so that I only have one copy of the data and don't need to keep setting it (hardcoding == bad) in the method itself; changing the constructor variable will change the behaviour of the entire class easily.

class DropCanvas(QtGui.QGraphicsView):
    
    def __init__(self):
        super(DropCanvas, self).__init__()
        self.setAcceptDrops(True)
        self.scene = model.scene.ProjectScene()
        
    def dropEvent(self, event):
        # Here is where you put actions you want when the drop is done
        
        
    def dragEnterEvent(self, event):
        event.accept()

This class is the QtGui droppable object. I have it set to QGraphicsView, but it can be anything, such as a different view or a dock or panel, provided it inherits QWidget (I think).

Personally, I have an object that is set up specifically upon a drop event. The code here won't work with a QGraphicsView, but it used to be a QListView so it did work.

class CanvasItem(QtGui.QStandardItem):
    
    def __init__(self, *args, **kwargs):
        super(CanvasItem, self).__init__(*args)
        self.setEditable(False)


The item inherits a standard item and disables editing of itself.


Put all of the classes together and you can drag and drop!

Thursday, 3 October 2013

Including Dbus in a Python QT4 application

Dbus isn't the most complicated beast in software, but it's notable.

There are a list of things I *don't* like about dbus.

- The C/C++ code is well documented, but good tutorials are absent
- The Python code is poorly documented if it exists, but there are some great tutorials to look off of
- Python Dbus + Qt or GTK requires that the process is forked or put into a daemon in order to allow connections. (Cannot initiate in a GUI application without detachment)

I just spent an hour figuring out that last one. I'm big on documentation, both in my work and when using other people's code. If it isn't there, the difficulties in developing with the software is exponentially greater and my time is wasted.

That said, dbus is amazing when it works and I love that KDE and QT/Maemo have taken great lengths to adopt it. Even the KDEOnWindows project uses dbus, on Windows!



I am trying to create a dbus interface so that my GUI application can talk to different portions of itself without messy imports and passing of variables. I will have a dbus/ folder that contains one module with one class that inherits dbus services and imports respective modules. Each method will pass the call and any arguments it includes to the function, which in turn passes it to the UI.

Why have dbus classes and modules in one folder? So I can iterate through the folder and add every dbus service without having to manually type it all in; automate it! (It's inspired by the debian and gentoo directory.d/ style of configuration management. I plan to extend this to a plugin system at some point.) Make sure all dbus files are referenced before the mainloop but after application is started.

It seems that walking through a directory and dynamically loading dbus objects still causes dbus to hang. My guess is that every dbus.service object needs to be placed into its own mainloop and importing a module with dbus methods doesn't properly place it into the mainloop.

Sorry for the mess. Dbus services must be added after the QApplication or QCoreApplication is initialized. I was adding them before, then getting this message in the terminal:

QSocketNotifier: Can only be used with threads started with QThread

Create references to dbus objects after application initialization!

For a real-world example, I will launch a dbus service that contains a method for creating a new project. I will name it NewProject and it will have an argument of a name. We will not worry about uniqueness or purpose for now.

------------------------------------------------------------

Import Code:

import dbus
import dbus.service
from dbus.mainloop.qt import DBusQtMainLoop

If this fails, you need to install dbus dependencies. This could be python3-dbus.mainloop.qt, python3-dbus/python-dbus, or python3-pyqt4. You will need to check with your distribution for the proper package names. A search for "dbus" is usually adequate.

Define an Interface Name:

This should reflect your application or purpose. I usually store it as a global variable.

INTERFACE = 'org.qtcide'

Write your class with the dbus methods
class DbusTest(dbus.service.Object):

    def __init__(self):
        busName = dbus.service.BusName(INTERFACE, bus = dbus.SessionBus())
        dbus.service.Object.__init__(self, busName, '/org/qtcide')
        

    @dbus.service.method(INTERFACE,
                        in_signature = 's', out_signature = 's')
    def NewProject(self, name):
        return name

Initialize application and mainloop:
DBusQtMainLoop(set_as_default = True)    
app = QtGui.QApplication(sys.argv)

Keep it looping:
sys.exit(app.exec_())


And you should have a dbus process running. Currently the NewProject method only returns the name you give it, but adding an import and calling a method with that Name is simple, and I shall explain how in the next post.

Forking may have some strange consequences on Windows, as true forking is resource intensive and not native.


For those of you interested, you can find my project I'm basing this tutorial on here: https://github.com/NucleaPeon/QTCIDE

Friday, 13 September 2013

How to install your own python modules

Problem: You have python code, such as a library, that you want available for other programs you, or others, may be developing. You also may want to add your code into a debian or rpm package for easy distribution.


Solution: Add a [name].pth file and your package to /usr/local/lib/pythonX.Y/dist-packages.


Important Notes:
  • site-packages is no longer used. Read the site.py module for more information on this.
  • The [name].pth contains a relative or absolute path to the directory or specific python files you want added to the interpreter, each item on its own line. (Comments prefixed with # are also recognized)

So I have this example:

/usr/local/lib/python3.2/dist-packages/mylib.pth
    # This file contains path to my library (relative):
    # Adds every .py file in mylib/ folder
    mylib/
    mylib/static/mylibvars.py



The path /usr/local/lib/python3.2/dist-packages/mylib/ contains library files and /usr/local/lib/python3.2/dist-packages/mylib/static/mylibvars.py is just to show how to specify an individual file.


Once you open up your interpreter, type in the following commands to check your work.


>>> import sys
>>> print(sys.path)

Friday, 2 August 2013

Quickly changing python version in gentoo from 2.x to 3.x series

On my gentoo laptop I am upgrading my python from version 2.7 to 3.2 to aid in some development at my job.

Since it's been set up with 2.7, I cannot utilize some important libraries such as python-sqlite because they are not built for the 3.2 target.

Without going into the more complex explanation on why it works, these are the steps to configure your system to utilize python 3.x.

/etc/portage/make.conf:
USE_PYTHON="3.2 3.3 2.7"
PYTHON_TARGETS="python3_2 python3_3"
PYTHON_SINGLE_TARGET="python3_2"
Simple Explanation:

USE_PYTHON tells emerge the list of desired python versions.

PYTHON_TARGETS tells emerge what targets you prefer to build for. Only if a package cannot build for these targets, yet has a version in USE_PYTHON, will it build in a different version -- I *think*.

PYTHON_SINGLE_TARGET tells emerge that if a package can be built for only one target, use that target. My install of Blender requires the single target point to python3_3.

Be sure to set the python version with eselect.

eselect python set [number]

Next step is to update things by calling python-update, no parameters.
Once your eselect python show --ABI shows the 3.x version you specify in PYTHON_SINGLE_TARGET, you can start rebuilding your system.

This process may take a while. All packages that cannot be built in the 3 series must have the python version specified in /etc/portage/package.use. An example of how to do this is so:

echo "[group]/[package] python_targets_python2_[x] python_single_target_python2_[x]" >> /etc/portage/package.use

Do this until you can get through emerge --deep --update --newuse world -av without dependency issues.

Once you complete the emerge, run revdep-rebuild to make sure your packages are in order.