项目3-PetStore宠物商店项目(完整流程从需求设计到实现)

2019年12月21日

一.系统分析与设计

1.项目描述

2.需求分析

image.png

3.原型设计 (原型图)

image.png

4.数据库可设计(实体ER图)

image.png

5.架构设计

image.png
image.png
image.png

6.系统设计

image.png
image.png

写4个Frame,两个GridTable

image.png

二.创建数据库

1.数据库安装略

2.编写数据库DDL脚本

/* 创建数据库 */
CREATE DATABASE IF NOT EXISTS petstore;
use petstore;
/* 用户表 */
CREATE TABLE IF NOT EXISTS accounts (
userid varchar(80) not null, /* 用户Id */
password varchar(25) not null, /* 用户密码 */
email varchar(80) not null, /* 用户Email */
name varchar(80) not null, /* 用户名 */
addr varchar(80) not null, /* 地址 */
city varchar(80) not null, /* 所在城市 */
country varchar(20) not null, /* 国家 */
phone varchar(80) not null, /* 电话号码 */
PRIMARY KEY (userid));
/* 商品表 */
CREATE TABLE IF NOT EXISTS products (
productid varchar(10) not null, /* 商品Id */
category varchar(10) not null, /* 商品类别 */
cname varchar(80) null, /* 商品中文名 */
ename varchar(80) null, /* 商品英文名 */
image varchar(20) null, /* 商品图片 */
descn varchar(255) null, /* 商品描述 */
listprice decimal(10,2) null, /* 商品市场价 */
unitcost decimal(10,2) null, /* 商品单价 */
PRIMARY KEY (productid));
/* 订单表 */
CREATE TABLE IF NOT EXISTS orders (
orderid bigint not null, /* 订单Id */
userid varchar(80) not null, /* 下订单的用户Id */
orderdate datetime not null, /* 下订单时间 */
status int not null default 0, /* 订单付款状态 0待付款 1已付款 */
amount decimal(10,2) not null, /* 订单应付金额 */
PRIMARY KEY (orderid));
/* 订单明细表 */
CREATE TABLE IF NOT EXISTS orderdetails (
orderid bigint not null, /* 订单Id */
productid varchar(10) not null, /* 商品Id */
quantity int not null, /* 商品数量 */
unitcost decimal(10,2) null, /* 商品单价 */
PRIMARY KEY (orderid, productid));

外键创建参考:https://www.jianshu.com/p/452ac68a1865

eg:

alert table orderdetails add foreign key(orderid) reference orders(orderid) ON UPDATE CASCADE ON DELETE CASCADE

3.插入初始数据

