「Qtその他 - PyQt5とPySide2」の版間の差分

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動
(文字列「__FORCETOC__」を「{{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This pag…)
 
(同じ利用者による、間の5版が非表示)
314行目: 314行目:


== QtPy ==
== QtPy ==
QtPyは、PyQtやPySideへの単一のAPIを使用して、ソフトウェアが開発できる抽象化レイヤである。<br>
Qt5(QtGuiモジュールがQtGuiとQtWidgetsに分割されている)を使用して、PyQt5、PyQt4、PySide2、PySideをサポートしている。<br>
<br>
PyQt5、PyQt4、PySide2、PySide向けの標準化されたPySide2のようなAPIが提供されている。<br>
PySide2モジュールやPyQt5モジュールの代わりに、qtpyモジュールからQtモジュールをインポートする。<br>
<br>
QtPyを使用するには、PyQt5、PyQt4、PySide2、PySideをインストールしている必要がある。<br>
これらのライブラリのいずれかが存在する場合、環境変数QT_APIを設定しない時は、標準でPyQt5が使用される。<br>
<br>
環境変数<code>QT_API</code>は、以下の値を取ることができる。<br>
* pyqt5
*: PyQt5を使用する。
* pyqtまたはpyqt4
*: PyQt4を使用する。
* pyside2
*: PySide2を使用する。
* pyside
*: PySideを使用する。
<br>
QtPyをインストールするには、以下のコマンドを実行する。<br>
pip3 install qtpy
<br>
Qt5以外のもの(PyQt 4やPySide 1等)をターゲットにする場合、[https://github.com/spyder-ide/qtpy QtPyのGitHub]を参照すること。<br>
Qt5以外のもの(PyQt 4やPySide 1等)をターゲットにする場合、[https://github.com/spyder-ide/qtpy QtPyのGitHub]を参照すること。<br>
QtPyは、PyQt4、PyQt5、PySide1、PySide2向けの標準化されたPySide2のようなAPIが提供されている。<br>
<br>
<br>
QtPyを使用すると、環境変数<code>QT_API</code>を使用して、ソフトウェアから読み込むAPIを制御できる。<br>
QtPyを使用すると、環境変数<code>QT_API</code>を使用して、ソフトウェアから読み込むAPIを制御できる。<br>
325行目: 346行目:
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>
== エラー関連 ==
PySide2プロジェクトを実行する時、以下のような警告が出力される場合がある。<br>
Qt WebEngine seems to be initialized from a plugin.
Please set Qt::AA_ShareOpenGLContexts using QCoreApplication::setAttribute before constructing QGuiApplication.
<br>
これは、<code>QApplication</code>クラスのインスタンスを生成する前に、<code>AA_ShareOpenGLContexts</code>を設定することで修正できる。<br>
以下の例は、PySide2を使用している場合である。<br>
<syntaxhighlight lang="python">
from PySide2 import QtCore, QtWidgets
# ...略
if __name__ == '__main__':
    QCoreApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
    app = QApplication(sys.argv)
    # ...略
</syntaxhighlight>
<br>
また、PySide6を使用することで警告を回避することもできる。<br>
<br><br>
{{#seo:
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux
|image=/resources/assets/MochiuLogo_Single_Blue.png
}}
__FORCETOC__
[[カテゴリ:Qt]][[カテゴリ:Python]]
{{#seo:
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux
|image=/resources/assets/MochiuLogo_Single_Blue.png
}}


__FORCETOC__
__FORCETOC__
[[カテゴリ:Qt]]
[[カテゴリ:Qt]][[カテゴリ:Python]]

2024年10月14日 (月) 10:57時点における最新版

概要

Qt5でPythonの開発環境を構築する時、PyQt5とPySide2の2種類が存在する。

ここでは、2種類のいくつかの差異を確認して、両方でシームレスに機能するサンプルコードを作成する手順を記載する。
これらを理解することにより、PyQt5の例からPySide2で動作するようにに変換することができる。


歴史

PyQt5とPySide2の2種類のパッケージが存在する理由を記載する。

PyQtは、Riverbank Computing社のPhilThompsonによって開発されている。
2009年にQtツールキットを所有していたNokiaは、Qt用のPythonバインディングをLGPLライセンス(Qtと同様)で利用できるようにしたいと考えていた。
Riverbank社と合意することができず、彼らはPySideとして独自のバインディングをリリースした。

2種類のインターフェースは最初は同等だったが、PySideの開発は最終的にPyQtに遅れをとっていた。(Qt5のリリース後に特に顕著だった)
PyQt5は2016年半ばから利用可能であったが、PySide2の最初の安定したリリースは2018年からである。
Qt5を使用したPythonの例の多くが、PySide2ではなくPyQt5であるのは、この遅延のせいである。

ただし、QtはPySide2を公式で採用したため、その実行可能性が確保されて、今後の人気が高まるはずである。

※注意
Sideは、フィンランド語でbinderを意味するため、PySideと呼ばれている。

PyQt 5 PySide 2
最新安定版
(2021年1月現在)
5.15 5.15
最初の安定版 2016年4月 2018年7月
開発者 Riverbank Computing社 Qt
ライセンス形態 GPL
または
コマーシャル
LGPL
プラットフォーム Python 3以降 Python 3以降
Python 2.7(Windows x86、Linux x64、MacOS)



どちらを使用すべきか

PyQt5とPySide2はQt5をラッピングしているため、同じAPIが99.9%存在する。(いくつかの違いについては、後述のセクションを参照)
同じソースコードをそのまま使用できることが多く、import文をPyQt5からPySide2に変更するだけである。

一方のライブラリで学習したことは、もう一方のライブラリを使用して簡単に適用できる。
例えば、PyQt5のチュートリアルを使用してPySide2を使用したソフトウェアを構築することができる。


ライセンス形態

PyQt5とPySide2のの主な違いは、ライセンス形態である。

PyQt5は、GPLまたは商用ライセンスの下で利用可能であり、PySide2はLGPLライセンスの下で利用可能である。

ソフトウェアをGPLの下でリリースする場合や配布されないソフトウェアを開発している場合、PyQt5のGPL要件が問題になる可能性はほとんどない。
ただし、ソースコードを配布せずにソフトウェアを配布する場合、Riverbank社からPyQt5の商用ライセンスを購入するか、PySide2を使用する必要がある。

LGPLライセンスでは、PySide2にバンドルされている場合でも、開発するソフトウェアのソースコードを配布する必要はない。
LGPLの対象となるソースコードが利用可能になっていることを確認するだけである。(これは、変更があった場合も含まれる)
通常の使用では変更はなく、既にPySide2 / Qt5の標準のソースコードでカバーされている。

Qt5は、Qt Commercial License、GPL2.0、GPL3.0、LGPL3.0ライセンスの下で利用できる。


UIファイル

PyQt5とPySde2は、どちらもQt CreatorまたはQt Designerからエクスポートされた.uiファイルをロードすることができる。
ただし、わずかな差異が存在するため、以下に詳細を記載する。

PyQt5は、UIファイルを読み込んでオブジェクトを生成するuicモジュールがある。

 
 # PyQt 5
 
 import sys
 from PyQt5 import QtWidgets, uic
 
 app = QtWidgets.QApplication(sys.argv)
 
 window = uic.loadUi("mainwindow.ui")
 window.show()
 app.exec()


PySide2は、UIファイルを読み込むために、QUILoaderクラスのオブジェクトを生成する必要がある。
これらはAPI名と引数が異なる。(loadメソッドとloadUIメソッド)

 # PySide 2
 
 import sys
 from PySide2 import QtCore, QtGui, QtWidgets
 from PySide2.QtUiTools import QUiLoader
 
 app = QtWidgets.QApplication(sys.argv)
 
 loader = QUiLoader()
 window = loader.load("mainwindow.ui", None)
 window.show()
 app.exec_()


PyQt5の画面オブジェクト(QMainWindow.__init__等)にUIファイルを読み込むには、
uic.loadUIメソッドを使用して、第1引数にUIファイルのパス、第2引数にself(ターゲットとなるウィジェット)を渡す。

 # PyQt 5
 
 import sys
 from PyQt5 import QtCore, QtGui, QtWidgets
 from PyQt5 import uic
 
 
 class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, *args, **kwargs):
       super().__init__(*args, **kwargs)
       uic.loadUi("mainwindow.ui", self)
 
 
 app = QtWidgets.QApplication(sys.argv)
 window = MainWindow()
 window.show()
 app.exec_()


PySide2の画面オブジェクトにUIファイルを読み込むには、
loadメソッドを使用して、第1引数には、第2引数に作成しているウィジェットの親ウィジェットを渡す。
これにより、ウィジェットの__init__ブロックにカスタムコードを追加できなくなるが、別の関数を使用してこれを回避できる。

 # PySide 2
 
 import sys
 from PySide2 import QtWidgets
 from PySide2.QtUiTools import QUiLoader
 
 
 def mainwindow_setup(w):
    w.setTitle("MainWindow Title")
 
 
 app = QtWidgets.QApplication(sys.argv)
 
 loader = QUiLoader()
 window = loader.load("mainwindow.ui", None)
 mainwindow_setup(window)
 window.show()
 app.exec()



UIファイルをPythonファイルに変換する

PyQt5とPySide2は、.uiファイルから.pyファイルへインポート可能モジュールを生成するための同一のスクリプトを提供する。

PyQt5の場合は、pyuic5をインストールして、以下のコマンドを実行する。

pyuic5 mainwindow.ui -o MainWindow.py


次に、MainWindowモジュールからUI_MainWindowクラス(使用している基本クラス(QMainWIndow等)を継承したサブクラス)をインポートして、
self.setupUI(self)メソッドを使用して、UIを設定する。

 # PyQt 5
 
 import sys
 from PyQt5 import QtWidgets
 from MainWindow import Ui_MainWindow
 
 
 class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, *args, **kwargs):
       super().__init__(*args, **kwargs)
       self.setupUi(self)
 
 
 app = QtWidgets.QApplication(sys.argv)
 window = MainWindow()
 window.show()
 app.exec()


PySide2の場合、pyside2-uicをインストールして、以下のコマンドを実行する。

pyside2-uic mainwindow.ui -o MainWindow.py


後の設定は、PyQt5と同様である。

 # PySide 2
 
 import sys
 from PySide2 import QtWidgets
 from MainWindow import Ui_MainWindow
 
 
 class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, *args, **kwargs):
       super().__init__(*args, **kwargs)
       self.setupUi(self)
 
 
 app = QtWidgets.QApplication(sys.argv)
 window = MainWindow()
 window.show()
 app.exec_()


PyQt5またはPySide2でQtDesignerを使用する方法の詳細は、Qt Creatorのチュートリアルを参照すること。


execメソッドとexec_メソッド

execメソッドはQtで使用されており、QApplicationまたはダイアログのイベントループを開始する。

Python 2.7では、execはキーワードだった経緯があり、変数、関数、メソッド名には使用できなかった。
PyQt4やPySideで実施された解決策は、この競合を回避するために、execの名前をexec_に変更することだった。
Python 3では、execキーワードを削除されており、名前を解放して使用できるようになった。

PyQt5は、Python 3のみを対象としているため回避策を取り除くことができ、execメソッド名はQt自体と同じ名前となっている。
ただし、exec_メソッド名は、下位互換性のために維持されている。

PySide2は、Python 3とPython 2.7の両方で使用できるため、引き続き、exec_メソッドを使用する。
ただし、Linux x64およびMacOSでのみ使用できる。

PyQt5とPySide2の両方をターゲットにしている場合は、exec_メソッドを使用する。


スロットとシグナル

PyQt5とPySide2のカスタムスロットとシグナルの定義では、わずかに差異があるシンタックスが使用される。

PySide2は、SignalとSlotという名前でスロットとシグナルを提供している。
PyQt5は、pyqtSignalとpyqtSlotという名前でスロットとシグナルを提供している。
これらの動作は、同じである。

以下に示すPyQt5とPySide2の例の動作は同じである。

 # シグナル
 
 my_custom_signal = pyqtSignal()    # PyQt5
 my_custom_signal = Signal()        # PySide2
 
 my_other_signal = pyqtSignal(int)  # PyQt5
 my_other_signal = Signal(int)      # PySide2


 # スロット
 
 @pyqtslot
 def my_custom_slot():
    # Do Something
 
 @Slot
 def my_custom_slot():
    # Do Something


PyQt5とPySide2の間で一貫性を確保する場合、以下のように、PyQt5またはPySide2にimportパターンを使用して、Signalおよび@Slotを定義する。

 from PyQt5.QtCore import pyqtSignal as Signal, pyqtSlot as Slot
 # または
 from PySide2.QtCore import Signal as pyqtSignal, Slot as pyqtSlot



PyQt5とPySide2の両方のサポート

PyQt5とPySide2の両方と互換性を持たせるライブラリ、ウィジェット、その他のツールを開発している場合、
両方のインポートセットを追加することで簡単に実行できる。

以下の例は、カスタムウィジェットライブラリで使用されているアプローチであり、単一のライブラリインポートでPyQt5とPySide2をサポートしている。
注意点は、sys.modulesにあることを確認するために、sysモジュールをインポートする必要がある。

 # xxx.pyファイル
 
 import sys
 
 if 'PyQt5' in sys.modules:
    # PyQt5
    from PyQt5 import QtGui, QtWidgets, QtCore
    from PyQt5.QtCore import pyqtSignal as Signal, pyqtSlot as Slot
 
 else:
    # PySide2
    from PySide2 import QtGui, QtWidgets, QtCore
    from PySide2.QtCore import Signal, Slot


ただし、上記の方法では、複数のPythonファイルが存在する場合、各Pythonファイルで記述するため煩雑である。
この解決策は、インポートロジックを独自のファイルに移動することである。

例えば、プロジェクトのルートにqt.pyファイルを作成して、
PyQt5とPySide2のいずれかからQtモジュール(QtCore、QtGui、QtWidgets等)をインポートした後、他のPythonファイルからインポートする。

以下に、qt.pyファイルのソースコードを記述する。
他のPyQt5モジュールおよびPySide2モジュール(ブラウザ、マルチメディア等)を使用する場合、if-elseブロックの両方に追加することを忘れないこと。

 # qt.pyファイル(プロジェクトのルート)
 
 import sys
 
 if 'PyQt5' in sys.modules:
    # PyQt5
    from PyQt5 import QtGui, QtWidgets, QtCore
    from PyQt5.QtCore import pyqtSignal as Signal, pyqtSlot as Slot
 
 else:
    # PySide2
    from PySide2 import QtGui, QtWidgets, QtCore
    from PySide2.QtCore import Signal, Slot


次に、他のPythonファイルからQt5(qt.pyファイル)をインポートする。

 # xxx.pyファイル
 
 from .qt import QtGui, QtWidgets, QtCore


その他の方法として、環境変数QT_APIを使用して、それらを切り替える方法がある。
詳細は、次のQtPyセクションを参照すること。


QtPy

QtPyは、PyQtやPySideへの単一のAPIを使用して、ソフトウェアが開発できる抽象化レイヤである。
Qt5(QtGuiモジュールがQtGuiとQtWidgetsに分割されている)を使用して、PyQt5、PyQt4、PySide2、PySideをサポートしている。

PyQt5、PyQt4、PySide2、PySide向けの標準化されたPySide2のようなAPIが提供されている。
PySide2モジュールやPyQt5モジュールの代わりに、qtpyモジュールからQtモジュールをインポートする。

QtPyを使用するには、PyQt5、PyQt4、PySide2、PySideをインストールしている必要がある。
これらのライブラリのいずれかが存在する場合、環境変数QT_APIを設定しない時は、標準でPyQt5が使用される。

環境変数QT_APIは、以下の値を取ることができる。

  • pyqt5
    PyQt5を使用する。
  • pyqtまたはpyqt4
    PyQt4を使用する。
  • pyside2
    PySide2を使用する。
  • pyside
    PySideを使用する。


QtPyをインストールするには、以下のコマンドを実行する。

pip3 install qtpy


Qt5以外のもの(PyQt 4やPySide 1等)をターゲットにする場合、QtPyのGitHubを参照すること。

QtPyを使用すると、環境変数QT_APIを使用して、ソフトウェアから読み込むAPIを制御できる。

 import os
 
 os.environ['QT_API'] = 'pyside2'
 from qtpy import QtGui, QtWidgets, QtCore  # imports PySide2.



エラー関連

PySide2プロジェクトを実行する時、以下のような警告が出力される場合がある。

Qt WebEngine seems to be initialized from a plugin.
Please set Qt::AA_ShareOpenGLContexts using QCoreApplication::setAttribute before constructing QGuiApplication.


これは、QApplicationクラスのインスタンスを生成する前に、AA_ShareOpenGLContextsを設定することで修正できる。
以下の例は、PySide2を使用している場合である。

 from PySide2 import QtCore, QtWidgets
 
 # ...略
 
 if __name__ == '__main__':
    QCoreApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
    app = QApplication(sys.argv)
 
    # ...略


また、PySide6を使用することで警告を回避することもできる。