python GUI實戰項目——tkinter庫的簡單實例

1、項目說明:

  本次經過實現一個小的功能模塊對Python GUI進行實踐學習。項目來源於軟件製造工程的做業。記錄在這裏以複習下思路和總結編碼過程。全部的源代碼和文件放在這裏:python

    連接: https://pan.baidu.com/s/1qXGVRB2 密碼: 4a4rsql

    內置四個文件,分別是ora.sql, dataBaseOpr.py, guiPy.py, test.py數據庫

2、效果預覽:

    

                      主界面c#

 

    

                      新增界面(更新界面一致)設計模式

    功能很簡單,就是作一張表的增刪改查,藉此簡單的熟悉下python,前幾天纔看了看相關的語法。oracle

3、環境說明:

  數據庫採用oracle12c,使用命令行進行操做。Python版本爲3.6.2,命令行+Pycharm社區版2017.1.4。Python庫使用了 ide

    cx_Oracle: 鏈接oracle數據庫函數

         tkinter: 簡單入門的GUI庫oop

  cx_Oracle庫的安裝我直接使用IDE自帶的包管理進行下載安裝的,tkinter是Python3.2之後自帶的標準庫,後面會講。佈局

4、編碼過程實現:

 一、數據庫表實現(ora.sql):

    

    conn username/pass 根據本機的用戶名和密碼修改,後面的數據庫鏈接統一都用我本身密碼,再也不贅述。

    

 

    

     爲了簡化Python代碼和實踐sql能力,寫了兩個簡單的存儲過程,分別是插入和更新,成功建立後只需調用存儲過程和傳遞參數列表便可。代碼詳情在ora.sql中。

    代碼摺疊:

    

 1 conn c##bai/bai123
 2 --建表
 3 create or replace table groupinfo (
 4 no varchar(12) not null,
 5 name varchar(20),
 6 headername varchar(20),
 7 tel varchar(15),
 8 constraint pk_groupinfo primary key(no));
 9 