use petstore;
/* 用户表数据 */
INSERT INTO accounts VALUES('j2ee','j2ee','yourname@yourdomain.com','关东升', '北京丰台区', '北京', '中国', '18811588888');
INSERT INTO accounts VALUES('ACID','ACID','acid@yourdomain.com','Tony', '901 San Antonio Road', 'Palo Alto', 'USA', '555-555-5555');
/* 商品表数据 */
INSERT INTO products VALUES ('FI-SW-01','鱼类','神仙鱼', 'Angelfish', 'fish1.jpg', '来自澳大利亚的咸水鱼', 650, 400);
INSERT INTO products VALUES ('FI-SW-02','鱼类','虎鲨', 'Tiger Shark','fish4.jpg', '来自澳大利亚的咸水鱼', 850, 600);
INSERT INTO products VALUES ('FI-FW-01','鱼类','锦鲤', 'Koi','fish3.jpg', '来自日本淡水鱼', 150, 120);
INSERT INTO products VALUES ('FI-FW-02','鱼类','金鱼', 'Goldfish','fish2.jpg', '来自中国的淡水鱼', 150, 120);
INSERT INTO products VALUES ('K9-BD-01','狗类','斗牛犬', 'Bulldog','dog2.jpg', '来自英国友好的伴侣犬', 1500, 1200);
INSERT INTO products VALUES ('K9-PO-02','狗类','狮子狗', 'Poodle','dog6.jpg', '来自法国可爱狗狗', 1250, 1000);
INSERT INTO products VALUES ('K9-DL-01','狗类','斑点狗', 'Dalmation','dog5.jpg', '有很多斑点的狗狗', 2150, 2000);
INSERT INTO products VALUES ('K9-RT-01','狗类', '金毛猎犬', 'Golden Retriever','dog1.jpg', '很好的伴侣犬', 3800, 3400);
INSERT INTO products VALUES ('K9-RT-02','狗类', '拉布拉多犬','Labrador Retriever','dog5.jpg', '很好的狩猎犬', 3600, 3020);
INSERT INTO products VALUES ('K9-CW-01','狗类', '吉娃娃', 'Chihuahua','dog4.jpg', '性格温顺的狗狗', 1500, 120);
INSERT INTO products VALUES ('RP-SN-01','爬行类','响尾蛇', 'Rattlesnake','snake1.jpg','可怕且危险的动物', 150, 110);
INSERT INTO products VALUES ('RP-LI-01','爬行类','鬣蜥蜴', 'Iguana','lizard1.jpg', '主要生活在美洲和马达加斯加、斐济和汤加等地。',2600, 2203);
INSERT INTO products VALUES ('RP-LI-02','爬行类','变色树蜥蜴', 'Iguana','lizard2.jpg', '可随环境及光线强弱改变体色', 1000, 903);
INSERT INTO products VALUES ('RP-LI-03','爬行类','中国水龙蜴', 'Iguana','lizard3.jpg', '生活在中国云南的蜥蜴', 1600, 1003);
INSERT INTO products VALUES ('FL-DSH-01','猫类','马恩岛猫', 'Manx','cat3.jpg', '它能有效地减少老鼠的数量很有好处', 2503, 2120);
INSERT INTO products VALUES ('FL-DLH-02','猫类','波斯', 'Persian','cat1.jpg', '非常好的家猫', 3150, 2620);
INSERT INTO products VALUES ('FL-DLH-03','猫类','狸花猫', 'Persian2','cat2.jpg', '原产于中国的猫', 1000, 940);
INSERT INTO products VALUES ('AV-CB-01','鸟类','亚马逊鹦鹉', 'Amazon Parrot','bird4.jpg', '寿命长达75年的大鸟', 3150, 3000);
INSERT INTO products VALUES ('AV-SB-02','鸟类','鹦鹉1', 'Finch1','bird1.jpg', '会唱歌的鸟儿', 150, 110);
INSERT INTO products VALUES ('AV-SB-03','鸟类','鹦鹉2', 'Finch2','bird2.jpg', '会唱歌的鸟儿', 150, 110);
INSERT INTO products VALUES ('AV-SB-04','鸟类','鹦鹉3', 'Finch3','bird3.jpg', '会唱歌的鸟儿', 150, 110);
INSERT INTO products VALUES ('AV-SB-05','鸟类','鹦鹉4', 'Finch6','bird6.jpg', '会唱歌的鸟儿', 150, 110);
INSERT INTO products VALUES ('AV-SB-06','鸟类','鹦鹉5', 'Finch5','bird5.jpg', '会唱歌的鸟儿', 150, 110);

4.执行上面sql语句,可以在mysql模式,直接复制粘贴执行,也可以用如下方式执行

mysql -h localhost -u root -p < /Users/mac/Documents/wangyu/gitRepository/HelloProj/com/pkg1/db/jpetstore-mysql-schema.sql
image.png
image.png

三.初始化项目

1.添加图片资源 右键HelloProj 选择粘贴即可 (只是普通的文件夹,不是包)

image.png

2.添加包 右键主工程选择package类型 ( 不是普通的文件夹init.py)

ui:com.ewell.petstore.ui
dao:com.ewell.petstore.dao
image.png

四.编写数据持久层代码

1.数据库连接配置文件

image.png

2.编写dao基类

# coding=utf-8

"""定义DAO基类"""
import pymysql
import configparser

class BaseDao(object):
    def __init__(self):
        self.config = configparser.ConfigParser()
        self.config.read('config.ini', encoding='utf-8')
        host = self.config['db']['host']
        user = self.config['db']['user']
        # 读取整数port数据
        port = self.config.getint('db', 'port')
        password = self.config['db']['password']
        database = self.config['db']['database']
        charset = self.config['db']['charset']
        self.conn = pymysql.connect(host=host,
                                    user=user,
                                    port=port,
                                    password=password,
                                    database=database,
                                    charset=charset)
    def close(self):
        """关闭数据库连接"""
        self.conn.close()

3.用户管理dao

# coding=utf-8
"""用户管理DAO"""
from com.ewell.petstore.dao.base_dao import BaseDao

class AccountDao(BaseDao):
    def __init__(self):
        super().__init__()
    def findbyid(self, userid):
        account = None
        try:
            # 2. 创建游标对象
            with self.conn.cursor() as cursor:
                # 3. 执行SQL操作
                sql = 'select userid,password,email,name,addr,city,country,phone ' \
                      'from accounts where userid =%s'
                cursor.execute(sql, userid)
                # 4. 提取结果集
                row = cursor.fetchone()
                if row is not None:
                    account = {}
                    account['userid'] = row[0]
                    account['password'] = row[1]
                    account['email'] = row[2]
                    account['name'] = row[3]
                    account['addr'] = row[4]
                    account['city'] = row[5]
                    account['country'] = row[6]
                    account['phone'] = row[7]
                # with代码块结束 5. 关闭游标
        finally:
            # 6. 关闭数据连接
            self.close()
        return account

4.商品管理dao

# coding=utf-8
"""商品管理DAO"""
from com.ewell.petstore.dao.base_dao import BaseDao

class ProductDao(BaseDao):
    def __init__(self):
        super().__init__()
    def findall(self):
        """查询所有商品信息"""
        products = []
        try:
            # 2. 创建游标对象
            with self.conn.cursor() as cursor:
                # 3. 执行SQL操作
                sql = 'select productid,category,cname,ename,image,listprice,unitcost,descn ' \
                      'from products'
                cursor.execute(sql)
                # 4. 提取结果集
                result_set = cursor.fetchall()
                for row in result_set:
                    product = {}
                    product['productid'] = row[0]
                    product['category'] = row[1]
                    product['cname'] = row[2]
                    product['ename'] = row[3]
                    product['image'] = row[4]
                    product['listprice'] = row[5]
                    product['unitcost'] = row[6]
                    product['descn'] = row[7]
                    products.append(product)
                # with代码块结束 5. 关闭游标
        finally:
            # 6. 关闭数据连接
            self.close()
        return products
    def findbycat(self, catname):
        """按照商品类别查询商品"""
        products = []
        try:
            # 2. 创建游标对象
            with self.conn.cursor() as cursor:
                # 3. 执行SQL操作
                sql = 'select productid,category,cname,ename,image,listprice,unitcost,descn ' \
                      'from products where category=%s'
                cursor.execute(sql, catname)
                # 4. 提取结果集
                result_set = cursor.fetchall()
                for row in result_set:
                    product = {}
                    product['productid'] = row[0]
                    product['category'] = row[1]
                    product['cname'] = row[2]
                    product['ename'] = row[3]
                    product['image'] = row[4]
                    product['listprice'] = row[5]
                    product['unitcost'] = row[6]
                    product['descn'] = row[7]
                    products.append(product)
                # with代码块结束 5. 关闭游标
        finally:
            # 6. 关闭数据连接
            self.close()
        return products
    def findbyid(self, productid):
        """按照商品id查询商品"""
        product = None
        try:
            # 2. 创建游标对象
            with self.conn.cursor() as cursor:
                # 3. 执行SQL操作
                sql = 'select productid,category,cname,ename,image,listprice,unitcost,descn' \
                      ' from products where productid=%s'
                cursor.execute(sql, productid)
                # 4. 提取结果集
                row = cursor.fetchone()
                if row is not None:
                    product = {}
                    product['productid'] = row[0]
                    product['category'] = row[1]
                    product['cname'] = row[2]
                    product['ename'] = row[3]
                    product['image'] = row[4]
                    product['listprice'] = row[5]
                    product['unitcost'] = row[6]
                    product['descn'] = row[7]
                # with代码块结束 5. 关闭游标
        finally:
            # 6. 关闭数据连接
            self.close()
        return product

5.订单管理dao

# coding=utf-8
"""订单管理DAO"""
import pymysql
from com.ewell.petstore.dao.base_dao import BaseDao

class OrderDao(BaseDao):
    def __init__(self):
        super().__init__()
    def findall(self):
        """查询所有订单"""
        orders = []
        try:
            # 2. 创建游标对象
            with self.conn.cursor() as cursor:
                # 3. 执行SQL操作
                sql = 'select orderid,userid,orderdate from orders'
                cursor.execute(sql)
                # 4. 提取结果集
                result_set = cursor.fetchall()
                for row in result_set:
                    order = {}
                    order['orderid'] = row[0]
                    order['userid'] = row[1]
                    order['orderdate'] = row[2]
                    orders.append(order)
                # with代码块结束 5. 关闭游标
        finally:
            # 6. 关闭数据连接
            self.close()
        return orders
    # order 是列表 或元组
    def create(self, order):
        """创建订单,插入到数据库"""
        try:
            # 2. 创建游标对象
            with self.conn.cursor() as cursor:
                # 3. 执行SQL操作
                sql = 'insert into orders (orderid,userid,orderdate,status,amount) ' \
                      'values (%s,%s,%s,%s,%s)'
                affectedcount = cursor.execute(sql, order)
                print('成功插入{0}条数据'.format(affectedcount))
                # 4. 提交数据库事物
                self.conn.commit()
                # with代码块结束 5. 关闭游标
        except pymysql.DatabaseError as e:
            # 4. 回滚数据库事物
            self.conn.rollback()
            print(e)
        finally:
            # 6. 关闭数据连接
            self.close()

6.订单明细管理dao

# coding=utf-8
"""订单明细管理DAO"""
import pymysql
from com.ewell.petstore.dao.base_dao import BaseDao

class OrderDetailDao(BaseDao):
    def __init__(self):
        super().__init__()
    # orderdetail是元组或列表
    def create(self, orderdetail):
        """创建订单明细,插入到数据库"""
        try:
            # 2. 创建游标对象
            with self.conn.cursor() as cursor:
                # 3. 执行SQL操作
                sql = 'insert into orderdetails (orderid, productid,quantity,unitcost) ' \
                      'values (%s,%s,%s,%s)'
                affectedcount = cursor.execute(sql, orderdetail)
                print('成功插入{0}条数据'.format(affectedcount))
                # 4. 提交数据库事物
                self.conn.commit()
                # with代码块结束 5. 关闭游标
        except pymysql.DatabaseError as e:
            # 4. 回滚数据库事物
            self.conn.rollback()
            print(e)
        finally:
            # 6. 关闭数据连接
            self.conn.close()

五.编写表示层代码

1.编写启动主模块app_main.py

# coding=utf-8
import wx
from com.ewell.petstore.ui.login_frame import LoginFrame

class App(wx.App):
    def OnInit(self):
        # 创建窗口对象
        frame = LoginFrame()
        frame.Show()
        return True

if __name__ == '__main__':
    app = App()
    app.MainLoop()  # 进入主事件循环

2.编写自定义窗口类 MyFrame(父类)

# coding=utf-8
"""定义Frame窗口基类"""
import sys
import wx

class MyFrame(wx.Frame):
    # 用户登录成功后,保存当前用户信息
    Session = {}
    def __init__(self, title, size):
        super().__init__(parent=None, title=title, size=size,
                         style=wx.DEFAULT_FRAME_STYLE ^ wx.MAXIMIZE_BOX)
        # 设置窗口居中
        self.Centre()
        # 设置Frame窗口内容面板
        self.contentpanel = wx.Panel(parent=self)
        ico = wx.Icon('resources\icon\dog4.ico', wx.BITMAP_TYPE_ICO)
        # 设置窗口图标
        self.SetIcon(ico)
        # 设置窗口的最大和最小尺寸
        self.SetSizeHints(size, size)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
    def OnClose(self, event):
        # 退出系统
        self.Destroy()
        sys.exit(0)

3.编写用户登录界面

image.png
# coding=utf-8
"""用户登录窗口"""
import wx
from com.ewell.petstore.dao.account_dao import AccountDao
from com.ewell.petstore.ui.my_frame import MyFrame
from com.ewell.petstore.ui.product_list_frame import ProductListFrame

