PyQt5 Dynamically add rectangles to QML grid - python

I have built a grid of rectangles in QML which is run from Python. engine.load('main.qml')
Window {
id: channels
Grid {
columns: 2
spacing: 9
Rectangle {
color: "#333"
width: 75
height: 75
}
Rectangle {
color: "#333"
width: 75
height: 75
}
}
}
However, I would like to have over fifty rectangles, so I need to be able to dynamically create and update them from python. How can I do that?

To provide information from python (or C++) to qml we can read this link. In it recommends to use QAbstractListModel since this notifies the changes to the qml, in addition to be added dynamically we will use Repeater.
main.qml:
import QtQuick.Window 2.2
import QtQuick 2.0
import QtQuick.Controls 1.4
Window {
visible: true
id: channels
Grid {
columns: 3
spacing: 9
Repeater{
model: myModel
delegate: Rectangle{
height: model.height
width: model.height
color: model.color
}
}
}
}
.py:
class Data(object):
def __init__(self, width=35, height=35, color=QColor("red")):
self._width = width
self._height = height
self._color = color
def width(self):
return self._width
def height(self):
return self._height
def color(self):
return self._color
class Model(QAbstractListModel):
WidthRole = Qt.UserRole + 1
HeightRole = Qt.UserRole + 2
ColorRole = Qt.UserRole + 3
_roles = {WidthRole: b"width", HeightRole: b"height", ColorRole: b"color"}
def __init__(self, parent=None):
QAbstractListModel.__init__(self, parent)
self._datas = []
def addData(self, data):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self._datas.append(data)
self.endInsertRows()
def rowCount(self, parent=QModelIndex()):
return len(self._datas)
def data(self, index, role=Qt.DisplayRole):
try:
data = self._datas[index.row()]
except IndexError:
return QVariant()
if role == self.WidthRole:
return data.width()
if role == self.HeightRole:
return data.height()
if role == self.ColorRole:
return data.color()
return QVariant()
def roleNames(self):
return self._roles
To make a test we use the following code:
main.py
if __name__ == "__main__":
import sys
QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
model = Model()
model.addData(Data(44, 33, QColor("red")))
model.addData(Data(23, 53, QColor("#333")))
context = engine.rootContext()
context.setContextProperty('myModel', model)
engine.load(QUrl.fromLocalFile("main.qml"))
if len(engine.rootObjects()) == 0:
sys.exit(-1)
qsrand(QTime.currentTime().msec())
timer = QTimer(engine)
timer.timeout.connect(lambda: model.addData(Data(20 + qrand() % 40,
20 + qrand() % 40,
QColor(qrand() % 255, qrand() % 255, qrand() % 255))))
timer.start(1000)
engine.quit.connect(app.quit)
sys.exit(app.exec_())
The complete example you find here.

Related

