0

J'ai créé un grand programme python avec une interface graphique basée sur PyQt4. Je voudrais que le paquet fonctionne à la fois dans un cahier IPython (ancienne installation avec Python 2.7 sur Windows), cahier Jupyter (Python 3.5 installé récemment avec Anaconda), et comme un programme python passé sur la ligne de commande. J'ai des problèmes en exécutant le code dans le cahier de Jupyter (voir directement au fond).Pourquoi PyQt4 se comporte-t-il différemment entre Jupyter et IPython notebook?

module Mon mymodule.py ressemble à ceci (très simplifié, environ 10k lignes avant le spectacle de nombreux autres fichiers python):

from PyQt4 import QtCore, QtGui 

class MyModule(object): 
    def __init__(self): 
     self.window = QtGui.QMainWindow() 
     self.window.show() 

L'utilisation typique de la ligne de commande est

python myscript.py 

avec le fichier suivant myscript.py

from PyQt4 import QtCore, QtGui 
import mymodule 

m = mymodule.MyModule() 

APP = QtGui.QApplication.instance() 
APP.exec_() 

Cela fonctionne très bien. Je comprends que APP.exec_() est nécessaire pour démarrer une sorte de EventLoop qui fonctionne à travers les événements d'interaction Gui.

Dans un ordinateur portable IPython, l'utilisateur fait généralement

import mymodule 
m = mymodule.MyModule() # opens the gui 
# this still leaves the console active to allow things like this: 
m.change_color("red") 

Je peux exécuter ce sans problème, où j'understant que IPython prend en quelque sorte soin de la EventLoop derrière la scène. Maintenant, en exécutant les mêmes commandes dans le bloc-notes de Jupyter, une fenêtre s'ouvre, mais se fige avant d'autoriser toute interaction de l'utilisateur. Donc je crois que le cahier de Jupyter ne traite pas les événements correctement parce que je ne lui ai pas dit de le faire. Une façon que j'ai trouvé est l'exécution de la commande %pylab avant d'exécuter mon code. Cependant, je rencontre souvent des problèmes liés à cela, par exemple en exécutant %pylab et %matplotlib inline en succession directe avant de démarrer mon programme, cela conduit à geler à nouveau une fois que je charge mon code (curieusement, inverser l'ordre des deux commandes magiques fonctionne à nouveau) . Aussi, je ne veux pas forcer l'utilisateur de mon programme à exécuter% pylab dans chaque nouveau cahier si cela peut être évité (aussi parce que je crois que cela nécessite une installation matlab, ce qui n'est pas une exigence de mon programme).

Quel code dois-je ajouter dans mymodule.py pour rendre les choses compatibles avec le code utilisateur décrit dans le bloc-notes Jupyter? Quelqu'un peut-il expliquer plus clairement comment le bloc-notes IPython et le bloc-notes Jupyter gèrent différemment le QEventLoop/QApplication (ou quel que soit le concept important), et comment les commandes magiques gâchent-elles cela? J'ai peur des bugs cachés dans mon programme à cause de cela, et je voudrais le rendre aussi robuste que possible pour ne pas frustrer les utilisateurs.

Répondre

0

Voici une solution de travail qui permet d'exécuter le code sans faire de distinction entre les différentes versions d'IPython/Jupyter et Python «brut». Mon fichier __init__.py contient cette section au début:

# enable IPython QtGui support if needed 
try: 
    from IPython import get_ipython 
    get_ipython().magic('gui qt') 
except BaseException as e: 
    # issued if code runs in bare Python 
    print('Could not enable IPython gui support: %s.' % e) 

# get QApplication instance 
from PyQt4 import QtCore, QtGui 
APP = QtGui.QApplication.instance() 
if APP is None: 
    print('Creating new QApplication instance "mymodule"') 
    APP = QtGui.QApplication(['mymodule']) 

Le script en cours d'exécution sur Python brut suffit alors ceci:

import mymodule # imports the above code 
from PyQt4 import QtCore, QtGui 
if __name__ == '__main__': 
    QtGui.QApplication.instance().exec_() 

J'ai trouvé aucun cas d'utilisation où cela ne fonctionne pas.