class LoginFrame(MyFrame):
    def __init__(self):
        super().__init__(title='用户登录', size=(340, 230))
        # 创建界面控件
        accountid_st = wx.StaticText(self.contentpanel, label='账号:')
        password_st = wx.StaticText(self.contentpanel, label='密码:')
        self.accountid_txt = wx.TextCtrl(self.contentpanel)
        self.password_txt = wx.TextCtrl(self.contentpanel, style=wx.TE_PASSWORD)
        # 创建FlexGrid布局fgs对象
        fgs = wx.FlexGridSizer(2, 2, 20, 20)
        fgs.AddMany([(accountid_st, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE),
                     (self.accountid_txt, 1, wx.CENTER | wx.EXPAND),
                     (password_st, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE),
                     (self.password_txt, 1, wx.CENTER | wx.EXPAND)])
        # 设置FlexGrid布局对象 设置行权重,列权重
        fgs.AddGrowableRow(0, 1)
        fgs.AddGrowableRow(1, 1)
        fgs.AddGrowableCol(0, 1)
        fgs.AddGrowableCol(1, 4)
        # 创建按钮对象
        okb_btn = wx.Button(parent=self.contentpanel, label='确定')
        self.Bind(wx.EVT_BUTTON, self.okb_btn_onclick, okb_btn)
        cancel_btn = wx.Button(parent=self.contentpanel, label='取消')
        self.Bind(wx.EVT_BUTTON, self.cancel_btn_onclick, cancel_btn)
        # 创建水平Box布局hbox对象
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(okb_btn, 1, wx.CENTER | wx.ALL | wx.EXPAND, border=10)
        hbox.Add(cancel_btn, 1, wx.CENTER | wx.ALL | wx.EXPAND, border=10)
        # 创建垂直Box布局,把fgs和hbox添加到垂直Box布局对象上
        vbox = wx.BoxSizer(wx.VERTICAL)
        # -1不指定权重
        vbox.Add(fgs, -1, wx.CENTER | wx.ALL | wx.EXPAND, border=25)
        vbox.Add(hbox, -1, wx.CENTER | wx.BOTTOM, border=20)
        self.contentpanel.SetSizer(vbox)
    def okb_btn_onclick(self, event):
        """确定按钮事件处理"""
        dao = AccountDao()
        account = dao.findbyid(self.accountid_txt.GetValue())
        password = self.password_txt.GetValue()
        if account is not None and account['password'] == password:
            print('登录成功。')
            next_frame = ProductListFrame()
            next_frame.Show()
            self.Hide()
            # 登录成功保存用户Session
            MyFrame.Session = account
        else:
            print('登录失败。')
            dlg = wx.MessageDialog(self, '您输入的账号或密码有误,请重新输入。',
                                   '登录失败',
                                   wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
    def cancel_btn_onclick(self, event):
        """取消按钮事件处理"""
        # 退出系统
        self.OnClose(self, event)

4.编写商品列表界面

image.png
# coding=utf-8
"""商品列表窗口"""
import wx
import wx.grid
from com.ewell.petstore.dao.product_dao import ProductDao
from com.ewell.petstore.ui.cart_frame import CartFrame
from com.ewell.petstore.ui.my_frame import MyFrame
from com.ewell.petstore.ui.product_list_gridtable import ProductListGridTable
# 商品类别
CATEGORYS = ['鱼类', '狗类', '爬行类', '猫类', '鸟类']

class ProductListFrame(MyFrame):
    def __init__(self):
        super().__init__(title='商品列表窗口', size=(1000, 700))
        #  购物车,键是选择的商品Id,值是商品的数量
        self.cart = {}
        # 选中商品
        self.selecteddata = {}
        # 创建DAO对象
        dao = ProductDao()
        # 查询所有数据
        self.data = dao.findall()
        # 创建分隔窗口
        splitter = wx.SplitterWindow(self.contentpanel, style=wx.SP_3DBORDER)
        # 创建分隔窗口中的左侧面板
        self.leftpanel = self.createleftpanel(splitter)
        # 创建分隔窗口中的右侧面板
        self.rightpanel = self.createrightpanel(splitter)
        # 设置分隔窗口左右布局
        splitter.SplitVertically(self.leftpanel,
                                 self.rightpanel,
                                 630)
        # 设置最小窗口尺寸
        splitter.SetMinimumPaneSize(630)
        # 设置整个窗口布局是垂直Box布局
        vbox = wx.BoxSizer(wx.VERTICAL)
        self.contentpanel.SetSizer(vbox)
        # 添加顶部对象(topbox)到vbox
        vbox.Add(self.createtopbox(), 1, flag=wx.EXPAND | wx.ALL, border=20)
        # 添加底部对象(splitter)到vbox
        vbox.Add(splitter, 1, flag=wx.EXPAND | wx.ALL, border=10)
        # 在当前创建(Frame对象)创建并添加默认状态栏
        self.CreateStatusBar()
        self.SetStatusText('准备就绪')
    def createtopbox(self):
        """创建顶部布局管理器topbox"""
        # 创建静态文本
        pc_st = wx.StaticText(parent=self.contentpanel, label='选择商品类别:', style=wx.ALIGN_RIGHT)
        # 创建按钮对象
        search_btn = wx.Button(parent=self.contentpanel, label='查询')
        reset_btn = wx.Button(parent=self.contentpanel, label='重置')
        choice = wx.Choice(self.contentpanel, choices=CATEGORYS, name='choice')
        # 绑定事件处理
        self.Bind(wx.EVT_BUTTON, self.search_btn_onclick, search_btn)
        self.Bind(wx.EVT_BUTTON, self.reset_btn_onclick, reset_btn)
        box = wx.BoxSizer(wx.HORIZONTAL)
        box.AddSpacer(200)
        box.Add(pc_st, 1, flag=wx.FIXED_MINSIZE | wx.ALL, border=10)
        box.Add(choice, 1, flag=wx.EXPAND | wx.ALL, border=5)
        box.Add(search_btn, 1, flag=wx.EXPAND | wx.ALL, border=5)
        box.Add(reset_btn, 1, flag=wx.EXPAND | wx.ALL, border=5)
        box.AddSpacer(260)
        return box
    def createleftpanel(self, parent):
        """创建分隔窗口中的左侧面板"""
        panel = wx.Panel(parent)
        # 创建网格对象
        grid = wx.grid.Grid(panel, name='grid')
        # 绑定网格事件处理
        self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.selectrow_handler)
        self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.selectrow_handler)
        # 初始化网格
        self.initgrid()
        # 创建水平Box管理管理
        box = wx.BoxSizer()
        # 设置水平Box管理管理网格grid
        box.Add(grid, 1, flag=wx.ALL, border=5)
        panel.SetSizer(box)
        return panel
    def initgrid(self):
        """初始化网格对象"""
        # 通过网格名字获得网格对象
        grid = self.FindWindowByName('grid')
        # 创建网格中所需的表格
        table = ProductListGridTable(self.data)
        # 设置网格的表格属性
        grid.SetTable(table, True)
        rowsizeinfo = wx.grid.GridSizesInfo(40, [])
        # 设置网格所有行高
        grid.SetRowSizes(rowsizeinfo)
        colsizeinfo = wx.grid.GridSizesInfo(0, [100, 80, 130, 200])
        # 设置网格所有列宽
        grid.SetColSizes(colsizeinfo)
        # 设置单元格默认字体
        grid.SetDefaultCellFont(wx.Font(11, wx.FONTFAMILY_DEFAULT,
                                        wx.FONTSTYLE_NORMAL,
                                        wx.FONTWEIGHT_NORMAL, faceName='微软雅黑'))
        # 设置行和列标题的默认字体
        grid.SetLabelFont(wx.Font(9, wx.FONTFAMILY_DEFAULT,
                                  wx.FONTSTYLE_NORMAL,
                                  wx.FONTWEIGHT_NORMAL, faceName='微软雅黑'))
        # 设置网格选择模式为行选择
        grid.SetSelectionMode(grid.wxGridSelectRows)
        # 设置行不能通过拖动改变高度
        grid.DisableDragRowSize()
        # 设置列不能通过拖动改变宽度
        grid.DisableDragColSize()
    def createrightpanel(self, parent):
        """创建分隔窗口中的右侧面板"""
        panel = wx.Panel(parent, style=wx.TAB_TRAVERSAL | wx.BORDER_DOUBLE)
        panel.SetBackgroundColour(wx.WHITE)
        # 显示第一张图片
        imagepath = 'resources/images/' + self.data[0]['image']
        image = wx.Bitmap(imagepath, wx.BITMAP_TYPE_ANY)
        image_sbitmap = wx.StaticBitmap(panel, bitmap=image, name='image_sbitmap')
        # 商品市场价格
        slistprice = "商品市场价:¥{0:.2f}".format(self.data[0]['listprice'])
        listprice_st = wx.StaticText(panel, label=slistprice, name='listprice')
        # 市场价格
        sunitcost = "商品单价:¥{0:.2f}".format(self.data[0]['unitcost'])
        unitcost_st = wx.StaticText(panel, label=sunitcost, name='unitcost')
        # 商品描述
        descn = "商品描述:{0}".format(self.data[0]['descn'])
        descn_st = wx.StaticText(panel, label=descn, name='descn')
        # 创建按钮对象
        addcart_btn = wx.Button(panel, label='添加到购物车')
        seecart_btn = wx.Button(panel, label='查看购物车')
        # 绑定事件处理
        self.Bind(wx.EVT_BUTTON, self.addcart_btn_onclick, addcart_btn)
        self.Bind(wx.EVT_BUTTON, self.seecart_btn_onclick, seecart_btn)
        # 创建垂直Box布局管理器
        box = wx.BoxSizer(wx.VERTICAL)
        box.AddSpacer(50)
        box.Add(image_sbitmap, 1, flag=wx.CENTER | wx.ALL, border=30)
        box.AddSpacer(50)
        box.Add(listprice_st, 1, flag=wx.EXPAND | wx.ALL, border=10)
        box.Add(unitcost_st, 1, flag=wx.EXPAND | wx.ALL, border=10)
        box.Add(descn_st, 1, flag=wx.EXPAND | wx.ALL, border=10)
        box.AddSpacer(20)
        box.Add(addcart_btn, 1, flag=wx.EXPAND | wx.ALL, border=10)
        box.Add(seecart_btn, 1, flag=wx.EXPAND | wx.ALL, border=10)
        panel.SetSizer(box)
        return panel
    def search_btn_onclick(self, event):
        """查询按钮事件处理"""
        # 通过名字查询choice控件
        choice = self.FindWindowByName('choice')
        # 获得选中类别索引
        selectcatidx = choice.GetSelection()
        if selectcatidx >= 0:
            # 获得选中的商品类别
            catname = CATEGORYS[selectcatidx]
            # 根据类别查询商品
            dao = ProductDao()
            self.data = dao.findbycat(catname)
            # 初始化网格
            self.initgrid()
    def reset_btn_onclick(self, event):
        """重置按钮事件处理"""
        # 查询所有商品
        dao = ProductDao()
        self.data = dao.findall()
        # 初始化网格
        self.initgrid()
    def addcart_btn_onclick(self, event):
        """添加到购物车事件处理"""
        if len(self.selecteddata) == 0:
            self.SetStatusText('请先选择商品')
            return
        # 获得选择的商品id
        productid = self.selecteddata['productid']
        if productid in self.cart.keys():  # 判断购物车中已经有该商品
            # 获得商品数量
            quantity = self.cart[productid]
            self.cart[productid] = (quantity + 1)
        else:  # 购物车中还没有该商品
            self.cart[productid] = 1
        # 显示在状态栏
        self.SetStatusText('商品{0}添加到购物车'.format(productid))
        print(self.cart)
    def seecart_btn_onclick(self, event):
        """查看添加到购物车事件处理"""
        next_frame = CartFrame(self.cart, self)
        next_frame.Show()
        self.Hide()
    def selectrow_handler(self, event):
        """选择网格行事件处理"""
        srowidx = event.GetRow()
        if srowidx >= 0:
            print(self.data[srowidx])
            self.selecteddata = self.data[srowidx]
            self.SetStatusText('选择第{0}行数据'.format(srowidx + 1))
            # 显示选择的图片
            imagepath = 'resources/images/' + self.selecteddata['image']
            image = wx.Bitmap(imagepath, wx.BITMAP_TYPE_ANY)
            # 通过名字查询子窗口
            image_sbitmap = self.FindWindowByName('image_sbitmap')
            image_sbitmap.SetBitmap(image)
            # 商品市场价格
            slistprice = "商品市场价:¥{0:.2f}".format(self.selecteddata['listprice'])
            listprice_st = self.FindWindowByName('listprice')
            listprice_st.SetLabelText(slistprice)
            # 市场价格
            sunitcost = "商品单价:¥{0:.2f}".format(self.selecteddata['unitcost'])
            unitcost_st = self.FindWindowByName('unitcost')
            unitcost_st.SetLabelText(sunitcost)
            # 商品描述
            descn = "商品描述:{0}".format(self.selecteddata['descn'])
            descn_st = self.FindWindowByName('descn')
            descn_st.SetLabelText(descn)
            self.rightpanel.Layout()
        event.Skip()

