Spyder: Kernel dies when running PyQt applications in the IPython console

Created on 11 Apr 2017  路  18Comments  路  Source: spyder-ide/spyder

@MikeAnbplips commented on Mon Apr 10 2017

I've posted some description here

While trying to run this simple GUI-based matplotlib script, I get a kernel died, restarting message, repeating 3-5 times, every other time I try to run the script.

I suspect this could be tied with PyQt, as I've used Spyder and matplotlib for relatively long and never had this type of behaviour.

from PyQt5.uic import loadUiType

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import (
    FigureCanvasQTAgg as FigureCanvas,
    NavigationToolbar2QT as NavigationToolbar)
import numpy as np
from scipy import interpolate
import pandas as pd


Ui_MainWindow, QMainWindow = loadUiType('txfmr.ui')

class Main(QMainWindow, Ui_MainWindow):

    def __init__(self,):
        super(Main, self).__init__()
        self.setupUi(self)
        self.fstart = 2
        self.fstop = 4
        self.fstartLblLine.setText(str(self.fstart))
        self.fstopLblLine.setText(str(self.fstop))
        self.LpLblLine.setText('1')
        self.LsLblLine.setText('1')
        self.kLblLine.setText('0.75')
        self.w = np.linspace(2*np.pi*self.fstart*1e9,2*np.pi*self.fstop*1e9,101)
        self.DrawBtn.clicked.connect(self.draw)

    def addmpl(self, fig):
        self.canvas = FigureCanvas(fig)
        self.mplvl.addWidget(self.canvas)
        self.canvas.draw()
        self.toolbar = NavigationToolbar(self.canvas, self.mplwindow, coordinates=True)
        self.mplvl.addWidget(self.toolbar)

    def rmmpl(self,):
        self.mplvl.removeWidget(self.canvas)
        self.canvas.close()
        self.mplvl.removeWidget(self.toolbar)
        self.toolbar.close()

    def R(self,L,w,rf=1,rf2=1,deg=0):
        #rf = 1.78/1.24
        r0 = rf*L*1e9
        rf2 = 1
        alpha = rf2/(w[-1]-w[0])
        if deg == 0:
            return np.array([r0])
        return r0 + (alpha*(w-w[0]))**deg


    def calc_txf_std(self,Lp,Ls,k,w,rf=1,rf2=1,deg=0):
        RL = 50
        Rp = self.R(Lp,self.w,1.78/1.24,rf2,deg)                         #primary inductance resistance
        Rs = self.R(Ls,self.w,4.66/2.4,rf2,deg)
        tratio = 1/k*np.sqrt(Lp/Ls)
        n = k*np.sqrt(Lp/Ls)
        M = k*np.sqrt(Lp*Ls)
        Qip = w*Lp/Rp
        Qis = w*Ls/Rs
        Ql = w*Ls/(Rs+RL)
        Rsp = Rp + Ql**2/(1+Ql**2)*n**2*(Rs+RL)
        Lsp = Lp*(1-k**2*Ql**2/(1+Ql**2))
        Qsp = w*Lsp/Rsp
        Rpp = Rsp*(1+Qsp**2)
        Lpp = Lsp*(1+Qsp**2)/Qsp**2
        Cres = 1/(w**2*Lpp)
        Qc = 1/(w*Rp*Cres)
        w0 = 1/np.sqrt(Cres*Lp)
        ww0 = np.array([(w/wx)**2-1 for wx in w0])
        R_e_o = Rs + (w*M)**2/(Rp*(1+(Qc*ww0)**2))
        R_e_o_max = np.array([x.max() for x in R_e_o[:]])
        ww0 = (w/w0)**2-1
        IL = 10*np.log10(Rsp/(Ql**2/(1+Ql**2)*n**2*RL))
        Lprime_p = Lp*(1 - (1-k**2*Ql**2/(1+Ql**2))*(1+Qsp**2)/Qsp**2)
        Qprime_p = w*Lprime_p/Rp
        R_o_res = Rs + (w*M)**2/(Rp*(1+Qprime_p**2))
        X_o_res = w*Ls - (w*M)**2/(w*Lprime_p*(1+Qprime_p**2)/Qprime_p**2)
        RTL = 20*np.log10(abs((R_e_o_max-50)/(R_e_o_max+50)))
        return({'tratio':tratio,'n':n,'Ql':Ql,'Qsp':Qsp,'Rsp':Rsp,'Lsp':Lsp,'Rpp':Rpp,'Lpp':Lpp,'Cres':Cres,'IL':IL,\
               'Lprime_p':Lprime_p,'Qprime_p':Qprime_p,'R_o_res':R_o_res,'X_o_res':X_o_res,'RL':RTL,'Rs':Rs,'Rp':Rp,'Qip':Qip,'Qis':Qis})

    def plt_stuff(self,Lp=1,Ls=0.9,k=0.85):

        fig = Figure()
        ax1 = fig.add_subplot(221)
        ax2 = fig.add_subplot(222)
        ax3 = fig.add_subplot(223)
        ax4 = fig.add_subplot(224)

        ax1.plot(self.w*1e-9/(2*np.pi),self.calc_txf_std(Lp*1e-9,Ls*1e-9,k,self.w)['Rpp'],label='Rpp')
        ax1.legend()
        fig.suptitle('voltage ratio :{:.2f}'.format(self.calc_txf_std(Lp*1e-9,Ls*1e-9,k,self.w)['tratio']))
        ax1.grid(True)
        ax3.plot(self.w*1e-9/(2*np.pi),self.calc_txf_std(Lp*1e-9,Ls*1e-9,k,self.w)['IL'],label='IL (dB)',color='g')
        ax3.legend()
        ax3.grid(True)
        ax4.plot(self.w*1e-9/(2*np.pi),self.calc_txf_std(Lp*1e-9,Ls*1e-9,k,self.w)['RL'],label='RL (dB)',color='k')
        ax4.legend()
        ax4.grid(True)
        if len(self.calc_txf_std(Lp*1e-9,Ls*1e-9,k,self.w)['Rs']) == 1:
            ax2.plot([self.w[0]*1e-9/(2*np.pi),self.w[-1]*1e-9/(2*np.pi)],[self.calc_txf_std(Lp*1e-9,Ls*1e-9,k,self.w)['Rs'],self.calc_txf_std(Lp*1e-9,Ls*1e-9,k,self.w)['Rs']],label='Rs',color='r')
            ax2.plot([self.w[0]*1e-9/(2*np.pi),self.w[-1]*1e-9/(2*np.pi)],[self.calc_txf_std(Lp*1e-9,Ls*1e-9,k,self.w)['Rp'],self.calc_txf_std(Lp*1e-9,Ls*1e-9,k,self.w)['Rp']],label='Rp',color='b')

        else:
            ax2.plot(w*1e-9/(2*pi),calc_txf_std(Lp*1e-9,Ls*1e-9,k,w)['Rs'],label='Rs',color='r')
            ax2.plot(w*1e-9/(2*pi),calc_txf_std(Lp*1e-9,Ls*1e-9,k,w)['Rp'],label='Rp',color='b')
        ax2.legend()
        ax2.grid(True)
        self.rmmpl()
        self.addmpl(fig)

    def draw(self,):
        Lp = float(self.LpLblLine.text())
        Ls= float(self.LsLblLine.text())
        k= float(self.kLblLine.text())
        self.fstart = float(self.fstartLblLine.text())
        self.fstop = float(self.fstopLblLine.text())
        self.w = np.linspace(2*np.pi*self.fstart*1e9,2*np.pi*self.fstop*1e9,101)
        self.plt_stuff(Lp,Ls,k)


