I am trying to create a kivy application with live graph using 'garden graph'. There seems a bug which offset the screen to left while updating the graph. What could be the possible reason?
graph.py
from kivy.config import Config
Config.set('graphics', 'width', '1024')
Config.set('graphics', 'height', '600')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.clock import Clock, mainthread
from kivy.uix.screenmanager import ScreenManager, Screen
"""
Graph References:
https://github.com/kivy-garden/graph
https://stackoverflow.com/questions/30821127/using-kivy-garden-graph-in-kv-language#answer-46522799
"""
from math import sin
import random
from kivy_garden.graph import Graph, MeshLinePlot
"""
Class to manage Testing Screen
"""
class WindowGraph(Screen, Widget):
def __init__(self, **kw):
super().__init__(**kw)
self.graph = None
self.plot = None
self.x = 101
self.generate_graph()
# Clock.schedule_interval(self.update_graph, 1/10)
#mainthread
def generate_graph(self):
self.graph = self.ids['graph_test']
self.plot = MeshLinePlot(color=[1, 0, 0, 1])
# self.plot.points = [(x, sin(x / 10.)) for x in range(0, 101)]
self.graph.add_plot(self.plot)
#mainthread
def update_graph(self, *args):
self.plot.points.append((self.x, random.random()))
self.graph.xmin = max(self.x - 50, 0)
self.graph.xmax = self.x + 1
self.x += 10
def control_graph(self):
self.update_graph()
"""
Class to manage all screens
"""
class WindowManager(ScreenManager):
pass
# Load kv builder file
kv = Builder.load_file("graph.kv")
class CTControlApp(App):
def build(self):
Window.clearcolor = (205/255, 205/255, 205/255, 1)
return kv
CTControlApp().run()
graph.kv
WindowManager:
WindowGraph:
<WindowGraph>
name: "layout_graph"
FloatLayout:
size: root.width, root.height
canvas.before :
Color:
rgba: 1, 1, 1, 1
Line:
width: 0.5
rectangle: self.x, self.y, self.width, self.height
Label:
text: "TESTING"
font_size : 22
pos_hint : {"x":0.01369*14, "y":0.03448*27}
size_hint:0.01369*45, 0.03448*2
background_color :1, 1, 1, 0
color : 1, 1, 1, 1
Graph:
id: graph_test
xlabel:'X'
ylabel:'Y'
x_ticks_minor:5
x_tics_major:25
y_ticks_major:1
y_grid_label:True
x_grid_label:True
padding:5
x_grid:True
y_grid:True
xmin:-0
xmax:100
ymin:-1
ymax:1
pos_hint: {"x":0.01369*2, "y":0.03448*2}
size_hint: 0.01369*56, 0.03448*25
Button :
id: button_graph_control
text: "UPDATE"
on_press: root.control_graph()
font_size : 24
pos_hint : {"x":0.01369*60, "y":0.03448*10}
size_hint:0.01369*10, 0.03448*3
background_color :0.5, 0.5, 0.5, 0.3
color : 1, 1, 1, 1
Screen before updating graph
Screen after updating graph
Please help me resolve this issue dear friends
Related
I have an application built on pyqt6 and wanted to add a feature ( displaying a panorama image),
so I found this code on the internet to display panorama image using pyqt5.
can someone help me convert it to pyqt6?
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Panoramic(QtWidgets.QWidget):
def __init__(self, imagePath):
QtWidgets.QWidget.__init__(self)
self.setCursor(QtCore.Qt.CrossCursor)
# keep a reference of the original image
self.source = QtGui.QPixmap(imagePath)
self.pano = QtGui.QPixmap(self.source.width() * 3, self.source.height())
self.center = self.pano.rect().center()
# use a QPointF for precision
self.delta = QtCore.QPointF()
self.deltaTimer = QtCore.QTimer(interval=25, timeout=self.moveCenter)
self.sourceRect = QtCore.QRect()
# create a pixmap with three copies of the source;
# this could be avoided by smart repainting and translation of the source
# but since paintEvent automatically clips the painting, it should be
# faster then computing the new rectangle each paint cycle, at the cost
# of a few megabytes of memory.
self.setMaximumSize(self.source.size())
qp = QtGui.QPainter(self.pano)
qp.drawPixmap(0, 0, self.source)
qp.drawPixmap(self.source.width(), 0, self.source)
qp.drawPixmap(self.source.width() * 2, 0, self.source)
qp.end()
def moveCenter(self):
if not self.delta:
return
self.center += self.delta
# limit the vertical position
if self.center.y() < self.sourceRect.height() * .5:
self.center.setY(self.sourceRect.height() * .5)
elif self.center.y() > self.source.height() - self.height() * .5:
self.center.setY(self.source.height() - self.height() * .5)
# reset the horizontal position if beyond the center of the virtual image
if self.center.x() < self.source.width() * .5:
self.center.setX(self.source.width() * 1.5)
elif self.center.x() > self.source.width() * 2.5:
self.center.setX(self.source.width() * 1.5)
self.sourceRect.moveCenter(self.center.toPoint())
self.update()
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.mousePos = event.pos()
def mouseMoveEvent(self, event):
if event.buttons() != QtCore.Qt.LeftButton:
return
delta = event.pos() - self.mousePos
# use a fraction to get small movements, and ensure we're not too fast
self.delta.setX(max(-25, min(25, delta.x() * .125)))
self.delta.setY(max(-25, min(25, delta.y() * .125)))
if not self.deltaTimer.isActive():
self.deltaTimer.start()
def mouseReleaseEvent(self, event):
self.deltaTimer.stop()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.drawPixmap(self.rect(), self.pano, self.sourceRect)
# resize and reposition the coordinates whenever the window is resized
def resizeEvent(self, event):
self.sourceRect.setSize(self.size())
self.sourceRect.moveCenter(self.center)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Panoramic('pano5.jpg')
w.show()
sys.exit(app.exec_())
Here is what I tried to do.
changed QtCore.Qt.CrossCursor to QtCore.Qt.CursorShape.CrossCursor
changed QtCore.Qt.LeftButton to QtCore.Qt.MouseButton.LeftButton
changed sys.exit(app.exec_()) to sys.exit(app.exec())
now I reached the point of getting this error
'line 32, in moveCenter
self.center += self.delta
TypeError: unsupported operand type(s) for +=: 'QPoint' and 'QPointF''
and could not find a way to work around it in pyqt6
Here are the changes that solved my issue:
Changed self.center = QPointF(self.pano.rect().center())) to self.center += self.delta.toPoint()).
and changed self.sourceRect.moveCenter(self.center.toPoint()) to self.sourceRect.moveCenter(self.center).
Now the code is successfully converted from pyqt5 to pyqt6.
PyQt6:
On Win10 Vertical header has only line numbers no lines.
On Linux KDE has both.
Did I miss something or bug?
I'm not able to test on MAC.
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableView
from PyQt6.QtCore import Qt, QAbstractTableModel
class TableModel(QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role):
if role == Qt.ItemDataRole.DisplayRole:
value = self._data[index.row()][index.column()]
return value
def rowCount(self, index):
# The length of the outer list.
return len(self._data)
def columnCount(self, index):
# The following takes the first sub-list, and returns
# the length (only works if all rows are an equal length)
return len(self._data[0])
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.table = QTableView()
data = [[7, 9, 2],
[1, -1, -1],
[3, 5.3, -5],
[3, 3, 2],
[7, 8, 23],]
self.model = TableModel(data)
self.table.setModel(self.model)
self.setCentralWidget(self.table)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
How do I synchronize the selection in the 2 charts below? Also, how do I get the bounds of the box selection?
import holoviews as hv
import hvplot.pandas
from bokeh.sampledata.autompg import autompg
hv.extension('bokeh')
hv.Layout([autompg.hvplot.scatter(x='mpg', y='yr', tools=['box_select']), autompg.hvplot.scatter(x='mpg', y='yr', tools=['box_select'])]).cols(1)
The linked selection is easy:
import holoviews as hv
import hvplot.pandas
from bokeh.sampledata.autompg import autompg
hv.extension('bokeh')
from holoviews.plotting.links import DataLink
a = autompg.hvplot.scatter(x='mpg', y='yr', tools=['box_select'])
b = autompg.hvplot.scatter(x='mpg', y='yr', tools=['box_select'])
DataLink(a, b)
hv.Layout([a, b]).cols(1)
Documentation: https://www.holoviews.org/user_guide/Linking_Plots.html
Now for retrieving the bounds. You can use BoundsXY for that:
import numpy as np
import holoviews as hv
from holoviews.streams import BoundsXY
data = np.random.multivariate_normal((0, 0), [[1, 0.1], [0.1, 1]], (1000,))
points = hv.Points(data).opts(tools=['box_select'])
sel = BoundsXY(source=points)
def cog(bounds):
'Center of gravity'
if bounds is None:
bounds=(0, 0, 0, 0)
index = points.dframe().x.between(*bounds[::2]) & points.dframe().y.between(*bounds[1::2])
x = points.dframe().loc[index, 'x'].mean() if index.any() else []
y = points.dframe().loc[index, 'y'].mean() if index.any() else []
return hv.Points((x, y)).opts(size=10)
mean_sel = hv.DynamicMap(cog, kdims=[], streams=[sel])
points * mean_sel
(modelled on http://holoviews.org/reference/apps/bokeh/selection_stream.html)
I am trying to force my QTextEdit widget to span the whole cell of the QGridLayout.
I tried a lot of different combinations of QSizePolicy and sizeHint(), but nothing has the desired effect.
In the example below I have a main widget of minimum size 800x600 and another widget 'blue' of size 100x100 in column 1. So I want the QTextEdit in column 0 to be 700x600.
In general I want 'edit' to be (n-blue.width())xm if my main widget is resized to nxm.
import sys
from PyQt5 import QtWidgets, QtCore
if __name__ == '__main__':
qApp = QtWidgets.QApplication(sys.argv)
mainWidget = QtWidgets.QWidget()
mainWidget.setMinimumSize(800, 600)
mainLayout = QtWidgets.QGridLayout(mainWidget)
blue = QtWidgets.QWidget(mainWidget)
blue.setStyleSheet('background-color: blue')
blue.setFixedSize(100, 100)
edit = QtWidgets.QTextEdit(mainWidget)
### what to do here?
policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
edit.setSizePolicy(policy)
###
mainLayout.addWidget(edit, 0, 0, 1, 1, QtCore.Qt.AlignCenter)
mainLayout.addWidget(blue, 0, 1, 1, 1, QtCore.Qt.AlignCenter)
mainLayout.setColumnStretch(0, 1)
mainLayout.setColumnStretch(1, 0)
mainWidget.setLayout(mainLayout)
mainWidget.show()
sys.exit(qApp.exec_())
I think you're just placing too many constraints on the QGridLayout. Try adding the widgets to the layout with just...
mainLayout.addWidget(edit, 0, 0)
mainLayout.addWidget(blue, 0, 1)
Also, you probably don't need to set the sizing policy explicitly -- it should just work as-is (it does for me).
The full, minimal working example would be something like...
import sys
from PyQt5 import QtWidgets, QtCore
if __name__ == '__main__':
qApp = QtWidgets.QApplication(sys.argv)
mainWidget = QtWidgets.QWidget()
mainWidget.setMinimumSize(800, 600)
mainLayout = QtWidgets.QGridLayout(mainWidget)
blue = QtWidgets.QWidget(mainWidget)
blue.setStyleSheet('background-color: blue')
blue.setFixedSize(100, 100)
edit = QtWidgets.QTextEdit(mainWidget)
mainLayout.addWidget(edit, 0, 0)
mainLayout.addWidget(blue, 0, 1)
mainWidget.show()
sys.exit(qApp.exec_())
I am trying to create this geometery:
_ ___
| | |
|1| 3 |
|_|___|
|_2_|
Where the 1 box is tall and skinny, the 2 box is short and wide. I can't quite get the layout correct. When I cange the rowSpan and columnSpan for 1 and 2 from 0 to 1 I see the bottom box in correct relative location but height is wrong, and width of box 1 is wrong.
This so qq helped but didn't fully fix my problem: How to arrange the items in QGridLayout as shown?
import sys
from PyQt4 import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
textEdit1 = QtGui.QTextEdit("LHS rectangle")
textEdit2 = QtGui.QTextEdit("Bottom rectangle")
textEdit3 = QtGui.QTextEdit("Central square")
self.gridLayout = QtGui.QGridLayout()
self.gridLayout.addWidget(textEdit1, 0, 0, 2, 0)
self.gridLayout.addWidget(textEdit2, 2, 1, 0, 2)
self.gridLayout.addWidget(textEdit3, 0, 1, 2, 2)
self.setLayout(self.gridLayout)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You need to set an appropriate stretch factor to change the amount of space given to each row/column:
self.gridLayout.addWidget(textEdit1, 0, 0)
self.gridLayout.addWidget(textEdit2, 1, 1)
self.gridLayout.addWidget(textEdit3, 0, 1)
self.gridLayout.setColumnStretch(0, 1)
self.gridLayout.setColumnStretch(1, 3)
self.gridLayout.setRowStretch(0, 3)
self.gridLayout.setRowStretch(1, 1)
Result: