PyQt: How to connect a QStandardItem in a QStandardItemModel to a function - python

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()

Related

How can I filter an ms-access databse, using QSqlTableModel and QLineEdit?

I'm building a GUI that allows users to search information in a ms access database
(yup. It has to be the ms access)
The user has a textfield where he can type his search
and the Tableview should update instantly.
At the moment the DB disappears whenever you type a letter in the field.
Took me a while to figure out the problem: my SQL statement is simply not right.
(Thanks to model.lastError)
The whole function looks like this:
self.ui.Kistenschild_suchen.textChanged.connect(self.update_filter)
def update_filter(self, s):
s = re.sub("[\W_]+", "", s)
filter_str = 'Produkt LIKE %{}%"'.format(s)
self.ui.model.setFilter(filter_str)
print(self.ui.model.lastError())
In this case I typed k
The errormessage is:
PySide6.QtSql.QSqlError("-3100", "QODBC: Unable to execute statement", "[Microsoft][ODBC-Treiber für Microsoft Access] Syntaxfehler in Abfrageausdruck 'Produkt LIKE (%h%\")'.") at 0x000001CA4FB33B88>
Point of interest should be the
'%h%")'."'
Since it shows more characters than typed
I tried to change in several ways, like changing the % to * and?
Still nothing
EDIT:
Here is the minimal reproducible example:
import re
import sys
from PySide6.QtCore import QSize, Qt
from PySide6.QtSql import QSqlDatabase, QSqlTableModel
from PySide6.QtWidgets import (
QApplication,
QLineEdit,
QMainWindow,
QTableView,
QVBoxLayout,
QWidget,
)
Driver= r'DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=D:\scripts\python\pyside_Tutorials\databases\chinook.accdb'
db = QSqlDatabase("QODBC")
db.setDatabaseName(Driver)
db.open()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
container = QWidget()
layout = QVBoxLayout()
self.search = QLineEdit()
self.search.textChanged.connect(self.update_filter)
self.table = QTableView()
layout.addWidget(self.search)
layout.addWidget(self.table)
container.setLayout(layout)
self.model = QSqlTableModel(db=db)
self.table.setModel(self.model)
self.model.setTable("Track")
self.model.select()
self.setMinimumSize(QSize(1024, 600))
self.setCentralWidget(container)
# tag::filter[]
def update_filter(self, s):
s = re.sub("[\W_]+", "", s)
filter_str = 'Name LIKE "%{}%"'.format(s)
self.model.setFilter(filter_str)
print(self.model.lastError())
print(s,type(s))
# end::filter[]
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
This code gives me the errormessage:
<PySide6.QtSql.QSqlError("-3010", "QODBC: Unable to execute statement", "[Microsoft][ODBC-Treiber für Microsoft Access] 1 Parameter wurden erwartet, aber es wurden zu wenig Parameter übergeben.") at 0x0000016FC7535108>
Which means something like: "1 parameter was expected but too few parameters were passed"
MS-Access needs a double apostrophe like:
def update_filter(self, s):
s = re.sub(r"[\W_]+", "", s)
filter_str = f"Produkt '%{s}%'"
self.model.setFilter(filter_str)```

How I can align values exported from database with query in PyQt5 table view

I have the following issue.
I have a very simple code which is connecting to my local sql database, and from there I am exporting a data set with a query. Then I display this data in a PyQT table widget and that is all.
The problem is that I cannot align my data to center of columns. Currently, everything is aligned to the left.
I tried to use different combinations with Qt.AlignCenter but I cannot make it working.
here is my code
import os
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
class BlobDelegate(QtWidgets.QStyledItemDelegate):
def displayText(self, value, locale):
if isinstance(value, QtCore.QByteArray):
value = value.data().decode()
return super(BlobDelegate, self).displayText(value, locale)
def createConnection():
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "stats.db")
db.setDatabaseName(file)
if not db.open():
QtWidgets.QMessageBox.critical(
None,
QtWidgets.qApp.tr("Cannot open database"),
QtWidgets.qApp.tr(
"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."
),
QtWidgets.QMessageBox.Cancel)
return False
return True
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
if not createConnection():
sys.exit(-1)
w = QtWidgets.QTableView()
w.setFont(QtGui.QFont('Arial', 18))
w.horizontalHeader().setStretchLastSection(False)
w.horizontalHeader().setFixedHeight(40)
w.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
# w.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
w.setWordWrap(True)
delegate = BlobDelegate(w)
w.setItemDelegateForColumn(4, delegate)
model = QtSql.QSqlQueryModel()
model.setQuery("SELECT * FROM table_tennis_statistics")
w.setModel(model)
w.resize(1024, 600)
w.show()
sys.exit(app.exec_())
screenshot
You have to modify the displayAlignment property of QStyleOptionViewItem and apply the delegate to all columns:
class BlobDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super(BlobDelegate, self).initStyleOption(option, index)
option.displayAlignment = QtCore.Qt.AlignCenter
def displayText(self, value, locale):
if isinstance(value, QtCore.QByteArray):
value = value.data().decode()
return super(BlobDelegate, self).displayText(value, locale)
delegate = BlobDelegate(w)
w.setItemDelegate(delegate)

QComboBox Auto Complete (QCompleter?)

I have another question with Python GUI widgets in Qt Designer.
I am using Python 3.7 w/ PyQt5.
I have a list of values being generated to a combo box from an SQL table.
The combo box displays all of the values correctly but there are around 100 values total, I want to be able to type and it start to autocomplete so I can quickly find and select any value I may need.
I've done some research which has me baffled.
The list I created in Python is named listofCustOrders as I am building a "business gui" to help me learn more about Python coding. Is there a way to autocomplete this list?
from PyQt5 import QtWidgets, uic
from Classes import CustOrders as CO
import DBConnection as DB
import time
class MyWindow(QtWidgets.QMainWindow):
listofCustOrders = []
def __init__(self):
super(MyWindow, self).__init__()
uic.loadUi('PyGUI.ui',self)
self.init()
def init(self):
global listofCustOrders
listofCustOrders = CO.CustOrders.getCustOrders()
for x in listofCustOrders:
self.cbCONum.addItem(x.getCustOrderNO())
self.cbCONum.currentIndexChanged.connect(self.coSelected)
self.CObutton.clicked.connect(self.Submitted1)
self.SLabel2.hide()
def coSelected(self, text):
cbCOIndex = self.cbCONum.currentIndex()
selectedCO = listofCustOrders[cbCOIndex]
self.RLbl2.setText(selectedCO.getPart())
self.QtyLbl3.setText(str(selectedCO.getQTY()))
def Submitted1(self):
self.SLabel1.hide()
self.SLabel2.show()
CBW = str(self.cbCONum.currentText())
PN = self.RLbl2.text()
QY = self.QLINE.text()
EP = self.EMPLINE.text()
TIMER = time.strftime('%m-%d-%Y %H:%M:%S')
conn1 = DB.DBConnection.getConnection()
cursor = conn1.cursor()
cursor.execute('''
INSERT INTO database.dbo (CustOrderNo, PartNo, Qty, Employee, Color)
VALUES (?, ?, ?, ?, ?)''',
(CBW, PN, QY, EP,TIMER,))
conn1.commit()
conn1.close()
self.QLINE.clear()
self.EMPLINE.clear()
self.RLbl2.clear()
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Qt has QCompleter class for such tasks, here is an example of how you can use it.
completer = QCompleter(wordList, self)
completer.setCaseSensitivity(Qt.CaseInsensitive)
comboBox.setCompleter(completer)
For reference: https://doc.qt.io/qt-5/qcompleter.html, also checkout simple examples that show how to change your completer if your model (set of words) changed - https://doc.qt.io/qt-5/qtwidgets-tools-completer-example.html . The examples are in C++, unfortunately.

PyQt5.QtSql.QSqlError while inserting lineEdit input into database

I want to insert the QLineEdit input into a database
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
from PyQt5 import QtSql as qsql
class Secondwindow(qtw.QWidget):
'''
description einfügen
'''
# Attribut Signal
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# your code will go here
# Messung starten
self.connectdb_button = qtw.QPushButton("Connect to Database ?")
hlaout_layout = qtw.QHBoxLayout()
hlaout_layout.addStretch(1)
hlaout_layout.addWidget(self.connectdb_button)
hlaout_layout.addStretch(1)
# input /nested layout
input1_label = qtw.QLabel("input 1 ")
self.input_1 = qtw.QLineEdit()
input1_hlayout = qtw.QHBoxLayout()
input1_hlayout.addStretch(1)
input1_hlayout.addWidget(input1_label)
input1_hlayout.addWidget(self.input_1)
input1_hlayout.addStretch(1)
input1_hlayout.setAlignment(qtc.Qt.AlignHCenter)
input2_label = qtw.QLabel("input 2 ")
self.input_2 = qtw.QLineEdit()
input2_hlayout = qtw.QHBoxLayout()
input2_hlayout.addStretch(1)
input2_hlayout.addWidget(input2_label)
input2_hlayout.addWidget(self.input_2)
input2_hlayout.addStretch(1)
input2_hlayout.setAlignment(qtc.Qt.AlignHCenter)
input3_label = qtw.QLabel("input 3 ")
self.input_3 = qtw.QLineEdit()
input3_hlayout = qtw.QHBoxLayout()
input3_hlayout.addStretch(1)
input3_hlayout.addWidget(input3_label)
input3_hlayout.addWidget(self.input_3)
input3_hlayout.addStretch(1)
input3_hlayout.setAlignment(qtc.Qt.AlignHCenter)
input4_label = qtw.QLabel("input 4 ")
self.input_4 = qtw.QLineEdit()
input4_hlayout = qtw.QHBoxLayout()
input4_hlayout.addStretch(1)
input4_hlayout.addWidget(input4_label)
input4_hlayout.addWidget(self.input_4)
input4_hlayout.addStretch(1)
input4_hlayout.setAlignment(qtc.Qt.AlignHCenter)
input5_label = qtw.QLabel("input 5 ")
self.input_5 = qtw.QLineEdit()
input5_hlayout = qtw.QHBoxLayout()
input5_hlayout.addStretch(1)
input5_hlayout.addWidget(input5_label)
input5_hlayout.addWidget(self.input_5)
input5_hlayout.addStretch(1)
input5_hlayout.setAlignment(qtc.Qt.AlignHCenter)
# select button
self.select_button = qtw.QPushButton("start query ")
select_buttonlayout = qtw.QHBoxLayout()
select_buttonlayout.setAlignment(qtc.Qt.AlignHCenter)
select_buttonlayout.addStretch(1)
select_buttonlayout.addWidget(self.select_button)
select_buttonlayout.addStretch(1)
# hauptlayout
haupt_layout = qtw.QFormLayout()
haupt_layout.addRow(self.connectdb_button)
haupt_layout.setVerticalSpacing(20)
haupt_layout.addRow(input1_hlayout)
haupt_layout.setVerticalSpacing(20)
haupt_layout.addRow(input2_hlayout)
haupt_layout.setVerticalSpacing(20)
haupt_layout.addRow(input3_hlayout)
haupt_layout.setVerticalSpacing(20)
haupt_layout.addRow(input4_hlayout)
haupt_layout.setVerticalSpacing(20)
haupt_layout.addRow(input5_hlayout)
haupt_layout.setVerticalSpacing(30)
haupt_layout.addRow(select_buttonlayout)
self.setLayout(haupt_layout)
self.show()
# Funktionalität
self.connectdb_button.clicked.connect(self.connect_to_db)
self.select_button.clicked.connect(self.query_data)
def connect_to_db(self):
self.database = qsql.QSqlDatabase.addDatabase('QSQLITE')
self.database.setDatabaseName('qtdatabase.db')
self.database.open()
if self.database.isOpen():
qtw.QMessageBox.about(self, 'connectet', "connection to db successful")
# einfügen siehe pdf
# if not self.db.open():
# error = self.db.lastError().text()
# qtw.QMessageBox.critical(
# None, 'DB Connection Error',
# 'Could not open database file: '
# f'{error}')
# sys.exit(1)
def query_data(self):
mein_input = []
item1 = self.input_1.text()
item2 = self.input_2.text()
item3 = self.input_3.text()
item4 = self.input_4.text()
item5 = self.input_5.text()
mein_input.append(item1)
mein_input.append(item2)
mein_input.append(item3)
mein_input.append(item4)
mein_input.append(item5)
self.query = qsql.QSqlQuery()
self.query.prepare("INSERT INTO userinput(firstcolumns) VALUES(?)")
self.query.addBindValue(mein_input)
if not self.query.execBatch():
print(self.query.lastError().text())
self.close()
# if not self.database.isOpen():
# qtw.QMessageBox.about(self, 'Erfolg', "Data inserted successfully")
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
w = Secondwindow()
sys.exit(app.exec_())
I get this error
Parameter count mismatch
When I insert a list instead of the LineEdit input the function works fine.
I checked that the table exists in the database also the column
output: ['firstcolumns']
import sqlite3
# connect to database query starten
try:
db_connection = sqlite3.connect("qtdatabase.db")
cursor = db_connection.cursor()
column_abfrage = '''SELECT * FROM userinput;'''
cursor.execute(column_abfrage)
cursor.close()
names = list(map(lambda x: x[0], cursor.description))
print(names)
except sqlite3.Error as error:
print(error)
finally:
if (db_connection):
db_connection.close()
print("db connection closed")
sqlite is a database that has a particular characteristic: If the database does not exist when you try to open then it will be created so the open() method will always return True so it can generate silent problems.
In this case it is always advisable to use the full path, this can be explicitly: "/full/path/of/database" or built based on the location of the script, for example if the database is in the same script folder then you can use the following code:
import os
# ...
current_dir = os.path.dirname(os.path.realpath(__file__))
db_path = os.path.join(current_dir, "qtdatabase.db")
self.database = qsql.QSqlDatabase.addDatabase('QSQLITE')
self.database.setDatabaseName(db_path)
if self.database.open():
qtw.QMessageBox.about(self, 'connectet', "connection to db successful")
In your case you are probably using some IDE that when running the script uses a working directory different from the location of the script.

PyQt4 Combobox Not Updating After Initial Selection

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)

Categories