if __name__ == '__main__':
    import sys
    from PyQt5 import QtGui,QtWidgets
    import numpy as np
    import pandas as pd
    fig1 = Figure()
    app = QtWidgets.QApplication(sys.argv)
    main = Main()
    main.addmpl(fig1)
    main.show()
    sys.exit(app.exec_())

The UI is create with Qt Designer and left as XML file:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QWidget" name="mplwindow" native="true">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <layout class="QVBoxLayout" name="mplvl"/>
     </widget>
    </item>
    <item>
     <widget class="QFrame" name="frame">
      <property name="maximumSize">
       <size>
        <width>16777215</width>
        <height>100</height>
       </size>
      </property>
      <property name="frameShape">
       <enum>QFrame::StyledPanel</enum>
      </property>
      <property name="frameShadow">
       <enum>QFrame::Raised</enum>
      </property>
      <widget class="QWidget" name="formLayoutWidget">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>30</y>
         <width>91</width>
         <height>80</height>
        </rect>
       </property>
       <layout class="QFormLayout" name="tx_params">
        <item row="0" column="0">
         <widget class="QLabel" name="LpLbl">
          <property name="text">
           <string>Lp (nH)</string>
          </property>
         </widget>
        </item>
        <item row="0" column="1">
         <widget class="QLineEdit" name="LpLblLine">
          <property name="maximumSize">
           <size>
            <width>40</width>
            <height>16777215</height>
           </size>
          </property>
         </widget>
        </item>
        <item row="1" column="0">
         <widget class="QLabel" name="LsLbl">
          <property name="text">
           <string>Ls (nH)</string>
          </property>
         </widget>
        </item>
        <item row="1" column="1">
         <widget class="QLineEdit" name="LsLblLine">
          <property name="maximumSize">
           <size>
            <width>40</width>
            <height>16777215</height>
           </size>
          </property>
          <property name="maxLength">
           <number>10</number>
          </property>
         </widget>
        </item>
        <item row="2" column="0">
         <widget class="QLabel" name="kLbl">
          <property name="text">
           <string>k</string>
          </property>
         </widget>
        </item>
        <item row="2" column="1">
         <widget class="QLineEdit" name="kLblLine">
          <property name="maximumSize">
           <size>
            <width>40</width>
            <height>16777215</height>
           </size>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
      <widget class="QPushButton" name="DrawBtn">
       <property name="geometry">
        <rect>
         <x>110</x>
         <y>30</y>
         <width>75</width>
         <height>23</height>
        </rect>
       </property>
       <property name="text">
        <string>Draw</string>
       </property>
      </widget>
      <widget class="QWidget" name="formLayoutWidget_2">
       <property name="geometry">
        <rect>
         <x>200</x>
         <y>30</y>
         <width>81</width>
         <height>80</height>
        </rect>
       </property>
       <layout class="QFormLayout" name="f_range">
        <item row="0" column="0">
         <widget class="QLabel" name="fstartLbl">
          <property name="text">
           <string>f start</string>
          </property>
         </widget>
        </item>
        <item row="0" column="1">
         <widget class="QLineEdit" name="fstartLblLine">
          <property name="maximumSize">
           <size>
            <width>40</width>
            <height>16777215</height>
           </size>
          </property>
         </widget>
        </item>
        <item row="1" column="0">
         <widget class="QLabel" name="fstopLbl">
          <property name="text">
           <string>f stop</string>
          </property>
         </widget>
        </item>
        <item row="1" column="1">
         <widget class="QLineEdit" name="fstopLblLine">
          <property name="maximumSize">
           <size>
            <width>40</width>
            <height>16777215</height>
           </size>
          </property>
          <property name="maxLength">
           <number>10</number>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
      <widget class="QLabel" name="label">
       <property name="geometry">
        <rect>
         <x>290</x>
         <y>30</y>
         <width>40</width>
         <height>20</height>
        </rect>
       </property>
       <property name="sizePolicy">
        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
         <horstretch>0</horstretch>
         <verstretch>0</verstretch>
        </sizepolicy>
       </property>
       <property name="maximumSize">
        <size>
         <width>40</width>
         <height>30</height>
        </size>
       </property>
       <property name="text">
        <string>GHz</string>
       </property>
      </widget>
      <widget class="QLabel" name="label_2">
       <property name="geometry">
        <rect>
         <x>290</x>
         <y>56</y>
         <width>40</width>
         <height>20</height>
        </rect>
       </property>
       <property name="sizePolicy">
        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
         <horstretch>0</horstretch>
         <verstretch>0</verstretch>
        </sizepolicy>
       </property>
       <property name="maximumSize">
        <size>
         <width>40</width>
         <height>30</height>
        </size>
       </property>
       <property name="text">
        <string>GHz</string>
       </property>
      </widget>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>21</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

