PDA

View Full Version : Python / PyQt5 MDI Window focus problem



apereira
14th September 2015, 05:11
I'm making my firsts steps into python/pyqt5 world, and trying to develop an application. The app has an MDI approach, and the main functionality is already accomplished and seems to work fine (up to now).

A subwindow that contains a database table (T1) is opened at the very beginning of the app, from this window another subwindow that contains a dialog (D1) can be opened to enter record data to this table.
After this, i added a development almost similar in behaviour as the described above (a window that contains a table (T2) from which a dialog is opened (D2) to enter record data).

The point is that the window that containts T2 and the window that contains D2 seems to have focus at the same time.
At this point, if D2 is closed the window D2 AND T2 are closed.

If T1 is clicked (taking focus away of D2 or T2) and then D2 is clicked again focus is taken by D2 correctly, and if D2 is closed ... it closes as expected.

I will post some code for your guidance.


class MainWindow(QMainWindow):

def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)

self.setObjectName("MainWindow")

self.centralwidget = QWidget(self)
self.centralwidget.setObjectName("centralwidget")

self.mdiArea = QMdiArea(self.centralwidget)
self.mdiArea.setObjectName("mdiArea")
self.mdiArea.setActivationOrder(QMdiArea.Activatio nHistoryOrder)

self.setCentralWidget(self.mdiArea)

globvar.mdiArea = self.mdiArea

globvar is a module that contains global variables (mdiarea, winlist). winlist is an instance of the class WindowList that let us control not to open twice the same window. This class has two methods, add and remove both return a boolean reporting the success status.

Here is the call of the T1 -> ImpABM made from the MainWindow class.


def ImpABM(self):
if globvar.winList.add('MdiIMP'):
child = modimpo.MdiIMP(self)
globvar.mdiArea.addSubWindow(child)
child.show()

And the call of the T2 --> ProvABM from the MainWindow class



def CliABM(self):
if globvar.winList.add('ABMfmcli'):
tabla = modabm.tabla( "fmcli", "Clientes")
tabla.addField(modabm.campo("cliid", "N", 6, "Código", "999999"))
tabla.addField(modabm.campo("clidesc", "C", 60, "Descripción" ))
tabla.isSequence("cliid", True)
tabla.orden = "clidesc"

self.cli = modabm.abmMaestros( self, tabla )
globvar.mdiArea.addSubWindow( self.cli )
self.cli.setPos(20, 20)
self.cli.setSize(500, 200)
self.cli.cargaDatos()
self.cli.show()


In module file modimpo, the class MdiIMP is defined as follow


class MdiImp(QDialog):

def __init__(self, parent=None):
super(MdiImp, self).__init__(parent)
self.setAttribute(Qt.WA_DeleteOnClose)
self.objectName = "MdiImp"
...

def closeEvent(self, event):
globvar.winList.remove( 'MdiImp' )

In the class MdiIMP a call to D1 -> dialgImpo is made.


def ImpoAlta(self):
if globvar.winList.add( 'dialgImpo' ):
d = dialgImpo( self, "A" )
globvar.mdiArea.addSubWindow( d )
d.updTabla.connect( self.LoadTabla )
d.show()

The dialgImpo class is as follow :


class dialgImpo(QDialog):

updTabla = pyqtSignal()

def __init__(self, parent=None, accion="" ):
super(dialgImpo, self).__init__(parent)
...
self.cancela = QtGui.QPushButton(self)
self.cancela.setObjectName("cancela")
self.cancela.clicked.connect( globvar.mdiArea.closeActiveSubWindow )
...

def closeEvent( self, event ):
globvar.winList.remove( 'dialgImpo' )
globvar.mdiArea.closeActiveSubWindow()

Up to now, seems to work fine.

Having in mind the idea of creating a master file maintenance class, i've developed the modabm module that contains the tabla (table) class that has a list of instances of campo (field) class, also defined in this module. The tabla class creates a table view of a master file based on a list of fields, as seen above in the ProvABM method.


class tabla(QDialog):

