So, I have a mysql database patients where is a client table. I need to show information about clients in GUI window in table. Also I need a filter by main fields to get more detailed information (separately surname, name, series of documents, a series of policy, etc.) with the possibility to search simultaneously in multiple fields. I started with code below.
The question number 1 is: What is easy to use in this task - QSqlTableModel or just QTableWidget?
The question number 2 is: How to filter by main fields? What that mean?
I would be very glad to any thoughts on the subject.
from PyQt4.QtGui import *
from PyQt4.QtSql import *
import sys
def main_():
app = QApplication(sys.argv)
table = QTableWidget()
db = QSqlDatabase.addDatabase("QMYSQL")
db.setHostName("127.0.0.1")
db.setDatabaseName("patients")
db.setUserName("root")
db.setPassword("")
table_name = "client"
if (db.open()==False):
QMessageBox.critical(None, "Database Error",
db.lastError().text())
query = QSqlQuery ("select * from "+ table_name + " limit 10")
table.setColumnCount(query.record().count())
table.setRowCount(query.size())
index=0
while (query.next()):
table.setItem(index,0,QTableWidgetItem(query.value(0).toString()))
table.setItem(index,1,QTableWidgetItem(query.value(1).toString()))
index += 1
table.show()
return app.exec_()
if __name__ == '__main__':
main_()
I think you would like to read more about Model-View concept.
After opening your database connection, you create a model of the data, then attach it to the view... no iterating needed:
model = QtSql.QSqlTableModel()
# selecting a table
model.setTable("clients")
# applying a filter
model.setFilter("id > 0")
# i.e.: executing the query
model.select()
# the widget to show the data
tableView = QTableView()
# attach the model to widget
tableView.setModel(model)
tableView.show()
Also: if you cannot open the database - exit the program, not only present a message box.
I hope that helps a bit.
Related
I am developing an application that reads MS Access DBs to produce extracts and data manipulation into SharePoint. One of my challenges is that I will need to write a lengthy GUI interface with many simple events. To keep the code organized I will be writing subroutines and import into the main program. As a result there will be code that needs to run outside the class and access objects inside the class. I am having a problem determining the proper syntax.
Or is there a way to import directly into the class to avoid the redefining issue?
In the InitUI there is a call to subroutine called TestIt() in that sub routine I print a message to verify the code is getting called. Then I try to set the text value of a Single Line Edit object from the main window. The question what is the proper way to call it? Do I need to redefine the variable.
The main program is this:
from PyQt5 import QtCore, QtGui, QtWidgets
from Main import Ui_MainWin
import sys, time, os, configparser
import pyodbc
from ServerQueue import *
class MainWindow_EXEC():
def __init__(self): # This section has to be laid out this way
app = QtWidgets.QApplication(sys.argv)
win = QtWidgets.QMainWindow()
self.ui = Ui_MainWin()
self.ui.setupUi(win)
self.initUI() # Inits that need to happen before start up
win.setWindowTitle("This is the Title!")
#win.resize(800,600)
win.show()
sys.exit(app.exec_())
def initUI(self):
TestIt()
self.ui.cmbAppCodes.currentIndexChanged.connect(self.selectionchange)
try:
self.conn = pyodbc.connect(r'Driver={Microsoft Access Driver
(*.mdb,*.accdb)};DBQ='+ dbPath + ';',autocommit=True)
self.cursor = self.conn.cursor()
except:
print("Error opening the database!")
qApp.quit()
print(dbPath)
self.loadAppCodes()
def loadAppCodes(self):
self.ui.cmbAppCodes.clear()
sql = "SELECT [App Code], [App Name] from [App List] ORDER BY [App Code];"
self.cursor.execute(sql)
res = self.cursor.fetchall()
for code in res:
self.ui.cmbAppCodes.addItem("{} - {}".format(code[0], code[1]))
def selectionchange(self,i):
App = self.ui.cmbAppCodes.itemText(i)
sql = "SELECT * FROM [LCM Application List] WHERE [App Code] = '" + App[0:3] + "';"
self.cursor.execute(sql)
res = self.cursor.fetchone()
self.ui.lneAPM.setText(res[3])
self.ui.lneTPM.setText(res[21])
self.ui.lneAPO.setText(res[4])
#-----------------------------------------------------------------------------
# Load up the init file
#-----------------------------------------------------------------------------
def iniSettings():
if sys.platform == "win32":
os.system('cls')
Home = os.path.expanduser('~') + "\\"
D = "W"
else:
os.system('clear')
Home = os.path.expanduser('~') + "/"
D = "O"
config = configparser.ConfigParser()
config.read(Home + 'LCM.ini') # Path to LCM.ini file
Configs = config['DEFAULT'] # Read the DEFAULT section
return [Home, Configs['LCMDB'], Configs['CR1'], D]
##########################################################################################
# Main Body
###########################################################################################
if __name__ == "__main__":
Paths = iniSettings() # This is to pull the ini data for paths and what not.
hmPath = Paths[0]
dbPath = Paths[1]
cr1Path = Paths[2]
myOS = Paths[3]
print("Home: {}\nCR1: {}\nDatabase: {}\n".format(hmPath, cr1Path, dbPath))
MainWindow_EXEC() # Becuase the init is laid out this way this is all that is needed
print("Application completed")enter code here
#This file is imported and is called ServerQueue.py
def TestIt():
print("TestIt")
self.ui.lneAPM.setText("Testing")
#-----------------------------------------------------------------------------
# Main Processing Loop
#-----------------------------------------------------------------------------
if __name__ == "__main__":
print("\n ********* \n This file is not meant to be run directly! \n ********* \n")
You are doing everything backwards. You are trying to get the elements of business logic to consume the GUI, but the correct logic is the reverse: The GUI consumes the information from the elements of business logic. Considering the above, the Test () function should not modify the GUI directly, but provide the information to the GUI, one way to implement that is to return the text.
def TestIt():
print("TestIt")
return "Testing"
# ...
def initUI(self):
self.ui.lneAPM.setText(TestIt())
# ...
If the above does not solve the problem then you probably have an XY problem so I recommend you to rewrite your question better providing a better MRE
I try to convert an old pyqt4 program to pyqt5 and change to python x64.
When querying the db the first time, I get a "Driver not loaded" error.
When I use mysql.connector.connect I can get data from the db without any problems.
I tried the last four nights to solve this problem and at the moment, don´t know any further.
Things I tried:
installed qt
install 32 bit python
download winpython which includes pyqt
deinstall of all python on the system an reinstall
installed mysql / and connector
tryied anaconda
I believe one of my problems is, that print(QtSql.QSqlDatabase.drivers()) only gives me:
['QSQLITE', 'QODBC', 'QODBC3', 'QPSQL', 'QPSQL7']
In the corresponding folder (as far as I understand) "C:\Python37\Lib\site-packages\PyQt5\Qt\plugins\sqldrivers" are only the following three files:
qsqlite.dll,qsqlodbc.dll,qsqlpsql.dll
I downloaded the qsqlmysql.dll file from https://raw.githubusercontent.com/pyqt/python-qt5/master/PyQt5/plugins/sqldrivers/ , After that "QMYSQL" and "QMYSQL3" are listed with print(QtSql.QSqlDatabase.drivers()) , but with this file my python crashes hard.
What could I try next? What do I do wrong?
Database is "Server-Version: 5.5.57-MariaDB - MariaDB Server"
from PyQt5 import QtCore, QtGui, QtWidgets, uic, QtSql
from PyQt5.QtSql import (QSql, QSqlDatabase, QSqlDriver,
QSqlDriverCreatorBase, QSqlError, QSqlField, QSqlIndex, QSqlQuery,
QSqlQueryModel, QSqlRecord, QSqlRelation, QSqlRelationalDelegate,
QSqlRelationalTableModel, QSqlResult, QSqlTableModel)
class MyApp(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.db_connection = QSqlDatabase.database()
print(QtSql.QSqlDatabase.drivers())
self.db_connection = QSqlDatabase.addDatabase("QMYSQL")
self.db_connection.setHostName(db_sql_server_ip)
self.db_connection.setDatabaseName(db_db_name)
self.db_connection.setUserName(db_user)
self.db_connection.setPassword(db_pw)
self.db_connection.setPort(int(db_sql_server_port))
self.db_connection.setConnectOptions("MYSQL_OPT_RECONNECT=1")
# first function which will get sql data
query = QSqlQuery()
# for demonstration
self.setting_name='123'
query.prepare("SELECT name,settings_value,pickled_data FROM settings WHERE user_id = ? AND active='1' AND name = ?")
query.addBindValue(_user_id)
query.addBindValue(self.setting_name)
query_status = query.exec_()
if query_status is not True:
errorText = query.lastError().text()
QtWidgets.QMessageBox.critical(self, 'Query error', errorText + str(57))
Code without qt can access sql:
# the following works, but I need qt..
import mysql.connector
db = mysql.connector.connect(host=db_sql_server_ip, # your host, usually localhost
user=db_user, # your username
passwd=db_pw, # your password
db=db_db_name) # name of the data base
# you must create a Cursor object. It will let
# you execute all the queries you need
cur = db.cursor()
# Use all the SQL you like
cur.execute("SELECT * FROM users")
# print all the first cell of all the rows
for row in cur.fetchall():
print(row[0])
db.close()
I have a Qt form with two QDateTimeEdit controls. I want to use these dates and times in the WHERE clause of a sqlite3 SELECT statement when the user presses the SEARCH button. But I can’t figure out how to extract the date and time components from the QDateTime object in order to construct the julian dates that Sqlite needs.
I have spent many hours reading and rereading the Qt C++ class reference documentation that the PyQt5 Reference Guide links to and trying different things, including trying to do various conversion using QVariant but all to no avail. Seems to me that the QString QDateTime::toString(const QString &format)const method is what I need to use but PyQt5 bindings don’t seem to include QString or the toString method? I came across a post that seemed to confirm that this method is what I need but it was referring to PyQt4. Is it possible that support for this method was dropped in the PyQt5 release?
According to the documentation:
The date and time functions use a subset of IS0-8601 date and time
formats. The date() function returns the date in this format:
YYYY-MM-DD. The time() function returns the time as HH:MM:SS. The
datetime() function returns "YYYY-MM-DD HH:MM:SS". The julianday()
function returns the Julian day - the number of days since noon in
Greenwich on November 24, 4714 B.C. (Proleptic Gregorian calendar).
The strftime() routine returns the date formatted according to the
format string specified as the first argument. The format string
supports the most common substitutions found in the strftime()
function from the standard C library plus two new substitutions, %f
and %J. The following is a complete list of valid strftime()
substitutions:
The time data type must have the format YYYY-MM-DD HH:MM:SS, In Qt (and therefore in PyQt) we can get it using the toString function:
QDateTime.toString("yyyy-MM-dd hh:mm:ss")
Example:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtSql import *
import sys
def createConnection():
db = QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('memory')
if not db.open():
QMessageBox.critical(None, qApp.tr("Cannot open database"),
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."),
QMessageBox.Cancel)
return False
query = QSqlQuery()
query.exec_("create table test(id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, dt DATETIME default current_timestamp)")
query.exec_("insert into test (dt) values('2007-01-01 10:00:00')")
query.exec_("insert into test (dt) values('2008-01-01 10:00:00')")
query.exec_("insert into test (dt) values('2009-01-01 10:00:00')")
query.exec_("insert into test (dt) values('2010-01-01 10:00:00')")
return True
class Widget(QWidget):
def __init__(self, change_cursor=True, parent=None):
QWidget.__init__(self, parent=parent)
layout = QHBoxLayout(self)
self.fromDateTE = QDateTimeEdit()
self.toDateTE = QDateTimeEdit()
layout.addWidget(self.fromDateTE)
layout.addWidget(self.toDateTE)
btn = QPushButton("Select")
layout.addWidget(btn)
btn.clicked.connect(self.onClick)
def onClick(self):
_from = self.fromDateTE.dateTime().toString("yyyy-MM-dd hh:mm:ss")
_to = self.toDateTE.dateTime().toString("yyyy-MM-dd hh:mm:ss")
sql = "SELECT * from test WHERE dt BETWEEN datetime('{}') AND datetime('{}')".format(_from, _to)
query = QSqlQuery(sql)
while query.next():
print(query.value(1))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
if not createConnection():
sys.exit(1)
w = Widget()
w.show()
sys.exit(app.exec_())
If we place the dates as shown in the following figure we get the result shown below.
Screenshot:
Output:
2008-01-01 10:00:00
I'm using pyqt4, qt designer and postgresql database. I've managed to make part of my GUI which inserts new data to my db, but now I'd like to try something else for my Delete section of GUI. Since I'm into python/pyqt for maybe a week now I'll try to explain what I'm looking for as simple as possible. First off: here is part of my code which I used for adding data:
...
class database_GUI(QtGui.QWidget, Ui_Main_GUI_Widget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.setupUi(self)
self.Add_button.clicked.connect(self.open_sub_ui)
def open_sub_ui(self):
if self.Add_comboBox.currentText() == "kirurg":
global kirurg
kirurg = kirurg_GUI()
kirurg.show()
...
class kirurg_GUI(QtGui.QWidget, Ui_kirurg_Widget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.setupUi(self)
self.kirurg_Add_button.clicked.connect(self.insert_kirurg)
def insert_kirurg(self):
oib_kir = self.kirurg_oib_kir_lineEdit.text()
if not oib_kir:
QtGui.QMessageBox.critical(self, 'Query error', 'oib_kir must not be empty')
else:
query = QSqlQuery()
status = query.exec("INSERT INTO kirurg (oib_kir, prezime, ime, adresa, broj_telefona)"
"VALUES ('%s', '%s', '%s', '%s', '%s')" % (''.join(self.kirurg_oib_kir_lineEdit.text()),
''.join(self.kirurg_prezime_lineEdit.text()),
''.join(self.kirurg_ime_lineEdit.text()),
''.join(self.kirurg_adresa_lineEdit.text()),
''.join(self.kirurg_broj_telefona_lineEdit.text())))
if status is not True:
errorText = query.lastError().text()
QtGui.QMessageBox.critical(self, 'Query error', errorText)
So basically, new GUI would open for every other comboBox option. If user selects comboBox option 1 and clicks Add, it opens GUI for adding data to table number 1, if user selects comboBox option 2 and clicks Add, it opens GUI for adding data to table number 2, and so on.
But, I'd like to be more flexibile with my Delete GUI so I was wondering is it possible to make something like this:
query.exec("DELETE FROM ('%s') WHERE ('%s) = ('%s'))
These ('%s') things should be strings, which my program would read from lineEdit.text(). So basically, program would read data that user enters in lineEdit and according to them it would know which row of data to delete and from which table. I hope you can see my point and question here. If it's not possible, I guess I'll just make another 12 copy pasted GUI's for each table, but I'm hoping there's better solution to that.
How can I perform post processing on my SQL3 database via python? The following code doesn't work, but what I am trying to do is first create a new database if not exists already, then insert some data, and finally execute the query and close the connection. But I what to do so separately, so as to add additional functionality later on, such as delete / updater / etc... Any ideas?
class TitlesDB:
# initiate global variables
conn = None
c = None
# perform pre - processing
def __init__(self, name):
import os
os.chdir('/../../')
import sqlite3
conn = sqlite3.connect(name)
c = conn.cursor()
c.execute('CREATE TABLE IF NOT EXISTS titles (title VARCHAR(100) UNIQUE)')
# insert a bunch of new titles
def InsertTitles(self, list):
c.executemany('INSERT OR IGNORE INTO titles VALUES (?)', list)
# perform post - processing
def __fina__(self):
conn.commit()
conn.close()
You could create a context manager to do the pre- and postprocessing.
import contextlib
#contextlib.contextmanager
def titles_cursor():
# perform pre - processing
conn = sqlite3.connect(name)
c = conn.cursor()
c.execute('CREATE TABLE IF NOT EXISTS titles (title VARCHAR(100) UNIQUE)')
yield c
# perform post - processing
conn.commit()
conn.close()
Use it in a with statement:
with titles_cursor() as c:
c.executemany('INSERT OR IGNORE INTO titles VALUES (?)', list)
First, wouldn't it be better to avoid having the sql connection inside the __init__?
You will have a problem if you want to use this class in the same instance after using __fina__.
You could have it in another method and call it and call the connection closing method when needed and commit after each method is executed.
Here is what I use : Create a class method that connects to the db and executes a query from an argument,commits and closes connection and you pass any query you want executed as an argument of that method.You can simply call this method anytime you want.
And the best about this is that you can create a method that passes multiple queries as arguments before closing db connection.
This is specially usefull if have to use sql connections to the same db in another class without using a set of methods each time you need to execute a sql query.
Here is a little example I used with MySQLdb module, It's pretty simple but it worked.
import MySQLdb
class DbQuery:
'''Here is the class I talked about'''
def __init__(self):
'''You can define the main queries here but it's not necessary
They can be global variables
If you don't have class dependency from which you get variables
you might not even need to define __init__'''
def Sql_Connect(self):
self.db = MySQLdb.connect("localhost","root","","data_db" )
self.cursor = db.cursor()
def Sql_Commit(self):
self.db.commit()
print "Info : Database updated"
except:
self.db.rollback()
print "Error : Database rollback"
self.db.close()
def Query(self,query):
self.Sql_Connect()
try :
self.cursor.execute(query)
self.Sql_Commit()
The only thing important is to remember the query structure.