Qt6 Python SQLServerからデータ照会とPDF作成(QTableView)
Qt6Creatorでプロジェクトsqlserverappを作成
sqlserverapp.pyproject sqlserverapp.py form.ui を作成
form.ui は、pyside6-uic form.ui -o ui_form.py を実行して ui_form.py を作成します。pymssqlを利用してSQLServerへ接続します。pdf作成は、reportlabを利用します。
form.ui
sqlservercon.py
# This Python file uses the following encoding: utf-8
import sys
from datetime import date, datetime
from typing import Any, List
from PySide6.QtCore import (
Qt,
QModelIndex,
QAbstractTableModel
)
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QTableView,
QMessageBox,
)
6
# Important:
# You need to run the following command to generate the ui_form.py file
# pyside6-uic form.ui -o ui_form.py, or
# pyside2-uic form.ui -o ui_form.py
from ui_form import Ui_sqlserverapp
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4, landscape, portrait
from reportlab.lib.pagesizes import A3
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.platypus import Table, TableStyle
import webbrowser
import pymssql
import csv
class SimpleTableModel(QAbstractTableModel):
def __init__(self, list, headers = [], parent = None):
QAbstractTableModel.__init__(self, parent)
self.list = list
self.headers = headers
def rowCount(self, parent):
return len(self.list)
def columnCount(self, parent):
return len(self.list[0])
def flags(self, index):
return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
def data(self, index, role):
if role == Qt.EditRole:
row = index.row()
column = index.column()
return self.list[row][column]
elif role == Qt.DisplayRole:
row = index.row()
column = index.column()
value = self.list[row][column]
# Perform per-type checks and render accordingly.
if isinstance(value, datetime):
# Render time to YYY-MM-DD.
return value.strftime("%Y-%m-%d")
elif isinstance(value, int):
# Render int to 0 dp
return "%d" % value
elif isinstance(value, float):
# Render float to 2 dp
return "%.2f" % value
elif isinstance(value, str):
# Render strings with quotes
return "%s" % value
# Default (anything not captured above: e.g. int)
else:
return value
elif role == Qt.TextAlignmentRole:
row = index.row()
column = index.column()
value = self.list[row][column]
if isinstance(value, int) or isinstance(value, float):
# Align right, vertical middle.
return int(Qt.AlignRight) + int(Qt.AlignVCenter) #,Qt.AlignHCenter,Qt.AlignRight,Qt.AlignCenter,Qt.AlignTop,Qt.AlignVCenter,Qt.AlignBottom
else:
return int(Qt.AlignLeft) + int(Qt.AlignVCenter)
def setData(self, index, value, role = Qt.EditRole):
if role == Qt.EditRole:
row = index.row()
column = index.column()
self.list[row][column] = value
self.dataChanged.emit(index, index)
return True
return False
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
if section < len(self.headers):
return self.headers[section]
else:
return "not implemented"
else:
return "%d" % (section + 1)
class sqlserverapp(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_sqlserverapp()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.select)
self.ui.pushButton_2.clicked.connect(self.csvwrt)
self.ui.pushButton_3.clicked.connect(self.pdfwrt)
self.ui.pushButton_4.clicked.connect(self.exit)
self.ui.radioButton.setChecked(True)
self.ui.label.setStyleSheet("background-color: pink")
self.ui.pushButton.setStyleSheet("background-color: yellow")
self.ui.pushButton_2.setStyleSheet("background-color: pink")
self.ui.pushButton_3.setStyleSheet("background-color: aqua")
self.ui.pushButton_4.setStyleSheet("background-color: green")
#self.ui.lineEdit_2''.setEchoMode(QlineEdit.Password)
rowdata = ['']
coldata = ['']
model = SimpleTableModel(rowdata, coldata)
self.ui.tableView.setModel(model) # create model and set
self.ui.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) #列幅の自動調整 0:Interactive 1:Stretch 2:Fixed 3:ResizeToContents
def exit(self) -> None:
sys.exit()
#self.close()
def select(self):
server = self.ui.lineEdit.text()
username = self.ui.lineEdit_2.text()
password = self.ui.lineEdit_3.text()
database = self.ui.lineEdit_4.text()
try:
conn = pymssql.connect( server , username , password, database )
tablenm = self.ui.lineEdit_5.text()
wherenm = self.ui.lineEdit_6.text()
ordernm = self.ui.lineEdit_7.text()
cursor = conn.cursor()
colsql = "select count(*) from sys.columns where object_id=object_id('" + tablenm + "')"
cursor.execute(colsql)
results = cursor.fetchall()
for res in results:
colcount = res[0]
self.ui.label_3.setText(str(colcount) + ' 項目')
#self.ui.tableView.columnCountChanged()
namsql = " SELECT t.name as tblnam,c.name as colname,type_name(user_type_id) as typname,max_length as maxleng ,CASE WHEN is_nullable = 1 THEN 'YES' ELSE 'NO' END as nullok"
namsql += " FROM sys.objects t INNER JOIN sys.columns c ON t.object_id = c.object_id"
namsql += " WHERE t.type = 'U' AND t.name='" + tablenm + "'"
namsql += " ORDER BY c.column_id"
cursor.execute(namsql)
results = cursor.fetchall()
coldata = []
i=0
for res in results:
coldata.append(res[1])
i += 1
if wherenm == '':
sql = "SELECT * from " + tablenm
else:
sql = "select * from " + tablenm + " where " + wherenm
pass
if ordernm == '':
sql += ''
else:
sql += " order by " + ordernm
pass
cursor.execute(sql)
results = cursor.fetchall()
# 実行結果
self.ui.label_2.setText(str(cursor.rowcount) + ' 件')
# データを表示
rowdata = []
row = 0
for res in results:
i=0
rowone = []
while i < colcount:
try:
strint = int(str(res[i]))
strfloat = float(str(res[i]))
if strint == strfloat:
rowone.append(strint)
else:
rowone.append(strfloat)
except:
rowone.append(str(res[i]))
else:
pass
i += 1
row += 1
rowdata.append(rowone)
model = SimpleTableModel(rowdata, coldata)
self.ui.tableView.setModel(model) # create model and set
cursor.close()
conn.close()
except:
QMessageBox.warning(self,'エラー','接続エラー')
else:
pass
return coldata, colcount, results, tablenm
def pdfwrt(self):
results = self.select()
tablenm = results[3]
try:
#用紙サイズ
ans = 1
if self.ui.radioButton.isChecked() == True:
ans = 1 #A4タテ
elif self.ui.radioButton_2.isChecked() == True:
ans = 2 #A4ヨコ
elif self.ui.radioButton_3.isChecked() == True:
ans = 3 #A3ヨコ
else:
pass
# 白紙をつくる
FILENAME = 'sqlservercon.pdf'
if ans == 1:
c = canvas.Canvas(FILENAME, pagesize=portrait(A4))
gyosuu = 50 #a4縦 50
elif ans == 2:
c = canvas.Canvas(FILENAME,pagesize=landscape(A4))
gyosuu = 34 #a4横 34
elif ans == 3:
c = canvas.Canvas(FILENAME,pagesize=landscape(A3))
gyosuu = 50 #a3横 34
else:
pass
# フォント登録
pdfmetrics.registerFont(UnicodeCIDFont('HeiseiMin-W3'))
pdfmetrics.registerFont(UnicodeCIDFont("HeiseiKakuGo-W5"))
page = 0
#テーブル項目名のセット
tb = []
hedcol = results[0]
tb.append(hedcol,) #tb[[a,b,c,....],]
row = 0
for res in results[2]:
rowdta = []
i = 0
for res_col in results[0]:
rowdta.append(str(res[i]))
i += 1
tb.append(rowdta)
if row == gyosuu:
c.setFont('HeiseiKakuGo-W5', 12)
if ans == 1:
c.drawString(10*mm, 290*mm, "* " + tablenm + "一覧表 *") #a4 縦 書き出し(横位置, 縦位置, 文字)
elif ans == 2:
c.drawString(10*mm, 200*mm, "* " + tablenm + "一覧表 *") #a4 横 書き出し(横位置, 縦位置, 文字)
elif ans == 3:
c.drawString(10*mm, 290*mm, "* " + tablenm + "一覧表 *") #a3 横 書き出し(横位置, 縦位置, 文字)
else:
pass
page += 1
strpage = 'page.' + str(page)
if ans == 1:
c.drawString(170*mm, 290*mm,strpage) # a4 縦 書き出し(横位置, 縦位置, 文字)
elif ans == 2:
c.drawString(270*mm, 200*mm,strpage) # a4 横 書き出し(横位置, 縦位置, 文字)
elif ans == 3:
c.drawString(370*mm, 290*mm,strpage) # a3 横 書き出し(横位置, 縦位置, 文字)
else:
pass
table = Table(tb,colWidths=40*mm, rowHeights=5*mm) # テーブルサイズ 列 行
table.setStyle(TableStyle([ # tableの装飾
('FONT', (0, 0), (-1, -1), 'HeiseiMin-W3', 11), #フォント,サイズ FONTL
('BOX', (0, 0), (-1, -1), 1, colors.black), #罫線外側 BOX
('INNERGRID', (0, 0), (-1, -1), 1, colors.black), #罫線内側 INNERGRID
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), #フォントの場所垂直 TOP MIDDLE BOTTOM
("ALIGN", (0,0), (-1,-1), "LEFT"), #フォントの場所水平 LEFT CENTER RIGHT
('BACKGROUND',(0,0),(1,0),colors.yellow),
('BACKGROUND',(0,1),(-1,3),colors.lightskyblue),
('TEXTCOLOR',(0,0),(-1,-1),colors.black),
]))
#セル結合 span #横線 lineabove #縦線 linebefore
c.setFont('HeiseiKakuGo-W5', 12)
if ans == 1:
table.wrapOn(c, 10*mm, 20*mm) # table a4 縦 書き出し位置 横 縦
table.drawOn(c, 10*mm, 20*mm)
elif ans == 2:
table.wrapOn(c, 10*mm, 10*mm) # table a4 横 書き出し位置 横 縦
table.drawOn(c, 10*mm, 10*mm)
elif ans == 3:
table.wrapOn(c, 10*mm, 20*mm) # table a3 横 書き出し位置 横 縦
table.drawOn(c, 10*mm, 20*mm)
else:
pass
# Canvasに書き込み
c.showPage()
row = -1
tb = []
tb.append(hedcol,)
row += 1
if row > 0:
num = 1
if ans == 1:
kasan = 2
elif ans == 2:
kasan = 2
elif ans == 3:
kasan = 2
else:
pass
while num < gyosuu + kasan - row:
tb.append(['','',''])
num += 1
c.setFont('HeiseiKakuGo-W5', 12)
if ans == 1:
c.drawString(10*mm, 290*mm, "* " + tablenm + "一覧表 *") #a4 縦 書き出し(横位置, 縦位置, 文字)
elif ans == 2:
c.drawString(10*mm, 200*mm, "* " + tablenm + "一覧表 *") #a4 横 書き出し(横位置, 縦位置, 文字)
elif ans == 3:
c.drawString(10*mm, 290*mm, "* " + tablenm + "一覧表 *") #a3 横 書き出し(横位置, 縦位置, 文字)
else:
pass
page += 1
strpage = 'page.' + str(page)
if ans == 1:
c.drawString(170*mm, 290*mm,strpage) # a4 縦 書き出し(横位置, 縦位置, 文字)
elif ans == 2:
c.drawString(270*mm, 200*mm,strpage) # a4 横 書き出し(横位置, 縦位置, 文字)
elif ans == 3:
c.drawString(370*mm, 290*mm,strpage) # a3 横 書き出し(横位置, 縦位置, 文字)
else:
pass
table = Table(tb,colWidths=40*mm, rowHeights=5*mm)
table.setStyle(TableStyle([ # tableの装飾
('FONT', (0, 0), (-1, -1), 'HeiseiMin-W3', 11),
('BOX', (0, 0), (-1, -1), 1, colors.black),
('INNERGRID', (0, 0), (-1, -1), 1, colors.black),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
]))
if ans == 1:
table.wrapOn(c, 10*mm, 20*mm) # table a4 縦 書き出し位置 横 縦
table.drawOn(c, 10*mm, 20*mm)
elif ans == 2:
table.wrapOn(c, 10*mm, 10*mm) # table a4 横 書き出し位置 横 縦
table.drawOn(c, 10*mm, 10*mm)
elif ans == 3:
table.wrapOn(c, 10*mm, 20*mm) # table a3 横 書き出し位置 横 縦
table.drawOn(c, 10*mm, 20*mm)
else:
pass
# Canvasに書き込み
c.showPage()
# ファイル保存
c.save()
# ブラウザーで表示
webbrowser.open(FILENAME)
except:
QMessageBox.warning(self,'エラー','PDF作成エラー')
else:
pass
def csvwrt(self):
results = self.select()
try:
with open('test.csv', 'w', newline='') as f:
writer = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC) #数値以外のフィールドをダブルクオートで囲む
writer.writerow(results[0]) #カラム名配列
for res in results[2]:
i=0
resdta = []
while i < results[1]:
resdta.append(res[i])
i += 1
writer.writerow(resdta)
QMessageBox.warning(self,'CSV作成','csvファイルを作成しました。')
except:
QMessageBox.warning(self,'エラー','CSV作成エラー')
else:
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = sqlserverapp()
widget.show()
sys.exit(app.exec())
実行画面
Ubuntuで実行 接続で select を実行しテーブル表示。CSV作成で select を実行し csvファイルを作成。PDF作成で select を実行し指定サイズで pdfファイルを作成。TableWidgetより処理が速くなりました。