10 --建立過程,直接傳入參數便可插入
11 create or replace procedure insert_groupinfo
12 (no groupinfo.no%type,
13 name groupinfo.name%type,
14 headername groupinfo.headername%type,
15 tel groupinfo.tel%type
16 )
17 is
18 begin
19 insert into groupinfo values(no,name,headername,tel);
20 commit;
21 end;
22 
23 --建立過程,直接傳入參數便可完成更新,第一個字段爲原紀錄no。必須有。
24 create or replace procedure update_groupinfo
25 (oldno groupinfo.no%type,
26 no groupinfo.no%type,
27 name groupinfo.name%type,
28 headername groupinfo.headername%type,
29 tel groupinfo.tel%type
30 )
31 is
32 n_no groupinfo.no%type;
33 n_name groupinfo.name%type;
34 n_headername groupinfo.headername%type;
35 n_tel groupinfo.tel%type;
36 grow groupinfo%rowtype;
37 ex_oldnoisnull exception;
38 begin
39 select * into grow from groupinfo g where g.no=oldno;
40 if oldno is null or grow.no is null then
41     raise ex_oldnoisnull;
42 end if;
43 if no is null then
44     n_no:= oldno;
45 else
46     n_no:= no;
47 end if;
48 if name is null then
49     n_name:= grow.name;
50 else
51     n_name:= name;
52 end if;
53 if headername is null then
54     n_headername:= grow.headername;
55 else
56     n_headername:= headername;
57 end if;
58 if tel is null then
59     n_tel:= grow.tel;
60 else
61     n_tel:= tel;
62 end if; 
63 --dbms_output.put_line(n_no||n_name||n_headername||n_tel);
64 update groupinfo g set g.no = n_no, g.name = n_name, g.headername = n_headername, g.tel = n_tel where g.no = oldno;
65 commit;
66 exception
67     when ex_oldnoisnull then
68 dbms_output.out_line('選擇的行不存在')
69 end;
ora.sql

 

 二、數據庫操做類(dataBaseOpr.py):

     先貼源碼,摺疊起來:

  1 #!/usr/bin/env python
  2 # encoding: utf-8
  3 """
  4 :author: xiaoxiaobai
  5 
  6 :contact: 865816863@qq.com
  7 
  8 :file: dataBaseOpr.py
  9 
 10 :time: 2017/10/3 12:04
 11 
 12 :@Software: PyCharm Community Edition
 13 
 14 :desc: 鏈接oracle數據庫,並封裝了增刪改查所有操做。
 15 
 16 """
 17 import cx_Oracle
 18 
 19 
 20 class OracleOpr:
 21 
 22     def __init__(self, username='c##bai', passname='bai123', ip='localhost', datebasename='orcl', ipport='1521'):
 23         """
 24         :param username: 鏈接數據庫的用戶名
 25         :param passname: 鏈接數據庫的密碼
 26         :param ip: 數據庫ip
 27         :param datebasename:數據庫名
 28         :param ipport: 數據庫端口
 29         :desc: 初始化函數用於完成數據庫鏈接,能夠經過self.connStatus判斷是否鏈接成功,成功則參數爲0,不成功則返回錯誤詳情
 30         """
 31         try:
 32             self.connStatus = '未鏈接'    # 鏈接狀態
 33             self.queryStatus = 0    # 查詢狀態
 34             self.updateStatus = 0   # 更新狀態
 35             self.deleteStatus = 0   # 刪除狀態
 36             self.insertStatus = 0   # 插入狀態
 37             self.__conn = ''
 38             self.__conStr = username+'/'+passname+'@'+ip+':'+ipport+'/'+datebasename
 39             self.__conn = cx_Oracle.connect(self.__conStr)
 40             self.connStatus = 0
 41         except cx_Oracle.Error as e:
 42             self.connStatus = e
 43 
 44     def closeconnection(self):
 45         try:
 46             if self.__conn:
 47                 self.__conn.close()
 48                 self.connStatus = '鏈接已斷開'
 49         except cx_Oracle.Error as e:
 50             self.connStatus = e
 51 
 52     def query(self, table='groupinfo', queryby=''):
 53         """
 54         :param table: 查詢表名
 55         :param queryby: 查詢條件,支持完整where, order by, group by 字句
 56         :return:返回數據集,列名
 57         """
 58         self.queryStatus = 0
 59         result = ''
 60         cursor = ''
 61         title = ''
 62         try:
 63             sql = 'select * from '+table+' '+queryby
 64             print(sql)
 65             cursor = self.__conn.cursor()
 66             cursor.execute(sql)
 67             result = cursor.fetchall()
 68             title = [i[0] for i in cursor.description]
 69             cursor.close()
 70             cursor = ''
 71         except cx_Oracle.Error as e:
 72             self.queryStatus = e
 73         finally:
 74             if cursor:
 75                 cursor.close()
 76             return result, title
 77 
 78     def insert(self, proc='insert_groupinfo', insertlist=[]):
 79         """
 80         :param proc: 過程名
 81         :param insertlist: 參數集合,主鍵不能爲空,參數必須與列對應,數量一致
 82         :desc: 此方法經過調用過程完成插入,須要在sql上完成存儲過程,能夠經過insertstatus的值判斷是否成功
 83         """
 84         self.insertStatus = 0
 85         cursor = ''
 86         try:
 87             cursor = self.__conn.cursor()
 88             cursor.callproc(proc, insertlist)
 89             cursor.close()
 90             cursor = ''
 91         except cx_Oracle.Error as e:
 92             self.insertStatus = e
 93         finally:
 94             if cursor:
 95                 cursor.close()
 96 
 97     def update(self, proc='update_groupinfo', updatelist=[]):
 98         """
 99         :param proc: 存儲過程名
100         :param updatelist: 更新的集合,第一個爲查詢主鍵,後面的參數爲對應的列,能夠更新主鍵。
101         :desc: 此方法經過調用存儲過程完成更新操做,能夠經過updatestatus的值判斷是否成功
102         """
103         self.updateStatus = 0
104         cursor = ''
105         try:
106             cursor = self.__conn.cursor()
107             cursor.callproc(proc, updatelist)
108             cursor.close()
109             cursor = ''
110         except cx_Oracle.Error as e:
111             self.updateStatus = e
112         finally:
113             if cursor:
114                 cursor.close()
115 
116     def delete(self, deleteby: '刪除條件,where關鍵詞後面的內容,即列名=列值(可多個組合)', table='groupinfo'):
117         """
118         :param deleteby: 刪除的條件,除where關鍵字之外的內容
119         :param table: 要刪除的表名
120         :desc:能夠經過deletestatus判斷是否成功刪除
121         """
122         self.deleteStatus = 0
123         cursor = ''
124         try:
125             sql = 'delete ' + table + ' where ' + deleteby
126             cursor = self.__conn.cursor()
127             cursor.execute(sql)
128             cursor.close()
129             cursor = ''
130         except cx_Oracle.Error as e:
131             self.deleteStatus = e
132         finally:
133             if cursor:
134                 cursor.close()
dataBaseOpr.py

 

    源碼註釋基本很清晰了,對關鍵點進行說明:數據庫鏈接的數據所有用默認參數的形式給出了,可根據實際狀況進行移植。關於調用存儲過程,只須要使用connect(**).cursor.callproc(存儲過程名, 參數列表)便可,方便高效。

 三、GUI界面搭建(tkinter):

     由於界面和邏輯我都寫在guiPy.py中的,沒有使用特別的設計模式。因此這一部分主要講tkinter的用法,下一部分說明具體的實現。

    關於安裝:Python3.2後自帶本庫,若引用沒有,極可能是安裝的時候沒有選。解決方案嘛找到安裝文件修改安裝便可,以下圖:

    

    

    下一步打上勾便可,完成安裝就能引用tkinter了。

 

  使用教程簡單介紹:

  我此次用的時候就是在網上隨便搜了一下教程,發現內容都很淺顯,並且不繫統,固然我也無法系統的講清楚,但官方文檔能夠啊,提醒本身,之後必定先看官方文檔!

  http://effbot.org/tkinterbook/tkinter-index.htm

 四、邏輯實現(guiPy.py):

     先上代碼,基本註釋都有:

  1 #!/usr/bin/env python
  2 # encoding: utf-8
  3 """
  4 :author: xiaoxiaobai
  5 
  6 :contact: 865816863@qq.com
  7 
  8 :file: guiPy.py
  9 
 10 :time: 2017/10/3 19:42
 11 
 12 :@Software: PyCharm Community Edition
 13 
 14 :desc: 該文件完成了主要窗體設計,和數據獲取,呈現等操做。調用時,運行主類MainWindow便可
 15 
 16 """
 17 import tkinter as tk
 18 from tkinter import ttk
 19 from dataBaseOpr import *
 20 import tkinter.messagebox
 21 
 22 
 23 class MainWindow(tk.Tk):
 24     def __init__(self):
 25         super().__init__()
 26 
 27         # 變量定義
 28         self.opr = OracleOpr()
 29         self.list = self.init_data()
 30         self.item_selection = ''
 31         self.data = []
 32 
 33         # 定義區域,把全局分爲上中下三部分
 34         self.frame_top = tk.Frame(width=600, height=90)
 35         self.frame_center = tk.Frame(width=600, height=180)
 36         self.frame_bottom = tk.Frame(width=600, height=90)
 37 
 38         # 定義上部分區域
 39         self.lb_tip = tk.Label(self.frame_top, text="評議小組名稱")
 40         self.string = tk.StringVar()
 41         self.string.set('')
 42         self.ent_find_name = tk.Entry(self.frame_top, textvariable=self.string)
 43         self.btn_query = tk.Button(self.frame_top, text="查詢", command=self.query)
 44         self.lb_tip.grid(row=0, column=0, padx=15, pady=30)
 45         self.ent_find_name.grid(row=0, column=1, padx=45, pady=30)
 46         self.btn_query.grid(row=0, column=2, padx=45, pady=30)
 47 
 48         # 定義下部分區域
 49         self.btn_delete = tk.Button(self.frame_bottom, text="刪除", command=self.delete)
 50         self.btn_update = tk.Button(self.frame_bottom, text="修改", command=self.update)
 51         self.btn_add = tk.Button(self.frame_bottom, text="添加", command=self.add)
 52         self.btn_delete.grid(row=0, column=0, padx=20, pady=30)
 53         self.btn_update.grid(row=0, column=1, padx=120, pady=30)
 54         self.btn_add.grid(row=0, column=2, padx=30, pady=30)
 55 
 56         # 定義中心列表區域
 57         self.tree = ttk.Treeview(self.frame_center, show="headings", height=8, columns=("a", "b", "c", "d"))
 58         self.vbar = ttk.Scrollbar(self.frame_center, orient=tk.VERTICAL, command=self.tree.yview)
 59         # 定義樹形結構與滾動條
 60         self.tree.configure(yscrollcommand=self.vbar.set)
 61         # 表格的標題
 62         self.tree.column("a", width=80, anchor="center")
 63         self.tree.column("b", width=120, anchor="center")
 64         self.tree.column("c", width=120, anchor="center")
 65         self.tree.column("d", width=120, anchor="center")
 66         self.tree.heading("a", text="小組編號")
 67         self.tree.heading("b", text="小組名稱")
 68         self.tree.heading("c", text="負責人")
 69         self.tree.heading("d", text="聯繫方式")
 70         # 調用方法獲取表格內容插入及樹基本屬性設置
 71         self.tree["selectmode"] = "browse"
 72         self.get_tree()
 73         self.tree.grid(row=0, column=0, sticky=tk.NSEW, ipadx=10)
 74         self.vbar.grid(row=0, column=1, sticky=tk.NS)
 75 
 76         # 定義總體區域
 77         self.frame_top.grid(row=0, column=0, padx=60)
 78         self.frame_center.grid(row=1, column=0, padx=60, ipady=1)
 79         self.frame_bottom.grid(row=2, column=0, padx=60)
 80         self.frame_top.grid_propagate(0)
 81         self.frame_center.grid_propagate(0)
 82         self.frame_bottom.grid_propagate(0)
 83 
 84         # 窗體設置
 85         self.center_window(600, 360)
 86         self.title('評議小組管理')
 87         self.resizable(False, False)
 88         self.mainloop()
 89 
 90     # 窗體居中
 91     def center_window(self, width, height):
 92         screenwidth = self.winfo_screenwidth()
 93         screenheight = self.winfo_screenheight()
 94         # 寬高及寬高的初始點座標
 95         size = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
 96         self.geometry(size)
 97 
 98     # 數據初始化獲取
 99     def init_data(self):