PySide Qt dataChanged.emit() not working but LayoutChanged.emit works

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide6.QtCore import QObject, Signal, Slot, Property, Qt,QUrl, QTimer, QAbstractListModel
class CpuLoadModel(QAbstractListModel):
def __init__(self):
QAbstractListModel.__init__(self)
self.__update_timer = QTimer(self)
self.__update_timer.setInterval(1000)
self.__update_timer.timeout.connect(self.__update)
self.__update_timer.start()
self.todos = []
def __update(self):
self.todos.append(random.randint(0, 99))
self.layoutChanged.emit()
print(self.todos)
self.dataChanged.emit(self.index,self.index,[])
def data(self, index, role):
if role == Qt.DisplayRole:
text = self.todos[index.row()]
print(f"Text: {index.row()} role : {role}")
print("--------")
return text
def rowCount(self, index):
return len(self.todos)
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
qmlRegisterType(CpuLoadModel, 'PsUtils', 1, 0, 'CpuLoadModel')
engine.load(QUrl("main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
self.dataChange.emit method is not working at all, however self.LayoutChange.emit works but with those mistakes from qml
TypeError: Cannot read property 'width' of null
The layoutChanged signal must be emitted when something in the model has changed (for example it has been reordered), and dataChanged when any of the items change data but none of them is used to indicate that a row was added, in that case it must use the beginInsertRows and endInsertRows methods.
def __update(self):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.todos.append(random.randint(0, 99))
self.endInsertRows()
Also change to:
def rowCount(self, index=QModelIndex()):
if index.isValid():
return 0
return len(self.todos)
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
import PsUtils 1.0
Window {
id: root
width: 640
height: 480
visible: true
title: "CPU Load"
ListView {
id:listviewId
anchors.fill: parent
model: CpuLoadModel { }
delegate: Rectangle {
width: listviewId.width
height: 30;
color: "white"
Rectangle {
id: bar
width: listviewId.width * display / 100.0
height: 30
color: "green"
}
Text {
anchors.verticalCenter: parent.verticalCenter
x: Math.min(bar.x + bar.width + 5, parent.width-width)
text: display + "%"
}
}
}
}
Basically the mistake was the parent.width in QML we better off to use id to refer to element instead of using parent.width ( use elementId.width)

How to add headers to qml TableView from TableModel

I need to add a header to a TableView that uses a custom model defined in Python.
I've tried overriding the headerData function in QAbstractTableModel with my custom headers. I'm following the same series of steps described in this C++ example of the same type of implementation: Header to a TableView
Unfortunately, the headers still don't show up at the top of the table. The table does however contain the data from overriding the data function in QAbstractTableModel.
Python:
class RouteTableModel(QAbstractTableModel):
def __init__(self, parent=None, *args):
super().__init__()
self._datatable = None
self._header = {
0 : 'X',
1 : 'Y',
2 : 'Z'
}
def data(self, index, role=Value):
i = index.row()
j = index.column()
if role == self.Value:
return '{0}'.format(self._datatable[i][j]['value'])
elif role == self.Selected:
return self._datatable[i][j]['selected']
else:
return None
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return self._header[section]
else:
return None
else:
return None
# create the application instance
app = QApplication(sys.argv)
# create a QML engine
engine = QQmlApplicationEngine()
# instantiate the TableModel class
xyztablemodel = RouteTableModel()
engine.rootContext().setContextProperty('XYZTableModel', xyztablemodel)
# load main QML file and start app engine
engine.load('view.qml')
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
QML:
import QtQuick 2.12
import QtQuick.Controls 2.12
GridLayout {
id: gridfpc2
flow: GridLayout.TopToBottom
rows: 4
columns: 2
rowSpacing: 20
columnSpacing: 35
TableView {
id: xyztable
Layout.rowSpan: 4
// Layout.alignment: Qt.AlignCenter
Layout.fillHeight: true
model: XYZTableModel
width: 350
delegate: CustomComp.XYZTableDelegate {
implicitWidth: parent.width / 3
implicitHeight: 20
}
}
}
No error messages occur in the Python or qml code. I'd expect the header to populate above the columns in the TableView, but they do not show up.
In the example you indicate in your question, it is from a QTableView that is very different from the TableView offered by QML. In your case you are using the TableView of QtQuick. This TableView has no headers so you must implement it, in my example I will use Repeaters. On the other hand headerData is not accessible from QML, so I will implement the Q_INVOKABLE of C++ using #Slot(), passing as an argument the result of the type of variable that returns the function:
main.py
from PySide2 import QtCore, QtGui, QtQml
class RouteTableModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None):
super().__init__(parent)
self._header = {0: "X", 1: "Y", 2: "Z"}
def columnCount(self, parent=QtCore.QModelIndex()):
return 3
def rowCount(self, parent=QtCore.QModelIndex()):
return 100
def data(self, index, role=QtCore.Qt.DisplayRole):
i = index.row()
j = index.column()
if role == QtCore.Qt.DisplayRole:
return "{}-{}".format(i, j)
#QtCore.Slot(int, QtCore.Qt.Orientation, result="QVariant")
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
return self._header[section]
else:
return str(section)
if __name__ == "__main__":
import os
import sys
app = QtGui.QGuiApplication(sys.argv)
engine = QtQml.QQmlApplicationEngine()
xyztablemodel = RouteTableModel()
engine.rootContext().setContextProperty("XYZTableModel", xyztablemodel)
current_dir = os.path.dirname(os.path.realpath(__file__))
filename = os.path.join(current_dir, "view.qml")
engine.load(QtCore.QUrl.fromLocalFile(filename))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
view.qml
import QtQuick 2.12
import QtQuick.Controls 2.4
import QtQuick.Window 2.11
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
color: '#222222'
TableView {
id: tableView
columnWidthProvider: function (column) { return 100; }
rowHeightProvider: function (column) { return 60; }
anchors.fill: parent
leftMargin: rowsHeader.implicitWidth
topMargin: columnsHeader.implicitHeight
model: XYZTableModel
width: 350
delegate: Rectangle {
implicitWidth: 100
implicitHeight: 50
Text {
text: display
}
}
Rectangle { // mask the headers
z: 3
color: "#222222"
y: tableView.contentY
x: tableView.contentX
width: tableView.leftMargin
height: tableView.topMargin
}
Row {
id: columnsHeader
y: tableView.contentY
z: 2
Repeater {
model: tableView.columns > 0 ? tableView.columns : 1
Label {
width: tableView.columnWidthProvider(modelData)
height: 35
text: XYZTableModel.headerData(modelData, Qt.Horizontal)
color: '#aaaaaa'
font.pixelSize: 15
padding: 10
verticalAlignment: Text.AlignVCenter
background: Rectangle { color: "#333333" }
}
}
}
Column {
id: rowsHeader
x: tableView.contentX
z: 2
Repeater {
model: tableView.rows > 0 ? tableView.rows : 1
Label {
width: 40
height: tableView.rowHeightProvider(modelData)
text: XYZTableModel.headerData(modelData, Qt.Vertical)
color: '#aaaaaa'
font.pixelSize: 15
padding: 10
verticalAlignment: Text.AlignVCenter
background: Rectangle { color: "#333333" }
}
}
}
ScrollIndicator.horizontal: ScrollIndicator { }
ScrollIndicator.vertical: ScrollIndicator { }
}
}

cannot add MapCircles to QML Map on MouseClick