# coding=utf-8
"""自定义GridTableBase类, 用于商品网格"""
import wx.grid
# 商品网格列名
COLUMN_NAMES = ['商品编号', '商品类别', '商品中文名', '商品英文名']

class ProductListGridTable(wx.grid.GridTableBase):
    def __init__(self, data):
        super().__init__()
        self.colLabels = COLUMN_NAMES
        self.data = data
    def GetNumberRows(self):
        return len(self.data)
    def GetNumberCols(self):
        return len(COLUMN_NAMES)
    def GetValue(self, rowidx, colidx):
        product = self.data[rowidx]
        if colidx == 0:
            return product['productid']
        elif colidx == 1:
            return product['category']
        elif colidx == 2:
            return product['cname']
        else:
            return product['ename']
    def GetColLabelValue(self, colidx):
        return self.colLabels[colidx]

5.编写购物车界面

image.png

image.png

其余代码参考:
https://github.com/wangyu204/HelloProj.git

六.发布可执行文件

1.TODO任务 查看

image.png

2.发布为可执行文件

2.1安装pyinstaller (大概花了40分钟,一致报错)

官网:http://www.pyinstaller.org/

pip install pyinstaller

image.png

2.2 参考

image.png

2.3 实现步骤

第一步切换到根目录,创建规范文件