@ccordoba12 commented on Mon Apr 10 2017

Thanks a lot for the issue report. I'm moving it to the Spyder repo, where it really belongs.

IPython Console

Most helpful comment

@MikeAnbplips can you change the main (with the code below) and try if that works for you?

if __name__ == '__main__':
    import sys
    from PyQt5 import QtWidgets
    fig1 = Figure()
    if not QtWidgets.QApplication.instance():
        app = QtWidgets.QApplication(sys.argv)
    else:
        app = QtWidgets.QApplication.instance() 
    main = Main()
    main.addmpl(fig1)
    main.show()
    sys.exit(app.exec_())

All 18 comments

@dalthviz, please take a look at this one.

Hi @MikeAnbplips can you post here also the .csv that you read in your code?

@dalthviz this is actually a leftover...I do not do anything with that file anymore.
@ccordoba12 can you please edit the top post and remove - or change - the line with the file path and remove the line with the pd.read_csv() command?

Done.

Thanks, @ccordoba12 please take out the file path above the pd.read_csv() as well...It's a little too detailed :)

@MikeAnbplips can you change the main (with the code below) and try if that works for you?

if __name__ == '__main__':
    import sys
    from PyQt5 import QtWidgets
    fig1 = Figure()
    if not QtWidgets.QApplication.instance():
        app = QtWidgets.QApplication(sys.argv)
    else:
        app = QtWidgets.QApplication.instance() 
    main = Main()
    main.addmpl(fig1)
    main.show()
    sys.exit(app.exec_())