I'm trying to create some markers that will be moving dynamically on a QML map. Before that, however, I need to plot them all on the map using the mouse so that I can update them later. I have been using pyqtProperty to send the coordinates I need to QML but when I try to add them to a MapItemView, they are undefined. The following code demonstrates what I am hoping to accomplish. Is the problem that I am passing a pyqtProperty to QML from another python object that was not added with setContextProperty() like in main.py? Or am I using the MapItemView delegation incorrectly?
main.qml
import QtQuick 2.7
import QtQml 2.5
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
import QtQuick.Window 2.2
import QtQuick.Layouts 1.2
import QtPositioning 5.9
import QtLocation 5.3
import QtQuick.Dialogs 1.1
ApplicationWindow {
id: root
width: 640
height: 480
visible: true
ListModel {
id: markers
}
Plugin {
id: mapPlugin
name: "osm" //"mapboxgl" "osm" "esri"
}
Map {
id: map
anchors.fill: parent
plugin: mapPlugin
center: atc.location
zoomLevel: 14
MapItemView {
model: markers
delegate: MapCircle {
radius: 50
color: 'red'
center: markLocation //issue here?
}
}
MapCircle {
id: home
center: atc.location
radius: 40
color: 'white'
}
MouseArea {
id: mousearea
anchors.fill: map
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true
property var coord: map.toCoordinate(Qt.point(mouseX, mouseY))
onDoubleClicked: {
if (mouse.button === Qt.LeftButton)
{
//Capture information for model here
atc.plot_mark(
"marker",
mousearea.coord.latitude,
mousearea.coord.longitude)
markers.append({
name: "markers",
markLocation: atc.get_marker_center("markers")
})
}
}
}
}
}
atc.py
import geocoder
from DC import DC
from PyQt5.QtPositioning import QGeoCoordinate
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
class ATC(QObject):
#pyqt Signals
locationChanged = pyqtSignal(QGeoCoordinate)
def __init__(self, parent=None):
super().__init__(parent)
self._location = QGeoCoordinate()
self.dcs = {}
g = geocoder.ip('me')
self.set_location(QGeoCoordinate(*g.latlng))
def set_location(self, coordinate):
if self._location != coordinate:
self._location = coordinate
self.locationChanged.emit(self._location)
def get_location(self):
return self._location
#pyqt Property
location = pyqtProperty(QGeoCoordinate,
fget=get_location,
fset=set_location,
notify=locationChanged)
#pyqtSlot(str, str, str)
def plot_mark(self, mark_name, lat, lng):
dc = DC(mark_name)
self.dcs[mark_name] = dc
self.dcs[mark_name].set_location(
QGeoCoordinate(float(lat), float(lng)))
#pyqtSlot(str)
def get_marker_center(self, mark_name):
return self.dcs[mark_name].location
DC.py
from PyQt5.QtPositioning import QGeoCoordinate
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
class DC(QObject):
#pyqt Signals
locationChanged = pyqtSignal(QGeoCoordinate)
def __init__(self, name, parent=None):
super().__init__(parent)
self.name = name
self._location = QGeoCoordinate()
def set_location(self, coordinate):
if self._location != coordinate:
self._location = coordinate
self.locationChanged.emit(self._location)
def get_location(self):
return self._location
#pyqt Property
location = pyqtProperty(QGeoCoordinate,
fget=get_location,
fset=set_location,
notify=locationChanged)
main.py
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtCore import QObject, QUrl, pyqtSignal, pyqtProperty
from PyQt5.QtPositioning import QGeoCoordinate
from ATC import ATC
if __name__ == "__main__":
import os
import sys
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
atc = ATC()
engine.rootContext().setContextProperty("atc", atc)
qml_path = os.path.join(os.path.dirname(__file__), "main.qml")
engine.load(QUrl.fromLocalFile(qml_path))
if not engine.rootObjects():
sys.exit(-1)
engine.quit.connect(app.quit)
sys.exit(app.exec_())
Instead of creating a model in QML you must create it in python to be able to handle it directly for it you must inherit from QAbstractListModel. For a smooth movement you should use QxxxAnimation as QPropertyAnimation.
In the following example, each time a marker is inserted, the on_markersInserted function will be called, which will move the marker towards the center of the window.
main.py
from functools import partial
from PyQt5 import QtCore, QtGui, QtQml, QtPositioning
import geocoder
class Marker(QtCore.QObject):
locationChanged = QtCore.pyqtSignal(QtPositioning.QGeoCoordinate)
def __init__(self, location=QtPositioning.QGeoCoordinate(), parent=None):
super().__init__(parent)
self._location = location
def set_location(self, coordinate):
if self._location != coordinate:
self._location = coordinate
self.locationChanged.emit(self._location)
def get_location(self):
return self._location
location = QtCore.pyqtProperty(QtPositioning.QGeoCoordinate,
fget=get_location,
fset=set_location,
notify=locationChanged)
def move(self, location, duration=1000):
animation = QtCore.QPropertyAnimation(
targetObject=self,
propertyName=b'location',
startValue=self.get_location(),
endValue=location,
duration=duration,
parent=self
)
animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
def moveFromTo(self, start, end, duration=1000):
self.set_location(start)
self.move(end, duration)
class MarkerModel(QtCore.QAbstractListModel):
markersInserted = QtCore.pyqtSignal(list)
PositionRole = QtCore.Qt.UserRole + 1000
def __init__(self, parent=None):
super().__init__(parent)
self._markers = []
self.rowsInserted.connect(self.on_rowsInserted)
def rowCount(self, parent=QtCore.QModelIndex()):
return 0 if parent.isValid() else len(self._markers)
def data(self, index, role=QtCore.Qt.DisplayRole):
if index.isValid() and 0 <= index.row() < self.rowCount():
if role == MarkerModel.PositionRole:
return self._markers[index.row()].get_location()
return QtCore.QVariant()
def roleNames(self):
roles = {}
roles[MarkerModel.PositionRole] = b'position'
return roles
#QtCore.pyqtSlot(QtPositioning.QGeoCoordinate)
def appendMarker(self, coordinate):
self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
marker = Marker(coordinate)
self._markers.append(marker)
self.endInsertRows()
marker.locationChanged.connect(self.update_model)
def update_model(self):
marker = self.sender()
try:
row = self._markers.index(marker)
ix = self.index(row)
self.dataChanged.emit(ix, ix, (MarkerModel.PositionRole,))
except ValueError as e:
pass
#QtCore.pyqtSlot(QtCore.QModelIndex, int, int)
def on_rowsInserted(self, parent, first, end):
markers = []
for row in range(first, end+1):
markers.append(self.get_marker(row))
self.markersInserted.emit(markers)
def get_marker(self, row):
if 0 <= row < self.rowCount():
return self._markers[row]
class ManagerMarkers(QtCore.QObject):
locationChanged = QtCore.pyqtSignal(QtPositioning.QGeoCoordinate)
def __init__(self, parent=None):
super().__init__(parent)
self._center= Marker(parent=self)
self._model = MarkerModel(self)
g = geocoder.ip('me')
self.moveCenter(QtPositioning.QGeoCoordinate(*g.latlng))
def model(self):
return self._model
def center(self):
return self._center
def moveCenter(self, position):
self._center.set_location(position)
center = QtCore.pyqtProperty(QtCore.QObject, fget=center, constant=True)
model = QtCore.pyqtProperty(QtCore.QObject, fget=model, constant=True)
# testing
# When a marker is added
# it will begin to move toward
# the center of the window
def on_markersInserted(manager, markers):
end = manager.center.get_location()
for marker in markers:
marker.move(end, 5*1000)
if __name__ == "__main__":
import os
import sys
app = QtGui.QGuiApplication(sys.argv)
manager = ManagerMarkers()
engine = QtQml.QQmlApplicationEngine()
engine.rootContext().setContextProperty("manager", manager)
qml_path = os.path.join(os.path.dirname(__file__), "main.qml")
engine.load(QtCore.QUrl.fromLocalFile(qml_path))
if not engine.rootObjects():
sys.exit(-1)
manager.model.markersInserted.connect(partial(on_markersInserted, manager))
engine.quit.connect(app.quit)
sys.exit(app.exec_())
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtPositioning 5.9
import QtLocation 5.3
ApplicationWindow {
id: root
width: 640
height: 480
visible: true
Plugin {
id: mapPlugin
name: "osm" // "mapboxgl" "osm" "esri"
}
Map {
id: map
anchors.fill: parent
plugin: mapPlugin
center: manager.center.location
zoomLevel: 14
MapCircle {
id: home
center: manager.center.location
radius: 40
color: 'white'
}
MapItemView {
model: manager.model
delegate: MapCircle {
radius: 50
color: 'red'
center: model.position
}
}
MouseArea {
id: mousearea
anchors.fill: map
acceptedButtons: Qt.LeftButton | Qt.RightButton
onDoubleClicked: if (mouse.button === Qt.LeftButton)
manager.model.appendMarker(map.toCoordinate(Qt.point(mouseX, mouseY)))
}
}
}