lista = ()

def __init__(self, parent=None, nom="", desc=""):
super(tabla, self).__init__(parent)
self.setAttribute(Qt.WA_DeleteOnClose)
self.nombre = nom
...

def closeEvent(self, event):
globvar.winList.remove('ABM' + self.nombre)

This window closes properly (till here everything is fine).
In the tabla class there is a method that creates a dialog based on the field definition to enter the record data.


def AltaRec(self):
if globvar.winList.add('dialgABM' + self.nombre):
debug("-- Dialogo ABM AltaRec " + self.nombre)
capturafm = dialgABM(self, ABM_ALTA, self.desc, self.campos, self.dialgx, self.dialgy, self.nombre)
globvar.mdiArea.addSubWindow(capturafm)
capturafm.show()

Here is the definition of D2 -> dialgABM class (the one that is creating problems).


class dialgABM(QtGui.QDialog):

commitRec = QtCore.Signal()

def __init__(self, parent=None, accion='', titulo='', campos=[], x=0, y=0, nombre=''):
super(dialgABM, self).__init__(parent)
...
self.cancela.clicked.connect(globvar.mdiArea.close ActiveSubWindow)
...

def closeEvent(self, event):
globvar.mdiArea.closeActiveSubWindow()
globvar.winList.remove('dialgABM' + self.nomtabla)


When closing the windows using the X button D2 AND T2 are closed.
When pressing the cancela button, T2 is closed.
When pressing the confirma button, T2 is closed too.
It seems that the T2 table is the "current" window when D2 is closed.
Tried (with no success) to setActiveWindow the D2 dialog.

Any help to solve this issue will be appreciated. I have not posted all the code because doesn't make sense.
If someone could be interested in this development, please let me know, there is no problem sharing it.

Thanks in advance

anda_skoa
14th September 2015, 08:18
You are closig the "active sub window" of the mdi area.
That will always be an mdi sub window, not any dialog.

Btw, in you create a widget as "central widget" but then don't use it as such.
Just create the mdi area.

Cheers,
_

apereira
14th September 2015, 17:05
You are closig the "active sub window" of the mdi area.
That will always be an mdi sub window, not any dialog._

As i could understand from the docs, the next lines creates a subwindow in the mdiarea containing the dialog.



capturafm = dialgABM(self, ABM_ALTA, self.desc, self.campos, self.dialgx, self.dialgy, self.nombre)
globvar.mdiArea.addSubWindow(capturafm)


You are suggesting to create an mdisubwindow and then activate it?
dialgABM should return a QMdiSubWindow object and then globvar.mdiarea.setActiveSubwindow( capturafm )



Btw, in you create a widget as "central widget" but then don't use it as such.
Just create the mdi area.

Cheers,
_
Ok, noted and corrected.

Many thanks for your help.

Best regards.

Ariel Pereira

anda_skoa
14th September 2015, 17:40
I am afraid I don't understand.

If you want a QMdiArea subwindow, why derive from QDialog?
You only do that if you want a dialog.

Cheers,
_

apereira
18th September 2015, 17:37
Dear anda_skoa,

Thanks for your message.

I've changed the class from QDialog to QWidget, but still having the same behaviour.
For your guidance, find a preview.jpg file where it is shown the behaviour of the app (both windows "Clientes" and "Clientes > Nuevo" have focus at the same time).

I'm also attaching a test file (mainwindow.ui) created with QtCreator, and a p.py file created with pyuic5, with two added lines that links the QWidgets to the mdiarea.
As I can understand from this sample, the subwindows are created in the addSubWindow mehtod, and they contain a QWidget object.
I've been following the idea of this file into the app but still having the same result.

Any help will be welcome.

Regards.

apereira
21st September 2015, 23:29
Got it!!

It was a setFocus call that was causing this problem ... don't know exactly why ... but after comenting the line the window focus problem has gone.
The setFocus was used to force the focus to be on a certain QLineEdit, but it seems that something else is going on.

Thanks for your help.


Regards.