@dalthviz
Yes! I have the 'kernel died..' only with my original code.
It appears to be related to that conditional statement you added, as I tried to comment out the imports and nothing changed.
I am a total noob so I'm curious to learn whether the if statement is correct practice that should have been there from the beginning.

Glad it works, about the conditional, I added it in order to be sure that there is only one QApplication instance in the process, since having more would cause a segmentation fault that makes the kernel die (that is what I suspect is happening with your initial code). You can read about this here

Also in the docs of Qt they say that you should created only one application object. You can read this here

@MikeAnbplips, this is needed because Spyder is built in PyQt, so it already has a QApplication running for you.

@dalthviz, is there a way for us to inform users about this?

@ccordoba12 maybe before actually executing the code we should search for a call to QApplication in the file and show a message with the information about handling its call with the validation of instance to prevent errors?

Yeah, but it would be very difficult for us to distinguish when people are using QApplication correctly and when they aren't.

Besides, they could define a variable to create a QApplication in another file and import it in their main one, and that's impossible for us to track.

So, in the end, it's not possible for us to correctly inform users in all situations.

Spyder itself could monkeypatch PyQt to prevent instantiating a second instance?

In [1]: from PyQt5 import QtWidgets

In [2]: QtWidgets.QApplication.instance()
Out[2]: <PyQt5.QtWidgets.QApplication at 0xb3eeca8>

In [3]: QtWidgets.QApplication(['test'])
Out[3]: <PyQt5.QtWidgets.QApplication at 0xc3d1e58>

In [4]: class SingletonError(Exception):
   ...:     pass

In [5]: def __new__(cls, *args, **kwargs):
   ...:     if cls.instance():
   ...:         msg = "There can only be one QApplication per process!"
   ...:         raise SingletonError(msg)
   ...:     return object.__new__(cls)

In [6]: QtWidgets.QApplication.__new__ = __new__

In [7]: QtWidgets.QApplication(['test'])
Traceback (most recent call last):

  File "<ipython-input-7-499cbaca69f6>", line 1, in <module>
    QtWidgets.QApplication(['test'])

  File "<ipython-input-5-03275aeba9f4>", line 4, in __new__
    raise SingletonError(msg)

SingletonError: There can only be one QApplication per process!

This is great suggestion Dave! However, I don't know how to do this without interfering with our facilities to set the Matplotlib backend.

It could probably go here:

https://github.com/ipython/ipython/blob/master/IPython/lib/guisupport.py#L112

because that's the function used by ipykernel to create a Qt app to run Matplotlib event's loop.

Yep, looks like the right place. I think the creator of the app should monkeypatch it to prevent anyone else accidentally instantiating a second instance.

Would you mind to open a PR in IPython with this feature and ping me when you do it? If you don't have time, I'll assign one of our juniors to do it.

I'm on holiday at the moment so wouldn't be able to tackle that for a couple of weeks so am happy for someone else to pick it up...

@ccordoba12 should I do the PR in IPython with the feature suggested?

Yes, please. I was about to contact you about it. You need to redefine the __new__ method of QApplication before one is created in get_app_qt4.

Please ping me once you open the PR to explain the IPython guys our rationale and why we need this.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

goanpeca picture goanpeca  路  3Comments

LDONT picture LDONT  路  3Comments

spyder-bot picture spyder-bot  路  3Comments

spyder-bot picture spyder-bot  路  3Comments

ok97465 picture ok97465  路  3Comments