How to see my data in model /view qml with pyqt5

I want to see my data in my QmL GUI thanks to model/view programming!
I take my data from mysql, with mysql.connector
i don't understand very well the model/view, i understand the model/view concept but, coding it, is hard! Please help me with my code give me a solution I am a french-speaking so excuse me for my mistakes in english
#Main.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#ligne d'importation des classes necessaire
import sys
from PyQt5.QtCore import QObject
class MainApp(QObject):
def __init__(self, context, parent=None):
super(MainApp, self).__init__(parent)
self.win = parent
self.ctx = context
def Insert(self):
nom = self.win.findChild(QObject, "nName").property("text")
prenom = self.win.findChild(QObject, "nFirstname").property("text")
self.win.findChild(QObject, "LOB").setProperty("text","cliqué")
# insert(nom,prenom)
return 0
SqlFunc.py
import mysql.connector
mydb = mysql.connector.connect(
host="localhost",
user="root",
passwd="",
database="agenda"
)
mycursor = mydb.cursor()
def insert(nom,prenom):
var=0
sql = "INSERT INTO contact (numero, nom) VALUES (%s, %s)"
val = (nom, prenom)
if mycursor.execute(sql, val):
mydb.commit()
return "Enregistrement effectué"
else:
return "Echec de l'enregistrement contact déjà enregistré!"
def select():
mycursor.execute("SELECT * FROM contact")
data_from = mycursor.fetchall()
if data_from == "":
return "Aucun contact enregistré"
else:
return data_from
def search(name):
mycursor.execute("SELECT contact.numero, contact.nom WHERE
contact.nom LIKE \'%{}%\' ORDER BY contact.nom".format(name))
myresult = mycursor.fetchall()
return myresult
def update(old_address,new_address):
sql_request = "UPDATE customers SET address = %s WHERE address = %s"
valors = (old_address, new_address)
if mycursor.execute( sql_request, valors):
mydb.commit()
return "Modification effectuer avec succes"
else:
return "Echec de la modification"
def delete(address):
sql_request = "DELETE FROM customers WHERE address = %s"
func_address = (address)
if mycursor.execute(sql_request,func_address):
mydb.commit()
return "Suppression effectuée"
else:
return "Echec de la suppression"
databases.py
import SqlFunc
class DataBase():
def __int__(self):
self.data_from_databases
#staticmethod
def take_from_mysql():
data_to_model = SqlFunc.select()
return data_to_model
#staticmethod
def search_by_name(contact_name):
search_result = SqlFunc.search(contact_name)
return search_result
#staticmethod
def update_valor(old,new):
update_result = SqlFunc.update(old,new)
return update_result
#staticmethod
def delete_valor(address):
delete_result = SqlFunc.delete(address)
return delete_result
Star_app.py
import sys, Modele,databases
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine
from os import system
try:
system('start
C:\\Users\\yyy\\PycharmProjects\\TEST_CEI_GUI\\mysql\\bin\\mysqld.exe')
except:
quit()
else:
from Main import MainApp
if __name__ == "__main__":
#data = databases.DataBase.take_from_mysql()
sys.argv += ['--style', 'material']
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
model = Modele.NewModel()
ctx = engine.rootContext()
engine.load('C:\\Users\yyy\PycharmProjects\TEST_CEI_GUI\GO7\\
main.qml')
win = engine.rootObjects()[0]
py_mainapp = MainApp(ctx, win)
ctx.setContextProperty("py_MainApp", py_mainapp)
ctx.setContextProperty("myModel", model)
win.show()
sys.exit(app.exec())
Modele.py
class NewModel(QAbstractListModel):
numero_role = Qt.UserRole + 1
nom_role = Qt.UserRole + 2
_roles = {numero_role: b"numero", nom_role: b"nom"}
def __init__(self):
super(NewModel, self).__init__()
self._contact = []
self._data = databases.DataBase().take_from_mysql()
def update(self,search_term):
self.beginResetModel()
self._contact = self._data.search_by_name(search_term)
self.endResetModel()
#pyqtSlot(str)
def search_input(self,searh_input):
if len(searh_input) > 3:
print(searh_input)
self.update(searh_input)
def rowCount(self, parent=None, *args, **kwargs):
return len(self._data)
def data(self, QModelIndex, role=None):
row = QModelIndex.row()
if role == self.numero_role:
return self._data[row]["numero"]
if role == self.nom_role:
return self._data[row]["nom"]
def roleNames(self):
return self._roles
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.4
Page {
width: 600
height: 400
clip: true
contentHeight: 20
title: qsTr("Consulter (Consulter les personnes enregistrer)")
GridView {
id: gridView
keyNavigationWraps: true
cellWidth: 220
cellHeight: 320
visible: true
model: myModel // QML connection to python model
delegate: Rectangle {
id: thumb_frame
height: 330
width: 200
Text{
id: contactnumero
text: numero //
}
Text{
id: contactnom
text: nom//
}
}
}
}
my GUI crashes after execution, and when I modify code nothing is displayed with my view
The problem is that you are assuming that fetchall() returns a list of tuples, instead you are assuming that it returns a list of dictionaries so in that case you should change to:
mycursor = mydb.cursor(dictionary=True)
On the other hand if you are going to use the model in QML you must load it as contextProperty before loading the QML:
if __name__ == "__main__":
#data = databases.DataBase.take_from_mysql()
sys.argv += ['--style', 'material']
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
model = Modele.NewModel()
ctx = engine.rootContext()
ctx.setContextProperty("myModel", model)
engine.load(r'main.qml')
if not engine.rootObjects():
sys.exit(-1)
win = engine.rootObjects()[0]
py_mainapp = MainApp(ctx, win)
ctx.setContextProperty("py_MainApp", py_mainapp)
sys.exit(app.exec())
On the other hand QQmlApplicationEngine waits for a Window or an ApplicationWindow, not a Page, so change the QML to:
import QtQuick 2.9
import QtQuick.Controls 2.4
ApplicationWindow {
width: 600
visible: true
height: 400
title: qsTr("Consulter (Consulter les personnes enregistrer)")
GridView {
id: gridView
anchors.fill: parent
keyNavigationWraps: true
cellWidth: 220
cellHeight: 320
visible: true
model: myModel // QML connection to python model
delegate: Rectangle {
id: thumb_frame
height: 330
width: 200
Row{
Text{
id: contactnumero
text: numero //
}
Text{
id: contactnom
text: nom
}
}
}
}
}
Another way is using QtSql with QSqlQueryModel:
main.py
from PyQt5 import QtCore, QtGui, QtSql, QtQml
class SqlQueryModel(QtSql.QSqlQueryModel):
def data(self, index, role=QtCore.Qt.DisplayRole):
value = QtCore.QVariant()
if index.isValid():
if role < QtCore.Qt.UserRole:
value = super(SqlQueryModel, self).data(index, role)
else:
columnIdx = role - QtCore.Qt.UserRole - 1;
modelIndex = self.index(index.row(), columnIdx)
value =super(SqlQueryModel, self).data(modelIndex, QtCore.Qt.DisplayRole)
return value
def roleNames(self):
roles = dict()
for i in range(self.record().count()):
roles[QtCore.Qt.UserRole + i +1] = self.record().fieldName(i).encode()
return roles
class Manager(QtCore.QObject):
def __init__(self, parent=None):
super(Manager, self).__init__(parent)
self._model = SqlQueryModel(self)
self.take_from_mysql()
#QtCore.pyqtProperty(SqlQueryModel)
def model(self):
return self._model
#QtCore.pyqtSlot()
def take_from_mysql(self):
self._model.setQuery("SELECT * FROM contact")
#QtCore.pyqtSlot(str)
def search_by_name(self, name):
query = QtSql.QSqlQuery()
query.prepare('''SELECT * FROM contact WHERE nom LIKE ? ORDER BY nom;''')
query.addBindValue("%{}%".format(name))
query.exec()
self._model.setQuery(query)
def createConnection():
db = QtSql.QSqlDatabase.addDatabase('QMYSQL')
db.setHostName("localhost")
db.setDatabaseName("agenda")
db.setUserName("root")
db.setPassword("")
if not db.open():
print('''Unable to establish a database connection.\n
This example needs SQLite support. Please read
the Qt SQL driver documentation for information
how to build it.\n\n Click Cancel to exit.''')
return False
return True
if __name__ == '__main__':
import sys
sys.argv += ['--style', 'material']
app = QtGui.QGuiApplication(sys.argv)
if not createConnection():
sys.exit(-1)
manager = Manager()
engine = QtQml.QQmlApplicationEngine()
ctx = engine.rootContext()
ctx.setContextProperty("manager", manager)
engine.load("main.qml")
sys.exit(app.exec_())
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.4
ApplicationWindow {
width: 600
visible: true
height: 400
title: qsTr("Consulter (Consulter les personnes enregistrer)")
GridView {
id: gridView
anchors.fill: parent
keyNavigationWraps: true
cellWidth: 220
cellHeight: 320
visible: true
model: manager.model // QML connection to python model
delegate: Rectangle {
id: thumb_frame
height: 330
width: 200
Row{
Text{
id: contactnumero
text: numero //
}
Text{
id: contactnom
text: nom
}
}
}
}
}

Update/repaint gauge inside QQuickWidget

I couldn't get through some problem for 2 days in my app, so I post here.
I have UI written in pyqt5 (Qt designer), and part of main window consists QML objects (inside QQuickWidget).
My problem is refreshing gauge object inside QQuickWidget.
For example, if I run application with QML file as ApplicationWindow, everything is ok and I am able to manipulate data:
Gauge is refreshing
But when I place this object (and change object in QML to Rectangle) into the QQuickWidget, I am not able to update the state of this object.
gauge inside python UI application - not refreshing
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = GUI_MainWindow() #Main window written in pyqt5
qmlRegisterType(RadialBar, "SDK", 1,0, "RadialBar")
# Setting source for QML Widget
# batteryCWidget is the QQUickWidget object (4 of them are on main window)
window.batteryCWidget.setSource(QUrl('qml_widget.qml'))
batteryWidget = MyClass() # Class with function to update data in QML
engine = QQmlApplicationEngine()
context = engine.rootContext()
context.setContextProperty("batteryWidget", batteryWidget)
engine.load('qml_widget.qml')
root = engine.rootObjects()[0]
timer = QTimer()
timer.start(200)
#Every 200ms I generate new number in function random_value
timer.timeout.connect(batteryWidget.random_value)
#and then update value in QML
batteryWidget.randomValue.connect(root.setValue)
Is it possible to update/repaint/refresh state of object inside QQuickWidget?
This is qml_widget.qml:
import QtQuick 2.4
import SDK 1.0
import QtQuick.Layouts 1.1
Rectangle {
id: root
Layout.alignment: Layout.Center
width: 160
height: 145
color: "#181818"
property var suffix: "A"
property int minVal: 0
property int maxVal: 100
property var actVal: 0
function setValue(v) {
actVal = v
}
Rectangle {
Layout.alignment: Layout.Center
width: 160
height: 145
color: "#1d1d35"
border.color: "#000000"
border.width: 3
Text {
id: name
text: "Battery Current"
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 5
font.pointSize: 13
color: "#6affcd"
}
RadialBar {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
width: parent.width / 1.4
height: width - (0.001)*actVal
penStyle: Qt.RoundCap
progressColor: "#6affcd"
foregroundColor: "#191a2f"
dialWidth: 11
minValue: minVal
maxValue: maxVal
value: actVal
suffixText: suffix
textFont {
family: "Halvetica"
italic: false
pointSize: 18
}
textColor: "#00ffc1"
}}
MyClass:
class MyClass(QObject):
randomValue = pyqtSignal(float)
def __init__(self, parent=None):
super(MyClass, self).__init__(parent)
def random_value(self):
v = float(randrange(1, 100))
self.randomValue.emit(v)
RadialBar class:
class RadialBar(QQuickPaintedItem):
class DialType():
FullDial = 0
MinToMax = 1
NoDial = 2
sizeChanged = pyqtSignal()
startAngleChanged = pyqtSignal()
spanAngleChanged = pyqtSignal()
minValueChanged = pyqtSignal()
maxValueChanged = pyqtSignal()
valueChanged = pyqtSignal()
dialWidthChanged = pyqtSignal()
backgroundColorChanged = pyqtSignal()
foregroundColorChanged = pyqtSignal()
progressColorChanged = pyqtSignal()
textColorChanged = pyqtSignal()
suffixTextChanged = pyqtSignal()
showTextChanged = pyqtSignal()
penStyleChanged = pyqtSignal()
dialTypeChanged = pyqtSignal()
textFontChanged = pyqtSignal()
def __init__(self, parent=None):
super(RadialBar, self).__init__(parent)
self.setWidth(200)
self.setHeight(200)
self.setSmooth(True)
self.setAntialiasing(True)
self._Size = 200
self._StartAngle = 40
self._SpanAngle = 280
self._MinValue = 0
self._MaxValue = 100
self._Value = 50
self._DialWidth = 25
self._BackgroundColor = Qt.transparent
self._DialColor = QColor(80,80,80)
self._ProgressColor = QColor(135,26,50)
self._TextColor = QColor(0, 0, 0)
self._SuffixText = ""
self._ShowText = True
self._PenStyle = Qt.FlatCap
self._DialType = RadialBar.DialType.MinToMax
self._TextFont = QFont()
def paint(self, painter):
painter.save()
size = min(self.width(), self.height())
self.setWidth(size)
self.setHeight(size)
rect = QRectF(0, 0, self.width(), self.height()) #self.boundingRect()
painter.setRenderHint(QPainter.Antialiasing)
pen = painter.pen()
pen.setCapStyle(self._PenStyle)
startAngle = -90 - self._StartAngle
if RadialBar.DialType.FullDial != self._DialType:
spanAngle = 0 - self._SpanAngle
else:
spanAngle = -360
#Draw outer dial
painter.save()
pen.setWidth(self._DialWidth)
pen.setColor(self._DialColor)
painter.setPen(pen)
offset = self._DialWidth / 2
if self._DialType == RadialBar.DialType.MinToMax:
painter.drawArc(rect.adjusted(offset, offset, -offset, -offset), startAngle * 16, spanAngle * 16)
elif self._DialType == RadialBar.DialType.FullDial:
painter.drawArc(rect.adjusted(offset, offset, -offset, -offset), -90 * 16, -360 * 16)
else:
pass
#do not draw dial
painter.restore()
#Draw background
painter.save()
painter.setBrush(self._BackgroundColor)
painter.setPen(self._BackgroundColor)
inner = offset * 2
painter.drawEllipse(rect.adjusted(inner, inner, -inner, -inner))
painter.restore()
#Draw progress text with suffix
painter.save()
painter.setFont(self._TextFont)
pen.setColor(self._TextColor)
painter.setPen(pen)
if self._ShowText:
painter.drawText(rect.adjusted(offset, offset, -offset, -offset), Qt.AlignCenter,str(self._Value) + self._SuffixText)
else:
painter.drawText(rect.adjusted(offset, offset, -offset, -offset), Qt.AlignCenter, self._SuffixText)
painter.restore()
#Draw progress bar
painter.save()
pen.setWidth(self._DialWidth)
pen.setColor(self._ProgressColor)
valueAngle = float(float(self._Value - self._MinValue)/float(self._MaxValue - self._MinValue)) * float(spanAngle) #Map value to angle range
painter.setPen(pen)
painter.drawArc(rect.adjusted(offset, offset, -offset, -offset), startAngle * 16, valueAngle * 16)
painter.restore()
#QtCore.pyqtProperty(str, notify=sizeChanged)
def size(self):
return self._Size
#size.setter
def size(self, size):
if self._Size == size:
return
self._Size = size
self.sizeChanged.emit()
#QtCore.pyqtProperty(int, notify=startAngleChanged)
def startAngle(self):
return self._StartAngle
#startAngle.setter
def startAngle(self, angle):
if self._StartAngle == angle:
return
self._StartAngle = angle
self.startAngleChanged.emit()
#QtCore.pyqtProperty(int, notify=spanAngleChanged)
def spanAngle(self):
return self._SpanAngle
#spanAngle.setter
def spanAngle(self, angle):
if self._SpanAngle == angle:
return
self._SpanAngle = angle
self.spanAngleChanged.emit()
#QtCore.pyqtProperty(int, notify=minValueChanged)
def minValue(self):
return self._MinValue
#minValue.setter
def minValue(self, value):
if self._MinValue == value:
return
self._MinValue = value
self.minValueChanged.emit()
#QtCore.pyqtProperty(int, notify=maxValueChanged)
def maxValue(self):
return self._MaxValue
#maxValue.setter
def maxValue(self, value):
if self._MaxValue == value:
return
self._MaxValue = value
self.maxValueChanged.emit()
#QtCore.pyqtProperty(float, notify=valueChanged)
def value(self):
return self._Value
#value.setter
def value(self, value):
if self._Value == value:
return
self._Value = value
self.valueChanged.emit()
#QtCore.pyqtProperty(float, notify=dialWidthChanged)
def dialWidth(self):
return self._DialWidth
#dialWidth.setter
def dialWidth(self, width):
if self._DialWidth == width:
return
self._DialWidth = width
self.dialWidthChanged.emit()
#QtCore.pyqtProperty(QColor, notify=backgroundColorChanged)
def backgroundColor(self):
return self._BackgroundColor
#backgroundColor.setter
def backgroundColor(self, color):
if self._BackgroundColor == color:
return
self._BackgroundColor = color
self.backgroundColorChanged.emit()
#QtCore.pyqtProperty(QColor, notify=foregroundColorChanged)
def foregroundColor(self):
return self._ForegrounColor
#foregroundColor.setter
def foregroundColor(self, color):
if self._DialColor == color:
return
self._DialColor = color
self.foregroundColorChanged.emit()
#QtCore.pyqtProperty(QColor, notify=progressColorChanged)
def progressColor(self):
return self._ProgressColor
#progressColor.setter
def progressColor(self, color):
if self._ProgressColor == color:
return
self._ProgressColor = color
self.progressColorChanged.emit()
#QtCore.pyqtProperty(QColor, notify=textColorChanged)
def textColor(self):
return self._TextColor
#textColor.setter
def textColor(self, color):
if self._TextColor == color:
return
self._TextColor = color
self.textColorChanged.emit()
#QtCore.pyqtProperty(str, notify=suffixTextChanged)
def suffixText(self):
return self._SuffixText
#suffixText.setter
def suffixText(self, text):
if self._SuffixText == text:
return
self._SuffixText = text
self.suffixTextChanged.emit()
#QtCore.pyqtProperty(str, notify=showTextChanged)
def showText(self):
return self._ShowText
#showText.setter
def showText(self, show):
if self._ShowText == show:
return
self._ShowText = show
#QtCore.pyqtProperty(Qt.PenCapStyle, notify=penStyleChanged)
def penStyle(self):
return self._PenStyle
#penStyle.setter
def penStyle(self, style):
if self._PenStyle == style:
return
self._PenStyle = style
self.penStyleChanged.emit()
#QtCore.pyqtProperty(int, notify=dialTypeChanged)
def dialType(self):
return self._DialType
#dialType.setter
def dialType(self, type):
if self._DialType == type:
return
self._DialType = type
self.dialTypeChanged.emit()
#QtCore.pyqtProperty(QFont, notify=textFontChanged)
def textFont(self):
return self._TextFont
#textFont.setter
def textFont(self, font):
if self._TextFont == font:
return
self._TextFont = font
self.textFontChanged.emit()
When you have an object created in Python/C++ and you want to connect it to an object created in QML, the correct option is to do it on the QML side using Connections, but for this you must create a property in MyClass.
main.py
import sys
from random import randrange
from PyQt5 import QtCore, QtGui, QtWidgets, QtQml, QtQuick, QtQuickWidgets
from RadialBar import RadialBar
class MyClass(QtCore.QObject):
randomValueChanged = QtCore.pyqtSignal(float)
def __init__(self, parent=None):
super(MyClass, self).__init__(parent)
self.m_randomValue = 0
#QtCore.pyqtProperty(float, notify=randomValueChanged)
def randomValue(self):
return self.m_randomValue
#randomValue.setter
def randomValue(self, v):
if self.m_randomValue == v:
return
self.m_randomValue = v
self.randomValueChanged.emit(v)
def random_value(self):
v = float(randrange(1, 100))
self.randomValue = v
class GUI_MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.batteryCWidget = QtQuickWidgets.QQuickWidget()
self.setCentralWidget(self.batteryCWidget)
self.batteryCWidget.setResizeMode(QtQuickWidgets.QQuickWidget.SizeRootObjectToView)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = GUI_MainWindow() #Main window written in pyqt5
QtQml.qmlRegisterType(RadialBar, "SDK", 1,0, "RadialBar")
batteryWidget = MyClass() # Class with function to update data in QML
context = window.batteryCWidget.rootContext()
context.setContextProperty("batteryWidget",batteryWidget)
window.batteryCWidget.setSource(QtCore.QUrl.fromLocalFile('qml_widget.qml'))
timer = QtCore.QTimer()
timer.timeout.connect(batteryWidget.random_value)
timer.start(200)
window.show()
sys.exit(app.exec_())
qml_widget.qml
import QtQuick 2.4
import SDK 1.0
import QtQuick.Layouts 1.1
Rectangle {
id: root
Layout.alignment: Layout.Center
width: 160
height: 145
color: "#181818"
property string suffix: "A"
property int minVal: 0
property int maxVal: 100
property real actVal: 0
Connections{
target: batteryWidget
onRandomValueChanged: root.actVal = batteryWidget.randomValue
}
Rectangle {
Layout.alignment: Layout.Center
width: 160
height: 145
color: "#1d1d35"
border.color: "#000000"
border.width: 3
Text {
id: name
text: "Battery Current"
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 5
font.pointSize: 13
color: "#6affcd"
}
RadialBar {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
width: parent.width / 1.4
height: width - (0.001)*actVal
penStyle: Qt.RoundCap
progressColor: "#6affcd"
foregroundColor: "#191a2f"
dialWidth: 11
minValue: minVal
maxValue: maxVal
value: actVal
suffixText: suffix
textFont {
family: "Halvetica"
italic: false
pointSize: 18
}
textColor: "#00ffc1"
}
}
}
You can find the complete code in the following link.
As #GrecKo says, a much simpler way is to make a binding.
...
property real actVal: batteryWidget.randomValue
Rectangle {
...

Categories