PyQt6 / finplot - Compatability Issue? (problem isolated to small runnable code) - python

My head is spinning from trying to get finplot to create an embedded graph for days with no luck. I decided to restart small.
The code that I included works as-is. However, if I change the import to PyQt6 from PyQt5, it stops working.
The application that I am trying to integrate this into is all done in PyQt6. finplot works and displays externally in my PyQt6 attempts, but I cannot use fplt.create_plot_widget while passing self.window() with PyQt6, it seems.
Honestly, I keep running into trouble any time i try to use pyqtgraph. I sidetracked to do something clever with mplfinance, but I'm back to where i was 6 months ago with trying to get anything graph and Qt related to show up in a layout nicely with the other widgets :/
Any help is appreciated. Thank you.
import sys
import finplot as fplt
from PyQt5.QtWidgets import *
import yfinance
class App(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'PyQt5 - QTabWidget'
self.left = 0
self.top = 0
self.width = 600
self.height = 400
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.menu = self.menuBar()
self.widget = MyGraphWidget(self)
self.setCentralWidget(self.widget)
fplt.show(qt_exec=False)
self.show()
class MyGraphWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
self.layout = QVBoxLayout(self)
self.resize(600, 400)
self.label = QLabel("AAPL")
self.df = yfinance.download('AAPL')
self.fplt_widget1, self.fplt_widget2 = fplt.create_plot_widget(self.window(), rows=2)
fplt.candlestick_ochl(self.df[['Open', 'Close', 'High', 'Low']])
self.layout.addWidget(self.label)
self.window().axs = [self.fplt_widget1]
self.layout.addWidget(self.fplt_widget1.ax_widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec())

This is not an issue with pyqtgraph. That project currently supports PyQt6/PySide6.
However, looking at finplot project's setup.py here, it looks like it is currently on PyQt5 (evidenced by the configuration line install_requires=['pandas', 'PyQt5', 'pyqtgraph>=0.11.1'],). The migration to PyQt6 would have to start there. There is some visible mention of hints toward PyQt6 in the project's issues here and here.
As of today, it appears that your options are:
Submit an issue to finplot's maintainer to request support for PyQt6.
Fork finplot, do the migration work yourself and submit a PR back.
Keep using finplot with PyQt5.

Related

Segmentation fault when exiting PyQT app

First, I have tried my best to find a solution to this problem here and other places and I am have a general idea of what the problem is, but it is not clear to me how to solve it.
The basic problem is that I am getting a segmentation fault when I close my app by pressing the standard "x" button.
The most important details (I think) are that I am using MacOS Sierra, python 3.5.2, and pyqt5.
The app I am building is very loosely based on another project (Dioptas), which is a relatively mature project. I am more or less getting started.
When I close the window, the terminal prints out as instructed in MainController.close_event():
> here
> closed
> accepted
> Segmentation fault: 11
I have tried many of the suggestions online and I am fairly sure that this is due to python not closing all of the windows (perhaps due to the order in which they are being closed - QApplication.CloseAllWindows() says they are closed in a random order, for one thing). If anyone has a suggestion or solution I would really appreciate it.
The following is my code:
import sys
import pyqtgraph as pg
import numpy as np
from PIL import Image
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class MainController(QWidget):
def __init__(self):
super().__init__
self.start()
self.create_signals()
def start(self):
self.widget = MainWidget()
self.widget.show()
def create_signals(self):
self.widget.closeEvent = self.close_event
def close_event(self, ev):
print("here")
QApplication.closeAllWindows()
print("closed")
ev.accept()
class MainWidget(QWidget):
def __init__(self, *args, **kwargs):
super(MainWidget, self).__init__(*args, **kwargs)
self.layout = QHBoxLayout()
self.layout.setContentsMargins(2, 2, 2, 2)
self.layout.setSpacing(6)
self.stepFilterDisplayWidget = StepFilterDisplayWidget()
self.stepFilterControlWidget = StepFilterControlWidget()
self.layout.addWidget(self.stepFilterDisplayWidget)
self.layout.addWidget(self.stepFilterControlWidget)
self.setLayout(self.layout)
self.setGeometry(100,100,1000,700)
class StepFilterDisplayWidget(QWidget):
def __init__(self, *args, **kwargs):
super(StepFilterDisplayWidget,self).__init__(*args,**kwargs)
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.plot = pg.ImageView()
self.layout.addWidget(self.plot)
self.button = QPushButton("Plot", self)
self.button.clicked.connect(self.showImage)
self.layout.addWidget(self.button)
def showImage(self):
im = Image.open('S_15a_crop.tif')
self.data = np.array(im)
self.plot.setImage(self.data)
class StepFilterControlWidget(QWidget):
def __init__(self, *args, **kwargs):
super(StepFilterControlWidget, self).__init__(*args, **kwargs)
if __name__ == "__main__":
app = QApplication(sys.argv)
controller = MainController()
app.exec_()
del app
The problem is about pyqtgraph (which uses PyQt4) and PyQt5 imports. pyqtgraph trying to use something belongs to PyQt4 which was overriden by PyQt5 import. This triggers the segmentation fault.
I was having the same problem - with pyqt5 and python3.10
My app has 3 pyqtgraph widgets:
1 pg.ImageView() created directly in the main application which is a QtWidgets.QMainWindow()
2 pg.PlotWidget() + pg.opengl.GLViewWidget() loaded into the mainwidow application from another class QueryBuilder() which is QWidget
To quit the app I was using a call to qApp.quit() action. When only the pg.ImageView() was loaded - I had no core dump issue. The segfault issue came just after adding the QueryBuilder() code.
Adding, into the quit method, a call to close the QueryBuilder() widget fixed the issue
from querybuilder import QueryBuilder # widgets with pyqtgraph
class MapDisplay():
...
self.querybuilder = QueryBuilder()
...
def quit(self):
self.querybuilder.close()
qApp.quit()

pyqt - Frameless widget weird titlebar appears

I noticed a weird behavior when running a frameless widget in PyQt.
If I minimize it in taskbar multiple times, a Windows XP title bar appears in the top left corner during a few milliseconds and then disappears.
Here is a simple code to reproduce the problem :
import sys
from PyQt5 import QtCore, QtWidgets
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ui = QtWidgets.QWidget()
ui.setWindowFlags(ui.windowFlags() | QtCore.Qt.FramelessWindowHint)
ui.show()
sys.exit(app.exec_())
The behavior is described in this video
My setup is Windows 7 (x64), Python 3.5 and PyQt5.7
(FYI, the problem was also present in PyQt5.6)
Can anyone explain this behavior and give a solution ?
I reported the issue to Qt and it seems to be a general Windows bug :
Sergio Martins added a comment
I can reproduce this problem with a pure Windows example, (passing WS_VISIBLE | WS_POPUP | WS_SYSMENU | WS_MINIMIZEBOX to CreateWindowEx()).
Doesn't seem fixable, other than removing the minimize button capability.
If anyone still has problems with it:
make the window translucent
create a widget that u can use as a window
Example:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.width = 400
self.height = 220
self.initUI()
def initUI(self):
self.setWindowFlags(Qt.FramelessWindowHint)
self.setFixedSize(self.width, self.height)
self.setAttribute(Qt.WA_TranslucentBackground)
self.window = QtWidgets.QWidget(self)
self.window.setStyleSheet("QWidget{background-color: #ffffff;}")
self.window.setGeometry(0, 0, self.width, self.height)
self.minimize_button = QtWidgets.QPushButton("🗕", self.window)
self.minimize_button.setGeometry(355, 2, 20, 20)
self.minimize_button.clicked.connect(self.minimize_window)
def minimize_window(self):
self.setWindowState(QtCore.Qt.WindowMinimized)
Now the title bar does not appear anymore, due to the window being invisible.

How to remember last geometry of PyQt application?

I am using PyQt5 5.5.1 (64-bit) with Python 3.4.0 (64-bit) on Windows 8.1
64-bit.
I am having trouble restoring the position and size (geometry) of my
very simple PyQt app.
Here is minimal working application:
import sys
from PyQt5.QtWidgets import QApplication, QWidget
class myApp(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
view = myApp()
sys.exit(app.exec())
What I read online is that this is the default behavior and we need to
use QSettings to save and retrieve settings from Windows registry,
which is stored in
\\HKEY_CURRENT_USER\Software\{CompanyName}\{AppName}\
Here are some of the links I read.
I could have followed those tutorials but those tutorials/docs were
written for C++ users.
C++ is not my glass of beer, and converting those codes are impossible to me.
Related:
QSettings(): How to save to current working directory
This should do.
import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import QSettings, QPoint, QSize
class myApp(QWidget):
def __init__(self):
super(myApp, self).__init__()
self.settings = QSettings( 'My company', 'myApp')
# Initial window size/pos last saved. Use default values for first time
self.resize(self.settings.value("size", QSize(270, 225)))
self.move(self.settings.value("pos", QPoint(50, 50)))
def closeEvent(self, e):
# Write window size and position to config file
self.settings.setValue("size", self.size())
self.settings.setValue("pos", self.pos())
e.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
frame = myApp()
frame.show()
app.exec_()
I simplified this example: QSettings(): How to save to current working directory
Similar to #Valentin's response, because I feel settings are being written to registry, which will be issue for cross compatiblity. Here is the relevant startEvent() and closeEvent() for the job.
def startEvent()
self.settings = QSettings(QSettings.IniFormat,QSettings.SystemScope, '__MyBiz', '__settings')
self.settings.setFallbacksEnabled(False) # File only, not registry or or.
# setPath() to try to save to current working directory
self.settings.setPath(QSettings.IniFormat,QSettings.SystemScope, './__settings.ini')
# Initial window size/pos last saved
self.resize(self.settings.value("size", QSize(270, 225)))
self.move(self.settings.value("pos", QPoint(50, 50)))
self.tab = QWidget()
def closeEvent(self, e):
# Write window size and position to config file
self.settings.setValue("size", self.size())
self.settings.setValue("pos", self.pos())
startEvent() should be initiated at startup and closeEvent() should be taken care before quitting the main window.
You should indeed use QSetting for this.
All the Qt examples have been converted to Python. They are included in the source packages of PyQt (or PySide), which you can download here
You can also look online in the github repo, particularly in application.py of mainwindows example.
def readSettings(self):
settings = QSettings("Trolltech", "Application Example")
pos = settings.value("pos", QPoint(200, 200))
size = settings.value("size", QSize(400, 400))
self.resize(size)
self.move(pos)
def writeSettings(self):
settings = QSettings("Trolltech", "Application Example")
settings.setValue("pos", self.pos())
settings.setValue("size", self.size())
Fire writeSettings() before quitting and initiate readSettings() on startup.
In my case I use .ini files to store settings (language, default user, ...). the same code works on both Debian and Windows.
An example:
from PySide.QtCore import QSettings
self.settings = QSettings('settings.ini', QSettings.IniFormat)
...
self.settings.setValue('size', self.size())

Qt QGraphicsDropShadowEffect is not showing

I am creating a custom widget my_widget inheriting from QWidget.
Here, I have a label to which I would like to apply QGraphicsDropShadowEffect however it does not seem to be working since I don't see any shadows.
My code is in Python and it's:
eff = QGraphicsDropShadowEffect()
self.my_widget_label.setGraphicsEffect(eff)
I tried various alterations to this code to no avail.
After doing a through search on Google, I came across many similar questions without answers.
What might be the cause? How can I get the shadow?
Works for me in C++. I did the following in a QDialog containing a QLabel object named titleLabel. I'm using Qt 4.8.4 on a Windows XP computer.
QGraphicsDropShadowEffect* eff = new QGraphicsDropShadowEffect(this);
eff->setBlurRadius(5);
titleLabel->setGraphicsEffect(eff);
See if this works for you:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class testShadow(QWidget):
def __init__(self, parent=None):
super(testShadow, self).__init__(parent)
self.resize(94, 35)
self.verticalLayout = QVBoxLayout(self)
self.verticalLayout.setObjectName("verticalLayout")
self.label = QLabel(self)
self.label.setText("Text Label")
self.shadow = QGraphicsDropShadowEffect(self)
self.shadow.setBlurRadius(5)
self.label.setGraphicsEffect(self.shadow)
self.verticalLayout.addWidget(self.label)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
main = testShadow()
main.show()
sys.exit(app.exec_())
I have only every tried to use this (and used it successfully) in QGraphicsScene situations. This works for me, while trying to set it on a normal QWidget actually crashes the entire application:
from PyQt4 import QtGui
class Graphics(QtGui.QWidget):
def __init__(self):
super(Graphics, self).__init__()
layout = QtGui.QVBoxLayout(self)
layout.setMargin(0)
shad = QtGui.QGraphicsDropShadowEffect(self)
shad.setBlurRadius(5)
self.scene = QtGui.QGraphicsScene(self)
self.view = QtGui.QGraphicsView(self)
self.view.setScene(self.scene)
text = self.scene.addText("Drop Shadow!")
text.setGraphicsEffect(shad)
layout.addWidget(self.view)
if __name__ == "__main__":
app = QtGui.QApplication([])
main = Graphics()
main.show()
main.raise_()
app.exec_()

pyqt Window similar to Win32 style

I'm trying to port a Win32 app to Python using pyqt for the GUI, but I can't seem to get a simple window with a text label and edit field such as the following simple Win32 style (basically WS_EX_CLIENTEDGE):
I played with setFrameStyle (ie using different styles and sunken - and then for a good measure all other sensible combinations) of the two widgets and used setContentsMargins() to zero to get it to fill all the space, but the qt window still looks quite different with regard to the border.
I get pretty close with the following (using QtGui.QFrame.WinPanel):
import sys
from PySide import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
label = QtGui.QLabel("Test")
label.setAlignment(QtCore.Qt.AlignCenter)
label.setFrameStyle(QtGui.QFrame.WinPanel | QtGui.QFrame.Sunken)
edit = QtGui.QTextEdit()
edit.setFrameStyle(QtGui.QFrame.WinPanel | QtGui.QFrame.Sunken)
edit.setText("Some text")
edit.moveCursor(QtGui.QTextCursor.MoveOperation.End)
vbox = QtGui.QVBoxLayout()
vbox.setContentsMargins(1, 1, 1, 1)
vbox.setSpacing(1)
vbox.addWidget(label)
vbox.addWidget(edit)
self.setLayout(vbox)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('Window Title')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
QFrame docs has an excellent overview of different frame styles.
To get closer than setFrameStyle allows, you need to paint you own widgets/panels, or use something other than QT.
wxPython
pywin32
You are probably not using the right style. Have a look at the documentation for QStyle.

Categories