cd /Users/mac/Documents/wangyu/gitRepository/HelloProj
wangyuMBP:HelloProj mac$ pyi-makespec [app_main.py](http://app_main.py/)
image.png

第二步:修改规范文件 (主要是添加资源文件和配置文件)

image.png

第三步执行(也是在根目录)

cd /Users/mac/Documents/wangyu/gitRepository/HelloProj
pyinstaller app_main.spec

报如下错误: TypeError: an integer is required (got type bytes)

image.png

网上反馈是兼容问题,

参考:https://github.com/pyinstaller/pyinstaller/issues/4265

解决:先卸载3.5版本,在安装开发版本 (晚上试了很久都下载不了,直接下载安装也不行)

pip uninstall pyinstaller

pip install https://github.com/pyinstaller/pyinstaller/archive/develop.tar.gz

image.png

pip install https://github.com/pyinstaller/pyinstaller/tarball/develop

https://pyinstaller.readthedocs.io/en/stable/installation.html#installing-in-mac-os-x

第二天早上公司一运行就很快安装好了

image.png

再次运行成功了

pyinstaller app_main.spec
image.png

点击 dist目录下的 app_main/app_main文件就可以正常运行了(下面错误待研究)

image.png

如果您发现本文对你有所帮助,如果您认为其他人也可能受益,请把它分享出去。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,735评论 0 10
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    小迈克阅读 2,972评论 1 3
  • 第八章 程序设计方法论 人生苦短,请用Python。Life is short. You need Python....
    jplee阅读 1,763评论 0 1
  • 剧中杨贵妃再马嵬驿自尽前的一段,大概是看到了人性最黑暗的一面,有临死前的一丝希望,但依旧没料到人心更加利益的一面;...
    霖临说阅读 524评论 0 49
  • 1-跟彩排,熟悉舞蹈音乐 2-中铁招生发礼物 3-整理检查材料
    48f0e80ec5c6阅读 200评论 0 0