Python桌面应用之XX学院水卡报表查询系统(Tkinter+cx_Oracle)

一、功能样式

Python桌面应用之XX学院水卡报表查询系统功能:

连接Oracle数据库,查询XX学院水卡操作总明细报表,汇总数据报表,个人明细报表,进行预览并且支持导出报表

1.总明细报表样式

2.汇总明细样式

3.个人明细样式

4.导出报表样式

5.错误提示样式

二、核心点

1. 安装cx_Oracle :使用cx_Oracle三方库连接Oracle,该库使用的python版本略低,可以在[https://cx-oracle.readthedocs.io/en/latest/\](https://cx-oracle.readthedocs.io/en/latest/进行查询,安装前先确定:python版本、Orale客户端版本(要不都是64位,要不都是32位),安装cx_Oracle的版本位数是跟python的位数相关的。

使用代码进行测试

python 复制代码
import cx_Oracle
# 账户  密码  ip:1521/实例名
conn = cx_Oracle.connect('system','Yxq123456','127.0.0.1:1521/ecard'
# 挂载数据库连接游标
self.cursor = conn.cursor()
print('连接数据库成功!')

2. 多参数查询Sql: sql语句使用:参数名来定义参数,多参数使用cursor.execute(sql,(参数1,参数2)).fetchall()来查询

python 复制代码
sql = "select a.outid ,a.name ,b.opfare,b.opdt,b.dscrp from base_customers a,rec_cust_acc b where a.customerid = b. customerid and b.opdt >= to_date(:preopdt,'yyyy-MM-dd HH24:mi:ss') and b.opdt <= to_date(:nextopdt,'yyyy-MM-dd HH24:mi:ss') order by b.opdt desc"
preopdt=self.pretimeInput.get()
nextopdt=self.nexttimeInput.get()
data = self.cursor.execute(sql,(preopdt,nextopdt)).fetchall()

**3. Treeview表格组件的使用:**这里使用了三个报表,其实可以将打开的Treeview做成一个表格class类,要使用的时候直接生成要使用的对象,传入该对象的大小,heading标题,data数据即可。

python 复制代码
# 明细查询
    def Consumedetail(self):
        self.consumedetail = tk.Tk()
        self.consumedetail.title('XX学院明细查询')
        self.consumedetail.geometry("1000x600")
        # 加载滚动条
        scrollBar = tk.Scrollbar(self.consumedetail)
        scrollBar.pack(side = tk.RIGHT,fill = tk.Y)
        self.tree = ttk.Treeview(self.consumedetail, columns=('outid', 'name', 'opfare', 'opdt','dscrp'), show="headings", displaycolumns="#all",yscrollcommand = scrollBar.set)
        self.tree.pack()
        self.tree.heading('outid', text="学号", anchor=tk.W)
        self.tree.heading('name', text="姓名", anchor=tk.W)
        self.tree.heading('opfare', text="交易金额", anchor=tk.W)
        self.tree.heading('opdt', text="交易日期", anchor=tk.W)
        self.tree.heading('dscrp', text="交易类型", anchor=tk.W)
        # 设置关联
        scrollBar.config(command = self.tree.yview)
        # 每次打开清空页面
        for item in self.tree.get_children():
            self.consumedetail.tree.delete(item)
        sql = "select a.outid ,a.name ,b.opfare,b.opdt,b.dscrp from base_customers a,rec_cust_acc b where a.customerid = b. customerid and b.opdt >= to_date(:preopdt,'yyyy-MM-dd HH24:mi:ss') and b.opdt <= to_date(:nextopdt,'yyyy-MM-dd HH24:mi:ss') order by b.opdt desc"
        preopdt=self.pretimeInput.get()
        nextopdt=self.nexttimeInput.get()
        data = self.cursor.execute(sql,(preopdt,nextopdt)).fetchall()
        # print(data)
        # data = [['2013090101','张三','100','2023-10-19','PC存款']]
        for itm in data:
            self.tree.insert("",tk.END,values=itm)
            self.tree.pack(padx=10,pady=10, fill=tk.BOTH,expand=1)
        exportbtn = tk.Button(self.consumedetail,text='导出',width=8,command=self.export).pack()

4. 导出数据自定义文件名 :报表里面导出数据,其实使用遍历treeview组件数据,在进行整理后写入csv文件,自定义文件名是使用filedialog.asksaveasfilename来打开文件框,里面的文件类型使用参数filetypes ,输入文件名后获取名称生成文件。这里导出的文件就只是csv文件,如果需要其他文件类型,可以自行导入其他三方库。

python 复制代码
     def export(self):
        # 导出export        
         # 打开文件夹选择对话框
        
        # 更新标签文本
        # print(folder_path)
        list = []
        columns = []
        # 获取表格内容id
        for row_id in self.tree.get_children():
            list.append(self.tree.item(row_id)['values'])
        print(len(self.tree.get_children()))   
        # 通过第一行获取列数生成标题
        # print(self.tree.item)
        if len(self.tree.get_children()) != 0:
            print('ok')
            folder_path = filedialog.asksaveasfilename(title='请选择一个文件',filetypes=[("CSV", ".csv")]) 
            for i in range(0,len(self.tree.item('I001')['values'])):
                columns.append(self.tree.heading(column=i)['text'])
        # 导出csv
            with open(f'{folder_path}.csv','w',newline='') as csvfile:
                fieldnames = columns
                writer = csv.writer(csvfile)
                writer.writerow(fieldnames)
                writer.writerows(list)
        else:
            messagebox.showwarning("提示", "没有数据,无法导出")
            return

5.遍历Treeview表格数据与标题 :获取Treeview里面的数据与标题,这里现获取id值,然后通过item获取['values']值,获取标题这里先遍历了第一行有多少数据,然后使用self.tree.heading(column=i)['text']来获取标题。

python 复制代码
 # 获取表格内容id
for row_id in self.tree.get_children():
    list.append(self.tree.item(row_id)['values'])
python 复制代码
 # 通过第一行获取列数生成标题
for i in range(0,len(self.tree.item('I001')['values'])):
    columns.append(self.tree.heading(column=i)['text'])

三、完整代码

python 复制代码
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import cx_Oracle
import time
import csv
from tkinter import filedialog

# mainapp
class mainApp(object):
    def __init__(self,parent,**kwargs):
        self.root = parent
        current_timestamp = time.time()
         # 将时间戳转换为本地时间的struct_time对象
        local_time = time.localtime(current_timestamp)
         # 使用strftime()方法将struct_time对象格式化为指定的时间字符串  
        # 挂在self时间
        self.pretime = time.strftime("%Y-%m-%d 00:00:00", local_time)
        self.nexttime = time.strftime("%Y-%m-%d %H:%M:%S", local_time)

        conn = cx_Oracle.connect('system','Yxq123456','127.0.0.1:1521/ecard')
        # conn = cx_Oracle.connect('ccense','XCXY123*','127.0.0.1:1521/ecard')
        # 挂载数据库连接游标
        self.cursor = conn.cursor()
        print('连接数据库成功!')

        self.root.config(**kwargs)
        self.root.title('XX学院')
        self.root.resizable(False, False)
        self.create_widgets()
        # 获取屏幕尺寸
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()

        # 确定窗口位置,并设置大小
        x_coordinate = (screen_width / 2) - 300 # 300是窗口的宽度
        y_coordinate = (screen_height / 2) - 200 # 200是窗口的高度
        self.root.geometry('650x400+{}+{}'.format(int(x_coordinate), int(y_coordinate)))
        # self.root.geometry("600x400")
        
    # 创建UI
    def create_widgets(self):
        self.titleLab = tk.Label(self.root,text='XX学院水卡报表管理系统',font=("kaiti",18)).place(x=190,y=30)
        self.outidLab = tk.Label(self.root,text='学号:').place(x=80,y=100)
        self.outidInput = tk.Entry(self.root, width=20)
        self.outidInput.place(x=130,y=100)
        # 姓名
        # 学号
        self.nameLab = tk.Label(self.root,text='姓名:').place(x=380,y=100)
        self.nameInput = tk.Entry(self.root,width=20)
        self.nameInput.place(x=430,y=100)
        # 起始时间
        self.mustLabel1 = tk.Label(self.root,text='*',font=('Arial', 16),fg = 'red').place(x=45,y=160)

        self.pretimeLab = tk.Label(self.root,text='起始时间:').place(x=55,y=160)
        self.pretimeInput = tk.Entry(self.root, width=20)
        self.pretimeInput.place(x=130,y=160)
        self.pretimeInput.insert(0,self.pretime)
        # 终始时间
        self.mustLabel2 = tk.Label(self.root,text='*',font=('Arial', 16),fg = 'red').place(x=350,y=160)
        
        self.nexttimeLab = tk.Label(self.root,text='终止时间:').place(x=360,y=160)
        self.nexttimeInput = tk.Entry(self.root,width=20)
        self.nexttimeInput.place(x=430,y=160)
        self.nexttimeInput.insert(0,self.nexttime)
        self.consumeBtn = tk.Button(self.root,text='明细查询',command=self.Consumedetail,width=10).place(x=130,y=260)
        self.sumBtn = tk.Button(root,text='汇总查询',command=self.sumDetail,width=10).place(x=300,y=260)
        self.personBtn = tk.Button(root,text='个人查询',command=self.personDetail,width=10).place(x=480,y=260)

        

    # 明细查询
    def Consumedetail(self):
        self.consumedetail = tk.Tk()
        self.consumedetail.title('XX学院明细查询')
        self.consumedetail.geometry("1000x600")
    
        # 加载滚动条
        scrollBar = tk.Scrollbar(self.consumedetail)
        scrollBar.pack(side = tk.RIGHT,fill = tk.Y)
        self.tree = ttk.Treeview(self.consumedetail, columns=('outid', 'name', 'opfare', 'opdt','dscrp'), show="headings", displaycolumns="#all",yscrollcommand = scrollBar.set)
        self.tree.pack()
        self.tree.heading('outid', text="学号", anchor=tk.W)
        self.tree.heading('name', text="姓名", anchor=tk.W)
        self.tree.heading('opfare', text="交易金额", anchor=tk.W)
        self.tree.heading('opdt', text="交易日期", anchor=tk.W)
        self.tree.heading('dscrp', text="交易类型", anchor=tk.W)
        # 设置关联
        scrollBar.config(command = self.tree.yview)
        # 每次打开清空页面
        for item in self.tree.get_children():
            self.consumedetail.tree.delete(item)
        sql = "select a.outid ,a.name ,b.opfare,b.opdt,b.dscrp from base_customers a,rec_cust_acc b where a.customerid = b. customerid and b.opdt >= to_date(:preopdt,'yyyy-MM-dd HH24:mi:ss') and b.opdt <= to_date(:nextopdt,'yyyy-MM-dd HH24:mi:ss') order by b.opdt desc"
        preopdt=self.pretimeInput.get()
        nextopdt=self.nexttimeInput.get()
        data = self.cursor.execute(sql,(preopdt,nextopdt)).fetchall()
        # print(data)
        # data = [['2013090101','张三','100','2023-10-19','PC存款']]
        for itm in data:
            self.tree.insert("",tk.END,values=itm)
            self.tree.pack(padx=10,pady=10, fill=tk.BOTH,expand=1)
        exportbtn = tk.Button(self.consumedetail,text='导出',width=8,command=self.export).pack()

    # 汇总查询
    def sumDetail(self):
        sql = "select sum(opfare),count(acccode),dscrp from rec_cust_acc where opdt >= to_date(:preopdt,'yyyy-MM-dd HH24:mi:ss') and opdt <= to_date(:nextopdt,'yyyy-MM-dd HH24:mi:ss') group by dscrp"
        self.sumtail = tk.Tk()
        self.sumtail.title('XX学院汇总查询')
        self.sumtail.geometry("800x600")
   
        # 加载滚动条
        # exportbtn = Button(sumtail,text='导出',width=8,command=export).pack()
        scrollBar = tk.Scrollbar(self.sumtail)
        scrollBar.pack(side = tk.RIGHT,fill = tk.Y)
        self.tree = ttk.Treeview(self.sumtail, columns=('sum', 'count', 'dscrp'), show="headings", displaycolumns="#all",yscrollcommand = scrollBar.set)
        self.tree.pack()
        self.tree.heading('sum', text="总金额", anchor=tk.W)
        self.tree.heading('count', text="总次数", anchor=tk.W)
        self.tree.heading('dscrp', text="交易类型", anchor=tk.W)
        # 设置关联
        scrollBar.config(command = self.tree.yview)
        # 每次打开清空页面
        for item in self.tree.get_children():
            self.tree.delete(item)
        # sql = "select a.outid ,a.name ,b.opfare,b.opdt,b.dscrp from base_customers a,rec_cust_acc b where a.customerid = b. customerid and b.opdt >= to_date(:preopdt,'yyyy-MM-dd HH24:mi:ss') and b.opdt <= to_date(:nextopdt,'yyyy-MM-dd HH24:mi:ss') order by b.opdt desc"
        preopdt=self.pretimeInput.get()
        nextopdt=self.nexttimeInput.get()
        data = self.cursor.execute(sql,(preopdt,nextopdt)).fetchall()
        # print(data)
        for itm in data:
            self.tree.insert("",tk.END,values=itm)
            self.tree.pack(padx=10,pady=10, fill=tk.BOTH,expand=1)

        exportbtn = tk.Button(self.sumtail,text='导出',width=8,command=self.export).pack()

    # 个人明细
    def personDetail(self):
        if(self.outidInput.get()):
            print('outid not is null')
            sql="select a.outid ,a.name ,b.opfare,b.oddfare,b.opdt,b.dscrp from base_customers a,rec_cust_acc b where a.customerid = b. customerid and b.opdt >= to_date(:preopdt,'yyyy-MM-dd HH24:mi:ss') and b.opdt <= to_date(:nextopdt,'yyyy-MM-dd HH24:mi:ss') and a.outid = :outid order by b.opdt desc"
            outidname = self.outidInput.get()
        elif(self.nameInput.get()):
            sql="select a.outid ,a.name ,b.opfare,b.oddfare,b.opdt,b.dscrp from base_customers a,rec_cust_acc b where a.customerid = b. customerid and b.opdt >= to_date(:preopdt,'yyyy-MM-dd HH24:mi:ss') and b.opdt <= to_date(:nextopdt,'yyyy-MM-dd HH24:mi:ss') and a.name like :name order by b.opdt desc"
            outidname = self.nameInput.get()
        else:
            messagebox.showwarning("提示", "请输入学号或者姓名!")
            return
        self.persontail = tk.Tk()
        self.persontail.title('XX学院个人查询')
        self.persontail.geometry("1200x600")
    
        # 加载滚动条
        # exportbtn = Button(persontail,text='导出',width=8,command=export).pack()
        scrollBar = tk.Scrollbar(self.persontail)
        scrollBar.pack(side = tk.RIGHT,fill = tk.Y)
        self.tree = ttk.Treeview(self.persontail, columns=('outid', 'name', 'opfare','oddfare', 'opdt','dscrp'), show="headings", displaycolumns="#all",yscrollcommand = scrollBar.set)
        self.tree.pack()
        self.tree.heading('outid', text="学号", anchor=tk.W)
        self.tree.heading('name', text="姓名", anchor=tk.W)
        self.tree.heading('opfare', text="交易金额", anchor=tk.W)
        self.tree.heading('oddfare', text="账户余额", anchor=tk.W)
        self.tree.heading('opdt', text="交易日期", anchor=tk.W)
        self.tree.heading('dscrp', text="交易类型", anchor=tk.W)
        # 设置关联
        scrollBar.config(command = self.tree.yview)
        # 每次打开清空页面
        for item in self.tree.get_children():
            self.tree.delete(item)
        # sql = "select a.outid ,a.name ,b.opfare,b.opdt,b.dscrp from base_customers a,rec_cust_acc b where a.customerid = b. customerid and b.opdt >= to_date(:preopdt,'yyyy-MM-dd HH24:mi:ss') and b.opdt <= to_date(:nextopdt,'yyyy-MM-dd HH24:mi:ss') order by b.opdt desc"
        preopdt=self.pretimeInput.get()
        nextopdt=self.nexttimeInput.get()
        # print(outidname)
        data = self.cursor.execute(sql,(preopdt,nextopdt,outidname)).fetchall()
        # print(data)
        for itm in data:
            self.tree.insert("",tk.END,values=itm)
            self.tree.pack(padx=10,pady=10, fill=tk.BOTH,expand=1)
        def export():
        # 导出export        
             # 打开文件夹选择对话框
            folder_path = filedialog.asksaveasfilename(title='请选择一个文件',filetypes=[("CSV", ".csv")]) 
            # 更新标签文本
            print(folder_path)
            list = []
            for row_id in self.tree.get_children():
                list.append(self.tree.item(row_id)['values'])
            with open(f'{folder_path}.csv','w',newline='') as csvfile:
                fieldnames = ['学号', '姓名', '交易金额','账户余额','交易日期','交易类型']
                writer = csv.writer(csvfile)
                writer.writerow(fieldnames)
                writer.writerows(list)

        exportbtn = tk.Button(self.persontail,text='导出',width=8,command=self.export).pack()


    def export(self):
        # 导出export        
         # 打开文件夹选择对话框
        
        # 更新标签文本
        # print(folder_path)
        list = []
        columns = []
        # 获取表格内容id
        for row_id in self.tree.get_children():
            list.append(self.tree.item(row_id)['values'])
        print(len(self.tree.get_children()))   
        # 通过第一行获取列数生成标题
        # print(self.tree.item)
        if len(self.tree.get_children()) != 0:
            print('ok')
            folder_path = filedialog.asksaveasfilename(title='请选择一个文件',filetypes=[("CSV", ".csv")]) 
            for i in range(0,len(self.tree.item('I001')['values'])):
                columns.append(self.tree.heading(column=i)['text'])
        # 导出csv
            with open(f'{folder_path}.csv','w',newline='') as csvfile:
                fieldnames = columns
                writer = csv.writer(csvfile)
                writer.writerow(fieldnames)
                writer.writerows(list)
        else:
            messagebox.showwarning("提示", "没有数据,无法导出")
            return

if __name__ == "__main__":
    root = tk.Tk()
    app =  mainApp(root)
    root.mainloop()
相关推荐
数据智能老司机42 分钟前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机2 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机2 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机2 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i2 小时前
drf初步梳理
python·django
每日AI新事件2 小时前
python的异步函数
python
这里有鱼汤3 小时前
miniQMT下载历史行情数据太慢怎么办?一招提速10倍!
前端·python
databook12 小时前
Manim实现脉冲闪烁特效
后端·python·动效
程序设计实验室13 小时前
2025年了,在 Django 之外,Python Web 框架还能怎么选?
python
倔强青铜三14 小时前
苦练Python第46天:文件写入与上下文管理器
人工智能·python·面试