Each character in text is cut off - python-3.x

When trying to plot labels in my application, the right side of each character in the text is cut off by a few pixels. Disabling opengl seems to fix the issue but I need opengl enabled for the application. Setting windows scaling to 125% seems to minimize/make this clipping issue go away, but there must be a better solution to this.Each character in the text is cut off on the right
Using pyside6 6.3.1, pyopengl 3.1.6, pyqtgraph 0.12.4
import os
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
import pyqtgraph as pg
from pyqtgraph import GraphicsLayoutWidget
from PySide6.QtCore import Qt, QPointF
class PlotCanvas(GraphicsLayoutWidget):
def __init__(self):
pg.setConfigOption("antialias", True)
pg.setConfigOption("useOpenGL", True)
pg.setConfigOption("enableExperimental", False)
pg.setConfigOption("background", "w")
pg.setConfigOption("foreground", "k")
super().__init__()
self.mainplot = self.addPlot()
self.setup_fig()
def setup_fig(self):
""" Setup an initial plot.
"""
self.mainplot.setRange(
xRange=(-10, 1000), yRange=(-10, 2500), disableAutoRange=True
)
self.mainplot.setLabel("left", "Money", units="$")
self.mainplot.setLabel("bottom", "Time", units="s")
self.mousept = pg.TextItem(anchor=(0, 0), border=None)
self.mainplot.addItem(self.mousept)
pg.SignalProxy(
self.mainplot.scene().sigMouseMoved,
rateLimit=30,
slot=self.mouse_moved_update,
)
self.mainplot.scene().sigMouseMoved.connect(self.mouse_moved_update)
def mouse_moved_update(self, evt):
"""Show coordinate by the mousepointer and update on movements.
"""
mouse_point = self.mainplot.vb.mapSceneToView(evt)
plot_point = self.mainplot.vb.mapSceneToView(QPointF(evt.x() + 15, evt.y()))
self.mousept.setHtml(
"<span style='font-size: 10pt; color: %s; border-style: solid'>"
"x:%0.1f, y:%0.1f</span>" % ('black', mouse_point.x(), mouse_point.y())
)
self.mousept.setPos(plot_point.x(), plot_point.y())
def main():
os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1"
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
app = QApplication([])
window = QMainWindow()
window.setCentralWidget(PlotCanvas())
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Related

Order of placement of widgets in PyQt: Can I place a GraphicalLayoutWidget on a QHBoxLayout?

