I created a button that when pressed, it shows a single database record in the Table, it works perfectly on the desktop version, but when I compile for android it doesn't work, can you help me?
This is the code:
"""
O aplicativo de teste
"""
import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW
import sqlite3
import asyncio
class KaiqueTeste(toga.App):
def startup(self):
main_box = toga.Box()
self.label = toga.Label(text="Kaique Teste")
self.lista = toga.Table(['Cod', 'Nome', 'CPF/CNPJ', 'RG/IE', 'Endereço', 'Cplt/Ende', 'Pont Ref', 'Bairro', 'Cidade', 'UF'], missing_value="")
self.lista.MIN_HEIGHT = 180
self.btn = toga.Button('CONFIRMA', style=Pack(padding=5), on_press=self.confirmar_cli)
pasta = toga.Box(style=Pack(direction=COLUMN, padding=5))
pasta.add(self.label)
pasta.add(self.lista)
pasta.add(self.btn)
main_box.add(pasta)
self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()
async def confirmar_cli(self, widget):
try:
conn = sqlite3.connect(self.paths.app/'banco.db')
c = conn.cursor()
c.execute("SELECT cod, nome, cpf_cnpj, rg_ie, endereco, cplt_ende, pont_ref, bairro, cidade, uf FROM clientes WHERE cod = 1")
dados_lidos = c.fetchall()
await asyncio.sleep(0.1)
self.lista.data = dados_lidos
except Exception as ERROR:
print(ERROR)
def main():
return KaiqueTeste()
What I try to do:
I want to write a Python program running on an RPI which communicates to a software called QLab4 via OSC. It should display the received workspace data (JSON format) in a table, while the user is able to select a cue from the table and fire it using a key press.
So far I got two scripts (just proof of concept stage), but I struggle to combine them into one program. Any hints to how to do it would be greatly appreciated.
Script 1:
Starts OSC client & server and sends and receives OSC to/from QLAB.
At the moment it just prints out the JSON (workspace) and the selected cue.
import argparse
import time
import threading
import json
from pythonosc import udp_client
from pythonosc import dispatcher
from pythonosc import osc_server
workspace = {}
def print_json(unused_addr, args):
global workspace
workspace = json.loads(args)
print(workspace)
def print_name(unused_addr, args):
decoded_json = json.loads(args)
print(args)
print(type(decoded_json['data']))
data = decoded_json['data']
print("Cue Name: " + data['displayName'])
print("Cue Number: " + data['number'])
def print_payload(unused_addr, args):
print ("Selected Cue: " + args)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--serverip", default="127.0.0.1", help="The ip to listen on")
parser.add_argument("--serverport", type=int, default=53001, help="The port the OSC Server is listening on")
parser.add_argument("--clientip", default="127.0.0.1", help="The ip of the OSC server")
parser.add_argument("--clientport", type=int, default=53000, help="The port the OSC Client is listening on")
args = parser.parse_args()
# listen to addresses and print changes in values
dispatcher = dispatcher.Dispatcher()
dispatcher.map("/reply/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueLists",print_json)
dispatcher.map("/update/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueList/*/playbackPosition",print_payload)
dispatcher.map("/reply/cue_id/*/valuesForKeys", print_json)
dispatcher.map("/reply/cue_id/*/valuesForKeys", print_name)
def start_server(ip, port):
print("Starting Server")
server = osc_server.ThreadingOSCUDPServer(
(ip, port), dispatcher)
print("Serving on {}".format(server.server_address))
thread = threading.Thread(target=server.serve_forever)
thread.start()
def start_client(ip, port):
print("Starting Client")
client = udp_client.SimpleUDPClient(ip, port)
# print("Sending on {}".format(client.))
thread = threading.Thread(target=get_workspace(client))
thread2 = threading.Thread(target=print_active(client))
thread.start()
thread2.start()
# send random values between 0-1 to the three addresses
def get_workspace(client):
client.send_message("/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueLists", 1)
client.send_message("/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/updates", 1)
def print_active(client):
while True:
client.send_message("/cue/active/valuesForKeys", "[\"displayName\",\"number\", \"type\", \"isBroken\", "
"\"isLoaded\", \"isPaused\", \"isRunning\", \"preWait\", \"duration\", \"postWait\"]")
time.sleep(1)
start_server(args.serverip, 53001)
start_client(args.clientip, 53000)
Script 2:
I copied the received JSON string of the workspace as variable. Then it builds a table using pyQt5. After hitting "R" it inserts a row for each received cue.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import json
# var
cue_count = 1
cue_id = ""
# data
data = '{"status":"ok","data":[{"number":"","uniqueID":"0CBC2F3A-B4CB-46A0-AE74-0810598256AD","cues":' \
'[{"number":"1","uniqueID":"C358F03C-128B-4BBA-B8D8-3E1C3D217775","flagged":false,' \
'"listName":"Cue 1","type":"Memo","colorName":"none","name":"Cue 1","armed":true},' \
'{"number":"2","uniqueID":"F2FDC19F-8E4F-43E6-B49F-BA421C886E63","flagged":false,' \
'"listName":"Cue 2","type":"Memo","colorName":"none","name":"Cue 2","armed":true},' \
'{"number":"3","uniqueID":"244B2654-51B3-4423-AF1A-6894F301CA6B","flagged":false,' \
'"listName":"Cue 3","type":"Memo","colorName":"none","name":"Cue 3","armed":true},' \
'{"number":"4","uniqueID":"42B37827-A5E8-444D-82D1-7706A8E197A6","flagged":false,' \
'"listName":"Cue 4","type":"Memo","colorName":"none","name":"Cue 4","armed":true}],' \
'"flagged":false,"listName":"Main Cue List","type":"Cue List","colorName":"none",' \
'"name":"Main Cue List","armed":true}],"workspace_id":"C936DAAF-D5C7-4D1D-8382-54CA426A1BDC",' \
'"address":"\/workspace\/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC\/cueLists"}'
class TableFromList(QTableWidget):
def __init__(self, data, *args):
# Call parent constructor
QTableWidget.__init__(self, *args)
# Set the necessary configurations fot the table
self.data = data
self.resizeColumnsToContents()
self.resizeRowsToContents()
self.setColumnWidth(0, 100)
self.setColumnWidth(0, 40)
self.setColumnWidth(1, 200)
self.setColumnWidth(2, 80)
self.setColumnWidth(3, 80)
self.setMinimumWidth(400)
self.setWindowTitle("WORKSPACE")
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.setStyleSheet("color:white; background-color:black; font-weight: bold; selection-background-color: orange")
self.setFont(QFont('Arial', 14))
# Declare the variable to set the header content
headers = ["No.", "NAME", "Armed"]
# Set the header label of the table
self.setHorizontalHeaderLabels(headers)
# Read the particular cell value
self.clicked.connect(self.on_click)
self.currentItemChanged.connect(self.select_cue)
# Display the window in the center of the screen
win = self.frameGeometry()
pos = QDesktopWidget().availableGeometry().center()
win.moveCenter(pos)
self.move(win.topLeft())
self.show()
def on_click(self):
for ItemVal in self.selectedItems():
# Read the header value based on the selected cell
subject = self.horizontalHeaderItem(ItemVal.column()).text()
# Print the detail information of the mark
print("\n", self.ID_list[ItemVal.row()], " got ", ItemVal.text(), " in ", subject)
def select_cue(self):
global cue_id
global data
data_dict = json.loads(data)
data_parsed = data_dict["data"][0]["cues"]
value = self.currentRow()
cue = self.item(value, 0)
cue_number = cue.text()
cue_number = int(cue_number) - 1
cue_id = data_parsed[cue_number]["uniqueID"]
print("ID: " + cue_id)
print("Cue Number is: " + cue.text())
def keyPressEvent(self, keyEvent):
super(TableFromList, self).keyPressEvent(keyEvent)
data = self.data
def build_cuelist(data):
data_dict = json.loads(data)
cue_count = len(data_dict["data"][0]["cues"])
data = data_dict["data"][0]["cues"]
self.setRowCount(0)
for i in range(cue_count):
rowPosition = table.rowCount()
table.insertRow(rowPosition) # insert new row
self.setItem(rowPosition, 0, QTableWidgetItem(str(data[i]["number"])))
self.setItem(rowPosition, 1, QTableWidgetItem(str(data[i]["name"])))
self.setItem(rowPosition, 2, QTableWidgetItem(str(data[i]["armed"])))
if keyEvent.key() == Qt.Key_Return:
print('*** Return pressed')
print("OSC: /cue/" + cue_id + "/go")
elif keyEvent.key() == Qt.Key_Enter:
print('*** Enter pressed')
elif keyEvent.key() == Qt.Key_1:
print('*** 1 pressed')
elif keyEvent.key() == Qt.Key_R:
build_cuelist(self.data)
# Create app object and execute the app
app = QApplication(sys.argv)
table = TableFromList(data, 0, 3)
table.show()
app.exec()
The code provided by the OP has various errors such as threads not being executed in a second thread, the code works because threads are not needed.
On the other hand, signals must be used to send the information from the callbacks to the widgets.
import json
import sys
import threading
from dataclasses import dataclass
from functools import cached_property
from PyQt5 import QtCore, QtGui, QtWidgets
from pythonosc import dispatcher
from pythonosc import osc_server
from pythonosc import udp_client
#dataclass
class Server(QtCore.QObject):
ip: str = "127.0.0.1"
port: int = 53001
datachanged = QtCore.pyqtSignal(dict)
def __post_init__(self):
super().__init__()
#cached_property
def osc_server(self):
dp = dispatcher.Dispatcher()
dp.map(
"/reply/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueLists",
self.handle_reply_workspace,
)
dp.map(
"/update/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueList/*/playbackPosition",
self.handle_update_workspace,
)
dp.map(
"/cue/active/valuesForKeys",
self.handle_cue_id,
)
return osc_server.ThreadingOSCUDPServer((self.ip, self.port), dp)
def start(self):
threading.Thread(target=self.osc_server.serve_forever, daemon=True).start()
def handle_reply_workspace(self, unused_addr, data):
try:
d = json.loads(data)
except json.JSONDecodeError as e:
print(str(e))
else:
self.datachanged.emit(d)
def handle_update_workspace(self, unused_addr, data):
print(data)
def handle_cue_id(self, unused_addr, data):
print(data)
#dataclass
class Client(QtCore.QObject):
ip: str = "127.0.0.1"
port: int = 53000
datachanged = QtCore.pyqtSignal(dict)
def __post_init__(self):
super().__init__()
def start_timer(self, dt=1000):
self.timer.start(dt)
def request_workspace(self):
self.osc_client.send_message(
"/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/cueLists", 1
)
self.osc_client.send_message(
"/workspace/C936DAAF-D5C7-4D1D-8382-54CA426A1BDC/updates", 1
)
#cached_property
def osc_client(self):
return udp_client.SimpleUDPClient(self.ip, self.port)
#cached_property
def timer(self):
timer = QtCore.QTimer()
timer.timeout.connect(self.handle_timeout)
return timer
#QtCore.pyqtSlot()
def handle_timeout(self):
keys = [
"displayName",
"number",
"type",
"isBroken",
"isLoaded",
"isPaused",
"isRunning",
"preWait",
"duration",
"postWait",
]
self.osc_client.send_message(
"/cue/active/valuesForKeys",
json.dumps(keys),
)
class TableWidget(QtWidgets.QTableWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setColumnCount(3)
self.setHorizontalHeaderLabels(["No.", "NAME", "Armed"])
self.setWindowTitle("WORKSPACE")
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.setStyleSheet(
"""
color:white;
background-color:black;
font-weight: bold;
selection-background-color: orange
"""
)
self.setFont(QtGui.QFont("Arial", 14))
self.setColumnWidth(0, 40)
self.setColumnWidth(1, 200)
self.setColumnWidth(2, 80)
self.setMinimumWidth(400)
#QtCore.pyqtSlot(dict)
def update_data(self, data):
self.setRowCount(0)
keys = ("number", "name", "armed")
cues = data["data"][0]["cues"]
for i, cue in enumerate(cues):
self.insertRow(self.rowCount())
for j, key in enumerate(keys):
value = cue[key]
item = QtWidgets.QTableWidgetItem()
item.setData(QtCore.Qt.DisplayRole, value)
self.setItem(i, j, item)
def main():
app = QtWidgets.QApplication(sys.argv)
server = Server()
server.start()
client = Client()
client.request_workspace()
client.start_timer()
table_widget = TableWidget()
table_widget.resize(640, 480)
table_widget.show()
server.datachanged.connect(table_widget.update_data)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Is there any DataGrid/DataGridView component
part of a GUI components library like Qt/GTK/WxWidgets/etc.
that has the same funcionality and flexibility
as C#/WPF or C#/Winforms DataGridView ?
(mainly for use with Python 3... cross-platform/avoid MS .net)
I mean working in tandem with a Dataset component
or similar paradigm/functionality to implement CRUD ?
I don't want to extend more the question in this phase...
I'll wait for some answers to develop the discussion...
Disclaimer: I have never used C#WPF or the other tool you mentioned.
Disclaimer2: This app does not have CRUD, yet. It only has read support.
Finally, there is no guarantee this is even close to what you are looking for. I am posting because it seems nobody has a better solution.
#!/usr/bin/python3
# sql_window.py
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import psycopg2, sys
class SQLWindowGUI :
def __init__(self):
self.db = psycopg2.connect(dbname = 'trial_new_db', user = 'postgres', password = 'true', host = '192.168.0.120')
button = Gtk.Button('Run SQL')
button.set_property('valign', Gtk.Align.END)
button.set_property('halign', Gtk.Align.END)
button.connect('clicked', self.run_sql_clicked)
overlay = Gtk.Overlay()
overlay.add_overlay(button)
self.sql_buffer = Gtk.TextBuffer()
self.sql_buffer.set_text("SELECT id, name FROM contacts")
textview = Gtk.TextView.new_with_buffer(self.sql_buffer)
textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
overlay.add (textview)
pane = Gtk.Paned()
pane.set_orientation(Gtk.Orientation.VERTICAL)
pane.set_position(100)
pane.add1(overlay)
self.scrolledwindow = Gtk.ScrolledWindow()
self.treeview = Gtk.TreeView()
self.scrolledwindow.add(self.treeview)
self.sql_error_buffer = Gtk.TextBuffer()
self.textview = Gtk.TextView.new_with_buffer(self.sql_error_buffer)
self.textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
box = Gtk.Box()
box.pack_start(self.scrolledwindow, True, True, 0)
box.pack_start(self.textview, True, True, 0)
pane.add2(box)
self.window = Gtk.Window()
self.window.add(pane)
self.window.set_title("SQL Window")
self.window.set_default_size(600, 500)
self.window.show_all()
self.window.connect('destroy', Gtk.main_quit)
def run_sql_clicked (self, button):
for column in self.treeview.get_columns():
self.treeview.remove_column(column)
start_iter = self.sql_buffer.get_start_iter ()
end_iter = self.sql_buffer.get_end_iter ()
string = self.sql_buffer.get_text(start_iter, end_iter, True)
cursor = self.db.cursor()
try:
cursor.execute(string)
except Exception as e:
self.sql_error_buffer.set_text(str(e))
self.textview.set_visible(True)
self.scrolledwindow.set_visible(False)
self.db.rollback()
return
#probably an UPDATE, report rows affected
if cursor.description == None:
result = "%s row(s) affected" % cursor.rowcount
self.sql_error_buffer.set_text(result)
self.textview.set_visible(True)
self.scrolledwindow.set_visible(False)
self.db.rollback()
return
#create treeview columns and a liststore to store the info
self.textview.set_visible(False)
self.scrolledwindow.set_visible(True)
type_list = list()
for index, row in enumerate(cursor.description):
column_name = row.name
type_ = row.type_code
if type_ == 23:
type_list.append(int)
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(column_name, renderer, text=index)
self.treeview.append_column(column)
column.set_sort_column_id(index)
elif type_ == 16:
type_list.append(bool)
renderer = Gtk.CellRendererToggle()
column = Gtk.TreeViewColumn(column_name, renderer, active=index)
self.treeview.append_column(column)
column.set_sort_column_id(index)
else:
type_list.append(str)
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(column_name, renderer, text=index)
self.treeview.append_column(column)
column.set_sort_column_id(index)
store = Gtk.ListStore()
store.set_column_types(type_list)
self.treeview.set_model(store)
for row in cursor.fetchall():
# do a convert, cell by cell, to make sure types are correct
store_row = list()
for index, element in enumerate(row):
store_row.append(type_list[index](element))
store.append (store_row)
self.db.rollback()
cursor.close()
def main_gui():
app = SQLWindowGUI()
Gtk.main()
if __name__ == "__main__":
sys.exit(main_gui())
History: I created it as an lightweight alternative to PGAdmin to simply view the data in Postgres. It should work on most any Linux distro with psycopg2 and Gtk3 installed. Theoretically, it should be cross-platform.
Combobox dbSelection does not update to apply in code when selecting table names from sqlite to display in combobox tabSelection.
It also takes a significant number of seconds to load the directory dialog window once the button is clicked.
I would also like to ensure that all tables within a database are listed in the tabSelection combobox.
The code is as follows and is associated with a Qt Designer file:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
import qcDbWidget2
import os
import sqlite3
class MainDialog(QWidget, qcDbWidget2.Ui_qcQueryWidget):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.connect(self.dbDirSelect, SIGNAL("clicked()"), self.getDirName)
def getDirName(self):
existDir = QFileDialog.getExistingDirectory(self)
dbDir = str(existDir)
self.dbDirDisplay.setText(dbDir)
dbFileList = []
for root, dirs, files in os.walk(dbDir):
for file in files:
if file.endswith('.db'):
dbFileList.append(file)
self.dbSelection.addItems(dbFileList)
tableList = []
self.dbSelection.update()
dbName = str(self.dbSelection.currentText())
dbPath = str(dbDir + '\\' + dbName)
conn = sqlite3.connect(dbPath)
c = conn.cursor()
res = c.execute("SELECT name FROM sqlite_master WHERE type='table';")
self.tabSelection.clear()
for name in res:
tableList.append(name[0])
print(name[0])
for name in tableList:
self.tabSelection.addItems(tableList)
app = QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()
Have you considered to use QTableView instead of QTableList?
Qt uses MVC which methods are way faster than doing it by hand.
In this case, it's pretty simple.
You create a model:
my_model = QStringListModel()
You can then save data to my_model:
my_list = ["toto", "tutu", "tata"]
my_model.setStringList(my_list)
Once you have a QTableView, you use it's method setModel() to provide the model.
my_table_view.setModel(my_model)
I am creating an application in PyQt in which I have a a list of check boxes which I have created using QStandardItemModel and QStandardItem and it works perfectly. I want to connect the first item in the list which is a 'Select all' check box to a function. This function should be able to check all the other items of the list. I'm trying to do this by the following code:
model = QStandardItemModel(list)
item = QStandardItem("Select all")
model.appendRow(item)
item.setCheckable(True)
model.itemChanged.connect(state_changed)
def state_changed(item):
print ("Hello")
I have added more items to the list from the output of an SQL query and I can see that 'Hello' is printed irrespective of which check box I click. This is my entire code:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtSql import *
def main():
db = QSqlDatabase.addDatabase("QODBC")
db.setHostName('LAPTOP-B79DRPA3')
db.setDatabaseName('local')
db.open()
if (db.open()==False):
QMessageBox.critical(None, "Database Error",
db.lastError().text())
query = QSqlQuery ()
query.exec_ ("select id from [Sarah].[dbo].fraga")
list = QListView()
model = QStandardItemModel(list)
item = QStandardItem("Select all")
model.appendRow(item)
item.setCheckable(True)
model.itemChanged.connect(state_changed)
while (query.next()):
item1 = QStandardItem(str(query.value(0)))
model.appendRow(item1)
item1.setCheckable(True)
list.setModel(model)
list.show()
return app.exec_()
def state_changed(item):
print ("Hello")
if __name__ == '__main__':
app = QApplication(sys.argv)
list = QListView()
model = QStandardItemModel(list)
main()
How do I ensure that the function is invoked only when the state of 'Select All' is changed?
Instead of connecting to QAbstractItemModel.itemChanged signal, connect to QAbstractItemView.clicked signal, which specifies the index clicked. Also, it's advisable not to name a variable list, as it interferes with the built-in list.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtSql import *
def main():
db = QSqlDatabase.addDatabase("QODBC")
db.setHostName('LAPTOP-B79DRPA3')
db.setDatabaseName('local')
db.open()
if (db.open()==False):
QMessageBox.critical(None, "Database Error",
db.lastError().text())
query = QSqlQuery ()
query.exec_ ("select id from [Sarah].[dbo].fraga")
list_view = QListView()
model = QStandardItemModel(list_view)
item = QStandardItem("Select all")
model.appendRow(item)
item.setCheckable(True)
list_view.clicked.connect(state_changed)
while (query.next()):
item1 = QStandardItem(str(query.value(0)))
model.appendRow(item1)
item1.setCheckable(True)
list_view.setModel(model)
list_view.show()
return app.exec_()
def state_changed(index):
row = index.row()
if row == 0:
print ("Hello")
if __name__ == '__main__':
app = QApplication(sys.argv)
list_view = QListView()
model = QStandardItemModel(list)
main()