GauiStori / PyQt-Qwt

Python PyQt wrapper for Qwt6
Other
54 stars 18 forks source link

Accessing the QwtPlotCanvas of a QwtPlot via the canvas() getter returns a QFrame object #4

Closed otonck closed 5 years ago

otonck commented 5 years ago

Hi, and thanks for this useful project.

We experience a problem to access the setFocusIndicator method of a QwtPlot's canvas. The object returned from canvas() is a QFrame even though the underlying C++ object is a QwtPlotCanvas.

It seems that the sipConvertFromType method does not manage to convert a QObject to a QwtPlotCanvas one, but stops at the QFrame subclass (may be because this is the "border" between PyQt classes and PyQt-Qwt classes in the inheritance path ?).

here is a simple script to reproduce :

from PyQt5.Qwt import QwtPlot, QwtPlotCanvas

from PyQt5.QtWidgets import QApplication

class BasePlot(QwtPlot):

    def __init__(self, parent=None):
        super(BasePlot, self).__init__(parent)
        cv = self.canvas()
        cv.setFocusIndicator(QwtPlotCanvas.ItemFocusIndicator)

app = QApplication([])
myPlot = BasePlot()

an exception is thrown :

Traceback (most recent call last): File "C:/Dvp/Projets/tmp/minqwt/test.py", line 14, in myPlot = BasePlot() File "C:/Dvp/Projets/tmp/minqwt/test.py", line 10, in init cv.setFocusIndicator(QwtPlotCanvas.ItemFocusIndicator) AttributeError: 'QFrame' object has no attribute 'setFocusIndicator'

If we create a QwtPlotCanvas like this, there is no problem, though :

from PyQt5.Qwt import QwtPlot, QwtPlotCanvas

from PyQt5.QtWidgets import QApplication

class BasePlot(QwtPlot):

    def __init__(self, parent=None):
        super(BasePlot, self).__init__(parent)
        cv = QwtPlotCanvas()
        cv.setFocusIndicator(QwtPlotCanvas.ItemFocusIndicator)
        self.setCanvas(cv)

app = QApplication([])
myPlot = BasePlot()

It may be more of a SIP question, but is there a way to tell SIP to try and convert the canvas QWidget to a QwtPlotCanvcas if needed ?

Best regards

GauiStori commented 5 years ago

Hi

On Tuesday, 27 November 2018 17:54:43 CET otonck wrote:

Hi, and thanks for this useful project. Glad you like it.

It may be more of a SIP question, but is there a way to tell SIP to try and convert the canvas QWidget to a QwtPlotCanvcas if needed ? To answer the first question.

canvas is a member of QwtPlot class QWT_EXPORT QwtPlot: public QFrame, public QwtPlotDict {... QWidget *canvas(); } and it doesn't have any setFocusIndicator function like QwtPlotCanvas has.

I don't know if you can convert canvas QWidget to a QwtPlotCanvcas. In what case do you need to do that?

Please take a look at the C++ Qwt examples, then I recommend the Qwt mailing list. If it can be implemented in C++ we can discuss a Python implementation but then I may need some help :)

Regards Gudjon

otonck commented 5 years ago

Thank you for the answer.

The canvas member is indeed a QWidget, but it is instanciated as a QwtPlotCanvas, see line 173 of qwt_plot.cpp :

// canvas
d_data->canvas = new QwtPlotCanvas( this );

Since PyQt-Qwt includes a SIP wrapper for class QwtPlotCanvas, I would expect the canvas() accessor to return an python object typed with the lowest class in the class hierarchy (that is QwtPlotCanvas, unless a canvas with another type is passed to the plot with setCanvas), but that is not the case : we get an object with the intermediate class QFrame instead, preventing us to use any of the useful methods of the QwtPlotCanvas class, unless using some trick like mentionned in the end of the first post (instanciating the QwtplotCanvas python' side and pass it to setCanvas()).

FYI, I would need this to replace PythonQwt by PyQt-Qwt for performance reasons in one of my projects, in which we make calls to setFocusIndicator().

GauiStori commented 5 years ago

Hi again

On Monday, 3 December 2018 09:56:28 CET otonck wrote:

Thanks you for the answer.

The canvas member is indeed a QWidget, but it is instanciated as a QwtPlotCanvas, see line 173 of qwt_plot.cpp :

// canvas
d_data->canvas = new QwtPlotCanvas( this );

Since PyQt-Qwt includes a SIP wrapper for class QwtPlotCanvas, I would expect the canvas() accessor to return an python object typed with the lowest class in the class hierarchy (that is QwtPlotCanvas, unless a canvas with another type is passed to the plot with setCanvas), but that is not the case : we get an object with the intermediate class QFrame instead, preventing us to use any of the useful methods of the QwtPlotCanvas class, unless using some trick like mentionned in the end of the first post (instanciating the QwtplotCanvas python' side and pass it to setCanvas()).

FYI, I would need this to replace PythonQwt by PyQt-Qwt for performance reasons in one of my projects, in which we make calls to setFocusIndicator().

You are right.

I found the solution in C++

BasePlot::BasePlot(QwtPlot parent) : QwtPlot(parent) { QwtPlotCanvas cv = qobject_cast<QwtPlotCanvas *>( canvas() ); cv->setFocusIndicator(QwtPlotCanvas::ItemFocusIndicator); }

So I just need to find out how to change this into SIP code. I have found some examples and will try.

Regards Gudjon

GauiStori commented 5 years ago

Hi again

Please check the current Git version. I think your issue is fixed.

/Gudjon

otonck commented 5 years ago

Hi again Please check the current Git version. I think your issue is fixed. /Gudjon

Hi Gudjon,

tested it this morning, working like a charm ! Thank you so much,

Best regards, Olivier