100         result, _ = self.opr.query()
101         if self.opr.queryStatus:
102             return 0
103         else:
104             return result
105 
106     # 表格內容插入
107     def get_tree(self):
108         if self.list == 0:
109             tkinter.messagebox.showinfo("錯誤提示", "數據獲取失敗")
110         else:
111             # 刪除原節點
112             for _ in map(self.tree.delete, self.tree.get_children("")):
113                 pass
114             # 更新插入新節點
115             for i in range(len(self.list)):
116                 group = self.list[i]
117                 self.tree.insert("", "end", values=(group[0],
118                                                     group[1],
119                                                     group[2],
120                                                     group[3]), text=group[0])
121             # TODO 此處需解決因主程序自動刷新引發的列表項選中後重置的狀況,我採用的折中方法是:把選中時的數據保存下來,做爲記錄
122 
123             # 綁定列表項單擊事件
124             self.tree.bind("<ButtonRelease-1>", self.tree_item_click)
125             self.tree.after(500, self.get_tree)
126 
127     # 單擊查詢按鈕觸發的事件方法
128     def query(self):
129         query_info = self.ent_find_name.get()
130         self.string.set('')
131         # print(query_info)
132         if query_info is None or query_info == '':
133             tkinter.messagebox.showinfo("警告", "查詢條件不能爲空!")
134             self.get_tree()
135         else:
136             result, _ = self.opr.query(queryby="where name like '%" + query_info + "%'")
137             self.get_tree()
138             if self.opr.queryStatus:
139                 tkinter.messagebox.showinfo("警告", "查詢出錯,請檢查數據庫服務是否正常")
140             elif not result:
141                 tkinter.messagebox.showinfo("查詢結果", "該查詢條件沒有匹配項!")
142             else:
143                 self.list = result
144                 # TODO 此處須要解決彈框後代碼列表刷新沒法執行的問題
145 
146     # 單擊刪除按鈕觸發的事件方法
147     def delete(self):
148         if self.item_selection is None or self.item_selection == '':
149             tkinter.messagebox.showinfo("刪除警告", "未選中待刪除值")
150         else:
151             # TODO: 刪除提示
152             self.opr.delete(deleteby="no = '"+self.item_selection+"'")
153             if self.opr.deleteStatus:
154                 tkinter.messagebox.showinfo("刪除警告", "刪除異常,多是數據庫服務意外關閉了。。。")
155             else:
156                 self.list = self.init_data()
157                 self.get_tree()
158 
159     # 爲解決窗體自動刷新的問題,記錄下單擊項的內容
160     def tree_item_click(self, event):
161         try:
162             selection = self.tree.selection()[0]
163             self.data = self.tree.item(selection, "values")
164             self.item_selection = self.data[0]
165         except IndexError:
166             tkinter.messagebox.showinfo("單擊警告", "單擊結果範圍異常,請從新選擇!")
167 
168     # 單擊更新按鈕觸發的事件方法
169     def update(self):
170         if self.item_selection is None or self.item_selection == '':
171             tkinter.messagebox.showinfo("更新警告", "未選中待更新項")
172         else:
173             data = [self.item_selection]
174             self.data = self.set_info(2)
175             if self.data is None or not self.data:
176                 return
177             # 更改參數
178             data = data + self.data
179             self.opr.update(updatelist=data)
180             if self.opr.insertStatus:
181                 tkinter.messagebox.showinfo("更新小組信息警告", "數據異常庫鏈接異常,多是服務關閉啦~")
182             # 更新界面,刷新數據
183             self.list = self.init_data()
184             self.get_tree()
185 
186     # 單擊新增按鈕觸發的事件方法
187     def add(self):
188         # 接收彈窗的數據
189         self.data = self.set_info(1)
190         if self.data is None or not self.data:
191             return
192         # 更改參數
193         self.opr.insert(insertlist=self.data)
194         if self.opr.insertStatus:
195             tkinter.messagebox.showinfo("新增小組信息警告", "數據異常庫鏈接異常,多是服務關閉啦~")
196         # 更新界面,刷新數據
197         self.list = self.init_data()
198         self.get_tree()
199 
200     # 此方法調用彈窗傳遞參數,並返回彈窗的結果
201     def set_info(self, dia_type):
202         """
203         :param dia_type:表示打開的是新增窗口仍是更新窗口,新增則參數爲1,其他參數爲更新
204         :return: 返回用戶填寫的數據內容,出現異常則爲None
205         """
206         dialog = MyDialog(data=self.data, dia_type=dia_type)
207         # self.withdraw()
208         self.wait_window(dialog)  # 這一句很重要!!!
209         return dialog.group_info
210 
211 
212 # 新增窗口或者更新窗口
213 class MyDialog(tk.Toplevel):
214     def __init__(self, data, dia_type):
215         super().__init__()
216 
217         # 窗口初始化設置,設置大小,置頂等
218         self.center_window(600, 360)
219         self.wm_attributes("-topmost", 1)
220         self.resizable(False, False)
221         self.protocol("WM_DELETE_WINDOW", self.donothing)   # 此語句用於捕獲關閉窗口事件,用一個空方法禁止其窗口關閉。
222 
223         # 根據參數類別進行初始化
224         if dia_type == 1:
225             self.title('新增小組信息')
226         else:
227             self.title('更新小組信息')
228 
229         # 數據變量定義
230         self.no = tk.StringVar()
231         self.name = tk.StringVar()
232         self.pname = tk.StringVar()
233         self.pnum = tk.StringVar()
234         if not data or dia_type == 1:
235             self.no.set('')
236             self.name.set('')
237             self.pname.set('')
238             self.pnum.set('')
239         else:
240             self.no.set(data[0])
241             self.name.set(data[1])
242             self.pname.set(data[2])
243             self.pnum.set(data[3])
244 
245         # 錯誤提示定義
246         self.text_error_no = tk.StringVar()
247         self.text_error_name = tk.StringVar()
248         self.text_error_pname = tk.StringVar()
249         self.text_error_pnum = tk.StringVar()
250         self.error_null = '該項內容不能爲空!'
251         self.error_exsit = '該小組編號已存在!'
252 
253         self.group_info = []
254         # 彈窗界面佈局
255         self.setup_ui()
256 
257     # 窗體佈局設置
258     def setup_ui(self):
259         # 第一行(兩列)
260         row1 = tk.Frame(self)
261         row1.grid(row=0, column=0, padx=160, pady=20)
262         tk.Label(row1, text='小組編號:', width=8).pack(side=tk.LEFT)
263         tk.Entry(row1, textvariable=self.no, width=20).pack(side=tk.LEFT)
264         tk.Label(row1, textvariable=self.text_error_no, width=20, fg='red').pack(side=tk.LEFT)
265         # 第二行
266         row2 = tk.Frame(self)
267         row2.grid(row=1, column=0, padx=160, pady=20)
268         tk.Label(row2, text='小組名稱:', width=8).pack(side=tk.LEFT)
269         tk.Entry(row2, textvariable=self.name, width=20).pack(side=tk.LEFT)
270         tk.Label(row2, textvariable=self.text_error_name, width=20, fg='red').pack(side=tk.LEFT)
271         # 第三行
272         row3 = tk.Frame(self)
273         row3.grid(row=2, column=0, padx=160, pady=20)
274         tk.Label(row3, text='負責人姓名:', width=10).pack(side=tk.LEFT)
275         tk.Entry(row3, textvariable=self.pname, width=18).pack(side=tk.LEFT)
276         tk.Label(row3, textvariable=self.text_error_pname, width=20, fg='red').pack(side=tk.LEFT)
277         # 第四行
278         row4 = tk.Frame(self)
279         row4.grid(row=3, column=0, padx=160, pady=20)
280         tk.Label(row4, text='手機號碼:', width=8).pack(side=tk.LEFT)
281         tk.Entry(row4, textvariable=self.pnum, width=20).pack(side=tk.LEFT)
282         tk.Label(row4, textvariable=self.text_error_pnum, width=20, fg='red').pack(side=tk.LEFT)
283         # 第五行
284         row5 = tk.Frame(self)
285         row5.grid(row=4, column=0, padx=160, pady=20)
286         tk.Button(row5, text="取消", command=self.cancel).grid(row=0, column=0, padx=60)
287         tk.Button(row5, text="肯定", command=self.ok).grid(row=0, column=1, padx=60)
288 
289     def center_window(self, width, height):
290         screenwidth = self.winfo_screenwidth()
291         screenheight = self.winfo_screenheight()
292         size = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
293         self.geometry(size)
294 
295     # 點擊確認按鈕綁定事件方法
296     def ok(self):
297 
298         self.group_info = [self.no.get(), self.name.get(), self.pname.get(), self.pnum.get()]  # 設置數據
299         if self.check_info() == 1:  # 進行數據校驗,失敗則不關閉窗口
300             return
301         self.destroy()  # 銷燬窗口
302 
303     # 點擊取消按鈕綁定事件方法
304     def cancel(self):
305         self.group_info = None  # 空!
306         self.destroy()
307 
308     # 數據校驗和用戶友好性提示,校驗失敗返回1,成功返回0
309     def check_info(self):
310         is_null = 0
311         str_tmp = self.group_info
312         if str_tmp[0] == '':
313             self.text_error_no.set(self.error_null)
314             is_null = 1
315         if str_tmp[1] == '':
316             self.text_error_name.set(self.error_null)
317             is_null = 1
318         if str_tmp[2] == '':
319             self.text_error_pname.set(self.error_null)
320             is_null = 1
321         if str_tmp[3] == '':
322             self.text_error_pnum.set(self.error_null)
323             is_null = 1
324 
325         if is_null == 1:
326             return 1
327         res, _ = OracleOpr().query(queryby="where no = '"+str_tmp[0]+"'")
328         print(res)
329         if res:
330             self.text_error_no.set(self.error_exsit)
331             return 1
332         return 0
333 
334     # 空函數
335     def donothing(self):
336         pass
guiPy.py

  能夠看的出,窗體類繼承自tkinter.TK()能夠直接經過self.x對主窗體添加控件和修改屬性。而後在初始化函數中須要聲明須要的成員變量,完成總體佈局以及控件的事件綁定,以及數據初始化,最後self.mainloop()使窗體完成自動刷新。咱們全部的邏輯處理都是在事件綁定方法中完成的,這樣感受就像是針對用戶的每個操做作出對應的邏輯處理和反應,同時須要考慮可能出現的異常以及全部的可能性,達到用戶友好的設計要求。

  運行此實例,可使用test,py中的測試方法,也能夠把guiPy.py和dataBaseOpr.py兩個類放在同一個文件夾,在本機安裝好上述兩個庫和完成數據庫建立的狀況下,直接在py解釋器下導入guiPy.py文件下全部的包,MainWindow()便可。 

相關文章
相關標籤/搜索