I'm new to PyQt and pyqtgraph and have a heatplot, which I'd like to place on a widget (if that's the right term), adjacent to which a slider will eventually appear. The code I have thus far, which is a modification of something I've shamelessly copied from an online tutorial, is as follows:
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets, mkQApp
from PyQt5.QtWidgets import QHBoxLayout
from pyqtgraph.Qt import QtGui, QtCore
pg.setConfigOption('background', 'lightgray')
pg.setConfigOption('foreground','black')
font = QtGui.QFont("Times", 18)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
layout = QHBoxLayout()
graph_widget = pg.GraphicsLayoutWidget(show=True)
self.setCentralWidget(graph_widget)
self.setWindowTitle('pyqtgraph example: heatmap display')
self.setStyleSheet("background-color: lightgray;")
self.resize(1000,1000)
#layout.addWidget(graph_widget) # Where my error occurs
self.show()
corrMatrix = np.array([
[ 1. , 0.5184571 , -0.70188642],
[ 0.5184571 , 1. , -0.86094096],
[-0.70188642, -0.86094096, 1. ]
])
columns = ["A", "B", "C"]
pg.setConfigOption('imageAxisOrder', 'row-major')
correlogram = pg.ImageItem()
tr = QtGui.QTransform().translate(-0.5, -0.5)
correlogram.setTransform(tr)
correlogram.setImage(corrMatrix)
plotItem = graph_widget.addPlot()
plotItem.invertY(True)
plotItem.setDefaultPadding(0.0)
plotItem.addItem(correlogram)
plotItem.showAxes( True, showValues=(True, True, False, False), size=40 )
ticks = [ (idx, label) for idx, label in enumerate( columns ) ]
for side in ('left','top','right','bottom'):
plotItem.getAxis(side).setTicks( (ticks, []) )
plotItem.getAxis(side).setTickFont(font)
plotItem.getAxis('bottom').setHeight(10)
colorMap = pg.colormap.get("CET-D1")
bar = pg.ColorBarItem( interactive=False,values=(0,1), colorMap=colorMap)
bar.setImageItem(correlogram, insert_in=plotItem)
mkQApp("Correlation matrix display")
main_window = MainWindow()
if __name__ == '__main__':
pg.exec()
The result is shown below:
Eventually I would like to place the above in a layout, in which a row contains my plot, a slider (and a few other widgets). A TypeError message results when I un-comment the line layout.addWidget(graph_widget). The message states
TypeError: addWidget(self, QWidget, stretch: int = 0, alignment: Union[Qt.Alignment,
Qt.AlignmentFlag] = Qt.Alignment()): argument 1 has unexpected type
'GraphicsLayoutWidget'
Is it not possible to place a GraphicsLayoutWidget on a QHBoxLayout()? If so, what's the correct way to organize things so I have my graph adjacent to which I can place sliders, line edits, etc.
One issue is that you are setting your graph_widget as the central widget for your QMainWindow instance and then later adding it to a layout with the intention of adding more widgets.
I think you are more likely to achieve the results you are looking for if you set a generic QWidget as the window's central widget, set a layout, and then add the graph_widget and any other widgets to the layout.
Here is an example using your code and the solution from #musicmante in the comments, and adding a vertical slider:
from PyQt5.QtWidgets import QHBoxLayout, QWidget, QSlider # Import PyQt5 first before pyqtgraph
from pyqtgraph.Qt import QtWidgets, mkQApp
import pyqtgraph as pg
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
pg.setConfigOption('background', 'lightgray')
pg.setConfigOption('foreground','black')
font = QtGui.QFont("Times", 18)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.central = QWidget() # create a QWidget
slider = QSlider(orientation=QtCore.Qt.Vertical) # Vertical Slider
graph_widget = pg.GraphicsLayoutWidget(show=True)
self.setWindowTitle('pyqtgraph example: heatmap display')
self.setStyleSheet("background-color: lightgray;")
self.resize(1000,1000)
self.setCentralWidget(self.central) # set the QWidget as centralWidget
layout = QHBoxLayout(self.central) # assign layout to central widget
layout.addWidget(graph_widget) # No More error
layout.addWidget(slider) # add a slider
self.show()
corrMatrix = np.array([
[ 1. , 0.5184571 , -0.70188642],
[ 0.5184571 , 1. , -0.86094096],
[-0.70188642, -0.86094096, 1. ]
])
columns = ["A", "B", "C"]
pg.setConfigOption('imageAxisOrder', 'row-major')
correlogram = pg.ImageItem()
tr = QtGui.QTransform().translate(-0.5, -0.5)
correlogram.setTransform(tr)
correlogram.setImage(corrMatrix)
plotItem = graph_widget.addPlot()
plotItem.invertY(True)
plotItem.setDefaultPadding(0.0)
plotItem.addItem(correlogram)
plotItem.showAxes( True, showValues=(True, True, False, False), size=40 )
ticks = [ (idx, label) for idx, label in enumerate( columns ) ]
for side in ('left','top','right','bottom'):
plotItem.getAxis(side).setTicks( (ticks, []) )
plotItem.getAxis(side).setTickFont(font)
plotItem.getAxis('bottom').setHeight(10)
colorMap = pg.colormap.get("CET-D1")
bar = pg.ColorBarItem( interactive=False,values=(0,1), colorMap=colorMap)
bar.setImageItem(correlogram, insert_in=plotItem)
mkQApp("Correlation matrix display")
main_window = MainWindow()
if __name__ == '__main__':
pg.exec()

PyQt5 fullscreen unwanted behavior on ubuntu

I'm running a pyqt5 application on an ubuntu and when the app is in full screen mode, I get this behavior of the dock and title bar popping over the application when a dialog box is clicked. When the dialog box is closed, everything goes back to normal. This issue doesn't happen on my intel but happens on ARM64. My system is the Jetson AGX Xavier 32GB. I've posted a sample code below that reproduces the issue.
I saw another post that suggested in setting the window flag Qt.X11BypassWindowManagerHint or Qt.BypassWindowManagerHint, even though it came with some issues but that didn't work either.
Any help would be greatly appriciated.
from PyQt5.QtWidgets import QComboBox, QVBoxLayout, QWidget, QHBoxLayout, QApplication, QFileDialog, QPushButton, QMainWindow
from PyQt5.QtCore import Qt
import sys
import pyautogui
class TestCode(QMainWindow):
def __init__(self):
super(TestCode, self).__init__()
self.OpenDialog = QPushButton()
self.OpenDialog.setText("OPEN D BOX")
self.OpenDialog.clicked.connect(self.openDBox)
self.closeWindow = QPushButton()
self.closeWindow.setText("CLOSE")
self.closeWindow.clicked.connect(self.close)
colors = ["Yellow", "Magenta", "Black", "White",
"Green", "Blue", "Cyan", "Red"]
self.drop_down = QComboBox()
for color in colors:
self.drop_down.addItem(color)
self.buttonLayout = QVBoxLayout()
self.buttonLayout.addWidget(self.OpenDialog)
self.buttonLayout.addWidget(self.drop_down)
self.buttonLayout.addWidget(self.closeWindow)
self.mainWidget = QWidget()
self.mainLayout = QHBoxLayout(self.mainWidget)
self.freeSpace = QWidget()
self.freeSpace.setStyleSheet("background-color: black")
self.mainLayout.setSpacing(10)
self.mainLayout.setContentsMargins(20, 20, 20, 20)
self.mainLayout.addLayout(self.buttonLayout)
self.mainLayout.addWidget(self.freeSpace, 1)
self.setCentralWidget(self.mainWidget)
self.showFullScreen()
# screenWidth, screenHeight = pyautogui.size()
# self.setGeometry(0,0,screenWidth+1, screenHeight)
def openDBox(self):
openImageFile, _ = QFileDialog.getOpenFileName()
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = TestCode()
# main_window.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.BypassWindowManagerHint)
main_window.show()
sys.exit(app.exec_())

Select points from a scattr plot using mouse cross hair - PyQt

I am new to PyQt and Im developing a utility where a user can import data from an excel file and plot its X and Y in a 2d scatter plot using below code:
def plot_2d_scatter(graphWidget,x,z,color=(66, 245, 72)):
graphWidget.clear()
brush = pg.mkBrush(color)
scatter = pg.ScatterPlotItem(size=5, brush=brush)
scatter.addPoints(x,z)
graphWidget.addItem(scatter)
Now I want a functionality which will allow the user to move his mouse over the scatter plot points using a cross hair / pointer / etc and select points on the scatter plot.
Whenever the user does a left click on the crosshair / marker on the scatter plot, I want its x,y coordinates to be saved for further use.
I have already tried the below snippet from somewhere on internet for using mouse events and getting my scatter points , but this didnt give me a cross hair that falls on my scatter points
def mouseMoved(self, evt):
pos = evt
if self.plotWidget.sceneBoundingRect().contains(pos):
mousePoint = self.plotWidget.plotItem.vb.mapSceneToView(pos)
mx = np.array([abs(float(i) - float(mousePoint.x())) for i in self.plotx])
index = mx.argmin()
if index >= 0 and index < len(self.plotx):
self.cursorlabel.setHtml(
"<span style='font-size: 12pt'>x={:0.1f}, \
<span style='color: red'>y={:0.1f}</span>".format(
self.plotx[index], self.ploty[index])
)
self.vLine.setPos(self.plotx[index])
self.hLine.setPos(self.ploty[index])
Any guidance is thankfully appreciated
my best fast effort, never used pg untill today:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QDesktopWidget, QWidget
from PyQt5.QtCore import Qt
from PyQt5 import QtGui, QtCore, QtWidgets
import pyqtgraph as pg
import numpy as np
class MyApp(QMainWindow):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
self.resize(781, 523)
self.graphWidget = pg.PlotWidget()
self.setCentralWidget(self.graphWidget)
self.show()
self.x = [1,2,3,4,5,6,7,8,9,5,6,7,8]
self.y = [1,2,3,4,5,6,7,8,9,5,6,7,8]
# self.y.reverse()
self.plot_2d_scatter(self.graphWidget, self.x, self.y)
self.cursor = Qt.CrossCursor
# self.cursor = Qt.BlankCursor
self.graphWidget.setCursor(self.cursor)
# Add crosshair lines.
self.crosshair_v = pg.InfiniteLine(angle=90, movable=False)
self.crosshair_h = pg.InfiniteLine(angle=0, movable=False)
self.graphWidget.addItem(self.crosshair_v, ignoreBounds=True)
self.graphWidget.addItem(self.crosshair_h, ignoreBounds=True)
self.cursorlabel = pg.TextItem()
self.graphWidget.addItem(self.cursorlabel)
self.proxy = pg.SignalProxy(self.graphWidget.scene().sigMouseMoved, rateLimit=60, slot=self.update_crosshair)
self.mouse_x = None
self.mouse_y = None
def plot_2d_scatter(self,graphWidget,x,z,color=(66, 245, 72)):
# graphWidget.clear()
brush = pg.mkBrush(color)
scatter = pg.ScatterPlotItem(size=5, brush=brush)
scatter.addPoints(x,z)
graphWidget.addItem(scatter)
def update_crosshair(self, e):
pos = e[0]
if self.graphWidget.sceneBoundingRect().contains(pos):
mousePoint = self.graphWidget.plotItem.vb.mapSceneToView(pos)
mx = np.array([abs(float(i) - float(mousePoint.x())) for i in self.x])
index = mx.argmin()
if index >= 0 and index < len(self.x):
self.cursorlabel.setText(
str((self.x[index], self.y[index])))
self.crosshair_v.setPos(self.x[index])
self.crosshair_h.setPos(self.y[index])
self.mouse_x = self.crosshair_v.setPos(self.x[index])
self.mouse_y = self.crosshair_h.setPos(self.y[index])
self.mouse_x = (self.x[index])
self.mouse_y = (self.y[index])
def mousePressEvent(self, e):
if e.buttons() & QtCore.Qt.LeftButton:
print('pressed')
# if self.mouse_x in self.x and self.mouse_y in self.y:
print(self.mouse_x, self.mouse_y)
if __name__ == '__main__':
app = QApplication(sys.argv)
myapp = MyApp()
# myapp.show()
try:
sys.exit(app.exec_())
except SystemExit:
print('Closing Window...')
it just prints out the coordinate of pressed point in graph
copied from https://www.pythonguis.com/faq/pyqt-show-custom-cursor-pyqtgraph/ and your piece of code result looks like:
there are other examples on SO like Trying to get cursor with coordinate display within a pyqtgraph plotwidget in PyQt5 and others

PySide2 How to make a transparent widget background

I'm writing a widget that should have a transparent background.
Unfortunately, I get a black background instead of transparent.
I've used attributes like:
WA_TranslucentBackground
WA_NoSystemBackground
and also flags:
Qt.FramelessWindowHint
Qt.X11BypassWindowManagerHint
my system using KWin windows manager which is based on x.org
Here's a quick example of my widget:
from PySide2.QtWidgets import *
from PySide2.QtCore import *
from PySide2.QtGui import *
import sys
from psutil import cpu_percent
class MyWidget(QWidget):
def __init__(self, parent=None):
super().__init__()
self.resize(600, 600)
self.dark = "#3B3A44"
self.light = "#4a4953"
self.color = "#75ECB5"
self.setAttribute(Qt.WA_TranslucentBackground, True)
self.setAttribute(Qt.WA_NoSystemBackground, True)
self.setStyleSheet("background:transparent;")
# self.setAttribute(QtCore.Qt.WA_PaintOnScreen)
self.setWindowFlags(Qt.FramelessWindowHint |
Qt.X11BypassWindowManagerHint |
Qt.Tool)
def paintEvent(self, event: QPaintEvent):
""" Dessine l'horloge """
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
base_rect = self.rect().adjusted(20, 20, -20, -20)
painter.setBrush(QBrush(QColor(self.dark)))
painter.drawEllipse(base_rect)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.move(800, 300)
w.show()
app.exec_()
Check if translucency is enable on your KDE systemsettings.
Also you can install other tools like xcompmgr and execute it before starting your app.

QGraphicsview + scene + QGroupBox movement issue

For past few days I was trying to solve the issue of widget movement. At some point I tried rewriting QComboBox classes with mouse signals but that did not work. As a work around I settled for parenting my widget to a QGraphicsWidget but once I try to add another item it does not display any more and I'm not sure what to do. Here is full test script:
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import QApplication,QGraphicsItem, QGraphicsView, QGraphicsScene, QDesktopWidget, QCheckBox, QGroupBox, QPushButton, QGridLayout, QLabel, QLineEdit, QComboBox, QFont, QRadioButton, QButtonGroup, QWidget, QShortcut, QKeySequence, QIcon, QListView, QStandardItemModel, QStandardItem, QAction, QIntValidator, QListWidget, QProgressBar, QSpacerItem
from PyQt4.QtCore import QRect
from functools import partial
import sys
class node_GUI(QtGui.QWidget):
def __init__(self):
super(node_GUI, self).__init__()
class Main(QtGui.QMainWindow):
def __init__(self, *args):
super(Main, self).__init__(*args)#QtGui.QMainWindow.__init__(self)
self.init_defaults()
def init_defaults(self):
self.setGeometry(800,800,500,200)
self.lay_main = QGridLayout()
self.centralwidget = QtGui.QWidget()
self.centralwidget.setLayout(self.lay_main)
self.setCentralWidget(self.centralwidget)
btn_create_node = QPushButton("Create Node View")
btn_create_node.clicked.connect(self.create_node_view)
self.lay_main.addWidget(btn_create_node)
def showWindow(self,window):
window.show()
def printTest(self):
print "Start"
box = QGroupBox("subWidget")
box_btn = QPushButton("Test")
box_btn.clicked.connect(self.printTest)
le_edit = QLineEdit()
lay = QGridLayout()
box.setLayout(lay)
lay.addWidget(box_btn)
lay.addWidget(le_edit)
area = QtGui.QGraphicsWidget()
area.setMinimumSize(QtCore.QSizeF(400,300))
area.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
area.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
proxy = self.scene.addWidget(box)
proxy.setParentItem(area)
print "END"
def create_node_view(self):
print "creting node view"
window = node_GUI()
window.setGeometry(QRect(100, 100, 400, 200))
window.setWindowTitle("node ")
window.setObjectName("node")
show_window = QPushButton("Show Node Editor")
show_window.setObjectName("btn")
show_window.clicked.connect(partial(self.showWindow,window))
self.lay_main.addWidget(show_window)
box = QGroupBox("Widgets")
box_btn = QPushButton("Test")
box_btn.clicked.connect(self.printTest)
le_edit = QLineEdit()
lay = QGridLayout()
box.setLayout(lay)
lay.addWidget(box_btn)
lay.addWidget(le_edit)
area = QtGui.QGraphicsWidget()
area.setMinimumSize(QtCore.QSizeF(300,300))
area.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
area.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
area.setAutoFillBackground(True)
ecs = QtGui.QGraphicsEllipseItem()
ecs.setRect(QtCore.QRectF(79,79,79,79))
ecs.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
ecs.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
view = QGraphicsView()
self.scene = QGraphicsScene()
self.scene.addItem(area)
proxy = self.scene.addWidget(box)
proxy.setParentItem(area)
self.scene.addItem(ecs)
view.setScene(self.scene)
lay_window = QGridLayout()
window.setLayout(lay_window)
lay_window.addWidget(view)
def main():
app = QtGui.QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
When you click on Create Node View > Show Node Editor > Test button > a new GroupBox should appear but that does not work. Not sure why.
Right so I stopped using QGraphicsWidget() and instead I just use QGraphicsRectItem(ecs for example) once I did that change everything started to work as expected.

Resources