wxPython example code (shopping cart)

wxPython is a popular cross-platform GUI toolkit. Use this to make a window interface for the program.
Official website: www.wxpython.org
I didn't study hard, so I put together an example of a small project. Paste the complete code below, you can refer to this change when you make the interface next time.

document structure

Mainly a few py files. In the resources folder, the original resources are placed, that is, two pictures.

Cart
    │  app_main.py
    ├─conf
    │      settings.py
    │      __init__.py
    ├─resources
    │      bats.ico
    │      dragon.jpg
    └─ui
            list_frame.py
            list_grid_table.py
            login_frame.py
            my_frame.py
            __init__.py

settings file

There is no setting here, but the data is put here, mainly to get the interface, not even the database:

# 登录的用户名和密码
ACCOUNTS = {
        'admin': {'pwd': 'admin'},
        'root': {'pwd': '123456'},
        'user': {'pwd': 'user123'},
    }

# 商品列名
COLUMN_NAMES = ["商品编号", "商品类别", "商品中文名", "商品英文名"]

# 商品类别
CATEGORY = ["食品", "酒类", "男装", "女装", "童装"]

# 商品信息,商品信息有点少,最好多搞几十条
PRODUCTS = [
    {'id': "001", 'category': "食品", 'name_cn': "薯片", 'name_en': "ShuPian"},
    {'id': "002", 'category': "酒类", 'name_cn': "葡萄酒", 'name_en': "PuTaoJiu"},
    {'id': "003", 'category': "男装", 'name_cn': "西装", 'name_en': "XiZhuang"},
    {'id': "004", 'category': "女装", 'name_cn': "短裙", 'name_en': "DuanQun"},
    {'id': "005", 'category': "童装", 'name_cn': "连衣裙", 'name_en': "LianYiQun"},
]

startup file

Start our project by starting the file, here is mainly to call to create a login window, and then enter the main event loop

import wx

from ui.login_frame import LoginFrame
from ui.list_frame import ListFrame

class App(wx.App):
    """启动模块"""

    def OnInit(self):
        """创建窗口对象"""
        frame = LoginFrame()
        # frame = ListFrame()  # 单独调试商品界面的时候,省的每次都要登录一下
        frame.Show()
        return True

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

window base class

First define a base class for all windows, and define the properties common to all windows in this base class. After that, each window is based on this class:

"""定义Frame窗口基类"""
import sys
import wx

class MyFrame(wx.Frame):
    session = {}  # 模拟Web的session,保留会话的数据

    def __init__(self, title, size):
        super().__init__(parent=None, title=title, size=size,
                         style=wx.DEFAULT_FRAME_STYLE ^ wx.MAXIMIZE_BOX)
        # style是定义窗口风格,具体看官网。https://docs.wxpython.org/wx.Frame.html#wx-frame
        # 上面的DEFAULT就是包含了下面所有的风格的:
        # wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER |
        # wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN
        # 上面的例子是去掉了其中的一个。官网的例子是这样的:
        # style = wx.DEFAULT_FRAME_STYLE & ~(wx.RESIZE_BORDER | wx.MAXIMIZE_BOX)  去掉了2个来固定窗口大小
        # 设置窗口居中
        self.Center()
        # 设置Frame窗口内容面板
        self.contentpanel = wx.Panel(parent=self)
        # 图标文件
        ico = wx.Icon("resources/bats.ico", wx.BITMAP_TYPE_ICO)
        # 设置图标
        self.SetIcon(ico)
        # 设定窗口大小,这里设置了相同的最大和最小值,也就是固定了窗口大小。
        # 因为上面的窗口风格了保留了wx.RESIZE_BORDER,所以这里用另外一个放来来保证大小不可调整
        # 这样做有一点不好,就是鼠标放在窗口边缘,会变成调整窗口大小的样子,但是拉不动窗口
        self.SetSizeHints(size, size)
        # 绑定关闭按钮的点击事件
        self.Bind(wx.EVT_CLOSE, self.on_close)

    def on_close(self, event):
        # 退出系统
        self.Destroy()
        sys.exit()

The size of the fixed window set here cannot be adjusted, but it should be better to use the official website.
Also defines the icon of the window, and the method called to close the window.
self.contentpanel = wx.Panel(parent=self)This property will always be used later

login window

Now let's really start to make the window interface, first make a simple login interface:

"""登录窗口"""
import wx

from ui.my_frame import MyFrame
from ui.list_frame import ListFrame
from conf import settings

class LoginFrame(MyFrame):
    accounts = settings.ACCOUNTS

    def __init__(self):
        super().__init__(title="用户登录", size=(340, 230))

        # 创建界面中的控件
        username_st = wx.StaticText(self.contentpanel, label="用户名:")  # 输入框前面的提示标签
        password_st = wx.StaticText(self.contentpanel, label="密码:")
        self.username_txt = wx.TextCtrl(self.contentpanel)  # 输入框
        self.password_txt = wx.TextCtrl(self.contentpanel, style=wx.TE_PASSWORD)

        # 创建FlexGrid布局对象
        fgs = wx.FlexGridSizer(2, 2, 20, 20)  # 2行2列,行间距20,列间距20
        fgs.AddMany([
            # 下面套用了3个分隔,垂直居中,水平靠右,固定的最小尺寸
            (username_st, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE),
            # 位置居中,尺寸是膨胀
            (self.username_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)  # 第一个0是指第一行,权重1
        fgs.AddGrowableRow(1, 1)  # 第一个1是指第二行,权重也是1
        # 上面一共就2行,用户名和密码,就是2行的空间是一样的
        fgs.AddGrowableCol(0, 1)  # 第一列,权重1,就是标签的内容
        fgs.AddGrowableCol(1, 4)  # 第二列,权重4,就是输入框,并且输入框是膨胀的应该会撑满
        # 上面2列分成5分,第一列占1/5,第二列占4/5

        # 创建按钮对象
        ok_btn = wx.Button(parent=self.contentpanel, label="确定")
        cancel_btn = wx.Button(parent=self.contentpanel, label="取消")
        # 绑定按钮事件:事件类型,绑定的事件,绑定的按钮
        self.Bind(wx.EVT_BUTTON, self.ok_btn_onclick, ok_btn)
        self.Bind(wx.EVT_BUTTON, self.cancel_btn_onclick, cancel_btn)

        # 创建水平Box布局对象,放上面的2个按钮
        box_btn = wx.BoxSizer(wx.HORIZONTAL)
        # 添加按钮控件:居中,四周都有边框,膨胀。border是设置边框的大小,实际效果没有框,但是占用空间
        box_btn.Add(ok_btn, 1, wx.CENTER | wx.ALL | wx.EXPAND, border=10)
        box_btn.Add(cancel_btn, 1, wx.CENTER | wx.ALL | wx.EXPAND, border=10)

        # 创建垂直Box,把上面的fgs对象和box_btn对象都放进来
        box_outer = wx.BoxSizer(wx.VERTICAL)
        box_outer.Add(fgs, -1, wx.CENTER | wx.ALL | wx.EXPAND, border=25)  # 权重是-1,就是不指定了
        # (wx.ALL ^ wx.TOP)这里只加3面的边框,上面就不加了
        box_outer.Add(box_btn, -1, wx.CENTER | (wx.ALL ^ wx.TOP) | wx.EXPAND, border=20)

        # 上面全部设置完成了,下面是设置Frame窗口内容面板
        self.contentpanel.SetSizer(box_outer)  # self.contentpanel 是在父类里定义的

    def ok_btn_onclick(self, event):
        username = self.username_txt.GetValue()  # 取出输入框的值
        password = self.password_txt.GetValue()
        if username in self.accounts:
            if self.accounts[username].get('pwd') == password:
                self.session['username'] = username
                print("登录成功")
                # 接下来要进入下一个Frame
                frame = ListFrame()
                frame.Show()
                self.Hide()  # 隐藏登录窗口
                return
            else:
                msg = "用户名或密码错误"
        else:
            msg = "用户名不存在"
        print(msg)
        dialog = wx.MessageDialog(self, msg, "登录失败")  # 创建对话框
        dialog.ShowModal()  # 显示对话框
        dialog.Destroy()  # 销毁对话框

    def cancel_btn_onclick(self, event):
        self.on_close(event)

Product list window

This window is more complicated. There is a drop-down list, which can be filtered. There are tables and pictures below, and if you click on the cells of the table, the content of the window will change accordingly:

"""商品列表窗口"""
import wx, wx.grid

from ui.my_frame import MyFrame
from ui.list_grid_table import ListGridTable
from conf import settings

class ListFrame(MyFrame):

    def __init__(self):
        super().__init__(title="商品列表", size=(1000, 700))

        # 购物车
        self.cart = {}
        # 商品列表
        self.data = settings.PRODUCTS

        # 创建分隔窗口
        splitter = wx.SplitterWindow(self.contentpanel, style=wx.SP_3DBORDER)
        # 分隔窗口的左侧面板
        self.left_panel = self.create_left_panel(splitter)  # 先准备一个函数,到函数里再去实现
        # 分隔窗口的右侧面板
        self.right_panel = self.create_right_panel(splitter)
        # 设置分隔窗口的布局,调用SplitVertically方法就是左右布局
        splitter.SplitVertically(self.left_panel, self.right_panel, 630)

        # 设置整个窗口的布局,是一个垂直的Box布局
        box_outer = wx.BoxSizer(wx.VERTICAL)
        # 下面直接把这个Box放到内容面板里了,也可以最后放。
        # 不过这个窗口的内容只有一个垂直Box,所以无所谓了
        # 还有其他的控件没有写,不过写完都是往box_splitter里添加
        self.contentpanel.SetSizer(box_outer)

        # 添加顶部对象
        box_outer.Add(self.create_top_box(), 1, flag=wx.EXPAND | wx.ALL, border=20)
        # 添加分隔窗口对象
        box_outer.Add(splitter, 1, flag=wx.EXPAND | wx.ALL, border=10)

        # 创建底部的状态栏
        self.CreateStatusBar()
        self.SetStatusText("准备就绪,欢迎您:%s" % self.session['username'])

    def create_top_box(self):
        """创建顶部的布局管理器"""
        # 创建静态文本
        label_st = wx.StaticText(parent=self.contentpanel, label="选择商品类别:", style=wx.ALIGN_RIGHT)
        # 创建下拉列表对象,这里取了一个name,之后可以通过name找到这个控件获取里面的内容
        # 查找的方法用FindWindowByName,另外还可以ById和ByLabel
        choice = wx.Choice(self.contentpanel, choices=settings.CATEGORY, name="choice")
        # 创建按钮对象
        search_btn = wx.Button(parent=self.contentpanel, label="查询")
        reset_btn = wx.Button(parent=self.contentpanel, label="重置")
        # 绑定事件
        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(label_st, 1, flag=wx.FIXED_MINSIZE | wx.ALL, border=10)
        box.Add(choice, 1, flag=wx.FIXED_MINSIZE | wx.ALL, border=5)
        box.Add(search_btn, 1, flag=wx.FIXED_MINSIZE | wx.ALL, border=5)
        box.Add(reset_btn, 1, flag=wx.FIXED_MINSIZE | wx.ALL, border=5)
        box.AddSpacer(300)  # 添加空白

        return box

    def create_left_panel(self, parent):
        """创建分隔窗口的左侧面板"""
        panel = wx.Panel(parent)

        # 创建网格对象
        grid = wx.grid.Grid(panel, name='grid')
        # 绑定事件
        self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.select_row_handler)
        self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.select_row_handler)

        # 初始化网格
        self.init_grid()  # 还是到另一个函数里去实现

        # 创建水平Box的布局管理器
        box = wx.BoxSizer()
        # 设置Box的网格grid
        box.Add(grid, 1, flag=wx.ALL, border=5)
        panel.SetSizer(box)

        return panel

    def init_grid(self):
        """初始化网格对象"""
        # 通过网格名字获取到对象
        grid = self.FindWindowByName('grid')

        # 创建网格中所需要的表格,这里的表格是一个类
        table = ListGridTable(settings.COLUMN_NAMES, self.data)
        # 设置网格的表格属性
        grid.SetTable(table, True)

        # 获取网格行的信息对象,40是行高,每一行都是40,后面的列表是单独指定每一行的,这里是空列表
        row_size_info = wx.grid.GridSizesInfo(40, [])
        # 设置网格的行高
        grid.SetRowSizes(row_size_info)
        # 指定列宽,前面是0,后面分别指定每一列的列宽
        col_size_info = wx.grid.GridSizesInfo(0, [100, 80, 130, 200])
        grid.SetColSizes(col_size_info)
        # 设置单元格默认字体
        grid.SetDefaultCellFont(wx.Font(11, wx.FONTFAMILY_DEFAULT,
                                        wx.FONTSTYLE_NORMAL,
                                        wx.FONTWEIGHT_NORMAL,
                                        faceName="微软雅黑"))
        # 设置表格标题的默认字体
        grid.SetLabelFont(wx.Font(11, wx.FONTFAMILY_DEFAULT,
                                  wx.FONTSTYLE_NORMAL,
                                  wx.FONTWEIGHT_NORMAL,
                                  faceName="微软雅黑"))

        # 设置网格选择模式为行选择模式
        grid.SetSelectionMode(grid.wxGridSelectRows)
        # 设置网格不能通过拖动改标高度和宽度
        grid.DisableDragRowSize()
        grid.DisableDragColSize()

    def create_right_panel(self, parent):
        """创建分隔窗口的右侧面板"""
        panel = wx.Panel(parent, style=wx.TAB_TRAVERSAL | wx.BORDER_DOUBLE)
        panel.SetBackgroundColour(wx.WHITE)  # 设置背景色,默认不是白色

        # 显示图片
        img_path = "resources/dragon.jpg"
        img = wx.Bitmap(img_path, wx.BITMAP_TYPE_ANY)  # 第二个参数设置图片格式可以是任意的
        img_bitmap = wx.StaticBitmap(panel, bitmap=img, name='img_bitmap')

        # 商品类别
        category = "商品类别:【还未选中商品】"
        category_st = wx.StaticText(panel, label=category, name='category')  # 创建控件同时指定label
        # 商品名称
        name = "商品名称:【还未选中商品】"
        name_st = wx.StaticText(panel, label=name, name='name')
        # 商品价格
        price = "商品价格:¥{0:.2f}".format(128)
        price_st = wx.StaticText(panel, label=price, name='price')
        # 商品描述
        description = "商品描述:%s" % "总之很好就是了"
        description_st = wx.StaticText(panel, label=description, name='description')

        # 创建按钮对象
        add_btn = wx.Button(panel, label="添加到购物车")
        see_btn = wx.Button(panel, label="查看购物车")
        self.Bind(wx.EVT_BUTTON, self.add_btn_onclick, add_btn)
        self.Bind(wx.EVT_BUTTON, self.see_btn_onclick, see_btn)

        # 布局,垂直的Box布局管理器
        box = wx.BoxSizer(wx.VERTICAL)
        box.Add(img_bitmap, 1, flag=wx.CENTER | wx.ALL, border=30)
        box.Add(category_st, 1, flag=wx.EXPAND | wx.ALL, border=10)
        box.Add(name_st, 1, flag=wx.EXPAND | wx.ALL, border=10)
        box.Add(price_st, 1, flag=wx.EXPAND | wx.ALL, border=10)
        box.Add(description_st, 1, flag=wx.EXPAND | wx.ALL, border=10)
        box.Add(add_btn, 1, flag=wx.EXPAND | wx.ALL, border=10)
        box.Add(see_btn, 1, flag=wx.EXPAND | wx.ALL, border=10)

        panel.SetSizer(box)

        return panel

    def search_btn_onclick(self, event):
        """查询按钮可以按商品类别进行筛选"""
        choice = self.FindWindowByName('choice')
        selected = choice.GetSelection()
        if selected >= 0:
            category = settings.CATEGORY[selected]
            # 根据商品类别查询商品
            products = []
            for item in settings.PRODUCTS:
                if item.get('category') == category:
                    products.append(item)
            # 上面已经生成了新了商品列表,将商品列表更新后,重新初始化网格
            self.data = products
            self.init_grid()

    def reset_btn_onclick(self, event):
        """单击重置按钮
        查询所有的商品/获取初始的商品列表
        然后初始化网格
        """
        self.data = settings.PRODUCTS
        self.init_grid()

    def select_row_handler(self, event):
        """选择的网格的行事件处理
        这里会刷新右侧面板的商品类别和名称
        """
        row_selected = event.GetRow()
        if row_selected >= 0:
            selected_data = self.data[row_selected]
            # 商品类别
            category = "商品类别:%s" % selected_data.get('category')
            category_st = self.FindWindowByName('category')
            category_st.SetLabelText(category)  # 先创建好控件,再修改或者设置label
            # 商品名称
            name = "商品名称:%s" % selected_data.get('name_cn')
            name_st = self.FindWindowByName('name')
            name_st.SetLabelText(name)

            # 刷新布局,如果更换了图片应该是要刷新的,没换图片不用刷新
            # self.right_panel.Layout()

        event.Skip()  # 事件跳过,貌似这里没什么用

    def add_btn_onclick(self, event):
        pass

    def see_btn_onclick(self, event):
        pass

table object

The content of the table displayed in the window is implemented separately in the data table class here. Here the GridTableBase class is inherited, and then several methods are refactored to return the number of rows, columns, and cell contents of the table, which can be displayed in the table of the window:

"""自定义数据表格类"""
from wx.grid import GridTableBase

class ListGridTable(GridTableBase):
    """自定义表格类
    下面分别重构了4个方法
    返回行数、列数、每个单元格的内容、列标题
    """

    def __init__(self, column_names, data):
        super().__init__()
        self.col_labels = column_names
        self.data = data

    def GetNumberRows(self):
        return len(self.data)

    def GetNumberCols(self):
        return len(self.col_labels)

    def GetValue(self, row, col):
        products = self.data[row]

        return {
            0: products.get('id'),
            1: products.get('category'),
            2: products.get('name_cn'),
            3: products.get('name_en'),
        }.get(col)

    def GetColLabelValue(self, col):
        return self.col_labels[col]

Effect

Login window
wxPython example code (shopping cart)
product list window
wxPython example code (shopping cart)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325981835&siteId=291194637