Thanks, Mr. RichardT.
With your help, I barely complete an MVC GUI style experiment(I actually did not know how to send messages from wx.FileDialog by using PyPubSub).
CLI Style(Read-Eval-Print Loop):
#!/usr/bin/env python3
import os, pickle, re, sys
class Content(object):
def __init__(self, title, artist, rating, ripped):
self.title = title
self.artist = artist
self.rating = rating
self.ripped = ripped
def __repr__(self):
return f'Title: {self.title}\nArtist: {self.artist}\nRating: {self.rating}\nRipped: {self.ripped}\n'
class Env(object):
def __init__(self, package, ls = [], fname="", flag = False):
self.package = package
self.ls = ls
self.fname = fname
self.flag = flag
def __repr__(self):
return f'<package: {self.package} list: {self.ls} filename: {self.fname} flag: {self.flag}>'
def install_package():
def insert_value(arg, env):
return Env(env.package, ls = [Content(*arg)] + env.ls, fname = env.fname)
def select(arg, env):
return Env(env.package, ls = env.ls, fname = env.fname, flag = True)
def new_file(arg, env):
with open(''.join(arg) + ".dat", "wb") as f:
pass
return Env(env.package, ls = env.ls, fname = arg)
def open_file(arg, env):
fname = ''.join(arg)
with open(fname, "rb") as f:
ls = pickle.load(f)
return Env(env.package, ls = ls, fname = fname)
def save_file(arg, env):
if env.fname == '':
return save_file_as('tmp.dat', env)
else:
os.rename(env.fname, env.fname + '.bak')
with open(env.fname, "wb") as f:
pickle.dump(env.ls, f)
return Env(env.package, ls = env.ls, fname = env.fname)
def save_file_as(arg, env):
fname = ''.join(arg)
with open(fname, "wb") as f:
pickle.dump(env.ls, f)
return Env(env.package, ls = env.ls, fname = fname)
def delete(arg, env):
return Env(env.package,
ls = [i for i in env.ls if i != env.ls[int(arg[0])]],
fname = env.fname)
return {'insert_value': insert_value,
'select': select,
'new_file': new_file,
'open_file': open_file,
'save_file': save_file,
'save_file_as': save_file_as,
'quit': lambda arg, env: sys.exit(),
'delete': delete}
# Eval
def interp(x, env):
x = [i.strip() for i in re.split("\(|,|、|\)", x) if i != ""]
return env.package[x[0]](x[1:], env)
# Print
def display(env):
if env.flag:
[print(i) for i in env.ls]
# print(env) # デバッグ用
return env
if __name__ == '__main__':
env = display(Env(install_package()))
while True:
try:
env = display(interp(input("simple database> "), env))
except KeyError:
env
except ValueError:
env
except IndexError:
env
GUI Style(MVC):
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# generated by wxGlade 1.0.4 on Wed Apr 12 23:57:09 2023
#
import wx
# begin wxGlade: dependencies
# end wxGlade
# begin wxGlade: extracode
from pubsub import pub
from simple_database import Env, install_package, interp
import sys
class Controller(object):
def __init__(self):
pub.subscribe(self.read, "Controller")
self.table = {"OnNewFile": "new_file({})",
"OnOpen": "open_file({})",
"OnSaveFile": "save_file",
"OnSaveFileAs": "save_file_as({})",
"OnQuit": "quit",
"OnAddCD": "insert_value({})"}
def read(self, x, env):
pub.sendMessage("Model.eval",
x = self.table[x[0]].format(", ".join(x[1:])),
env = env)
class Model(object):
def __init__(self):
pub.subscribe(self.eval, "Model")
def eval(self, x, env):
pub.sendMessage("View.SetProperties", env = interp(x, env))
def OnNewFile(self):
with NewFile(self) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
pass
pub.sendMessage("Controller.read",
x = ["OnNewFile", fileDialog.GetPath()],
env = self.env)
def OnOpen(self):
with OpenFile(self) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
pass
pub.sendMessage("Controller.read",
x = ["OnOpen", fileDialog.GetPath()],
env = self.env)
def OnSaveFileAs(self):
with SaveFileAs(self) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
pass
pub.sendMessage("Controller.read",
x = ["OnSaveFileAs", fileDialog.GetPath()],
env = self.env)
# end wxGlade
class View(wx.Frame):
def __init__(self, *args, **kwds):
# begin wxGlade: View.__init__
self.env = Env(install_package())
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.SetSize((400, 300))
self.SetTitle("Simple Database")
# Menu Bar
self.frame_menubar = wx.MenuBar()
wxglade_tmp_menu = wx.Menu()
item = wxglade_tmp_menu.Append(wx.ID_ANY, u"新規作成\tCtrl+N", "")
self.Bind(wx.EVT_MENU, lambda event: OnNewFile(self), item)
item = wxglade_tmp_menu.Append(wx.ID_ANY, u"開く...\tCtrl+O", "")
self.Bind(wx.EVT_MENU, lambda event: OnOpen(self), item)
item = wxglade_tmp_menu.Append(wx.ID_ANY, u"保存\tCtrl+S", "")
self.Bind(wx.EVT_MENU, lambda event: pub.sendMessage('Controller.read', x = ['OnSaveFile'], env = self.env), item)
item = wxglade_tmp_menu.Append(wx.ID_ANY, u"名前を付けて保存...\tShift+Ctrl+S", "")
self.Bind(wx.EVT_MENU, lambda event: OnSaveFileAs(self), item)
item = wxglade_tmp_menu.Append(wx.ID_ANY, "Quit\tCtrl+Q", "")
self.Bind(wx.EVT_MENU, lambda event: pub.sendMessage('Controller.read', 'OnQuit'), item)
self.frame_menubar.Append(wxglade_tmp_menu, u"ファイル")
self.SetMenuBar(self.frame_menubar)
# Menu Bar end
sizer_1 = wx.BoxSizer(wx.VERTICAL)
self.AddCD = wx.Button(self, wx.ID_ANY, "AddCD")
sizer_1.Add(self.AddCD, 0, 0, 0)
self.list_ctrl_1 = wx.ListCtrl(self, wx.ID_ANY, style=wx.LC_HRULES | wx.LC_REPORT | wx.LC_VRULES)
self.list_ctrl_1.AppendColumn(u"タイトル", format=wx.LIST_FORMAT_LEFT, width=-1)
self.list_ctrl_1.AppendColumn(u"アーティスト", format=wx.LIST_FORMAT_LEFT, width=-1)
self.list_ctrl_1.AppendColumn(u"レーティング", format=wx.LIST_FORMAT_LEFT, width=-1)
self.list_ctrl_1.AppendColumn(u"リップ", format=wx.LIST_FORMAT_LEFT, width=-1)
sizer_1.Add(self.list_ctrl_1, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
self.Layout()
self.Bind(wx.EVT_BUTTON, lambda event: ControllerDialog(self).ShowModal(), self.AddCD)
# end wxGlade
pub.subscribe(self.SetProperties, 'View')
def SetProperties(self, env):
self.env = env
self.list_ctrl_1.DeleteAllItems()
[[self.list_ctrl_1.InsertItem(sys.maxsize, obj) if col == 0
else self.list_ctrl_1.SetItem(index, col, obj) for col, obj
in enumerate([item.title, item.artist, item.rating, item.ripped])]
for index, item in enumerate(self.env.ls)]
# end of class View
class ControllerDialog(wx.Dialog):
def __init__(self, *args, **kwds):
# begin wxGlade: ControllerDialog.__init__
self.env = args[0].env
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_DIALOG_STYLE
wx.Dialog.__init__(self, *args, **kwds)
self.SetTitle("Add CD")
sizer_1 = wx.BoxSizer(wx.VERTICAL)
grid_sizer_1 = wx.GridSizer(4, 2, 0, 0)
sizer_1.Add(grid_sizer_1, 1, wx.EXPAND, 0)
Title = wx.StaticText(self, wx.ID_ANY, u"タイトル")
grid_sizer_1.Add(Title, 0, wx.ALIGN_CENTER, 0)
self.text_ctrl_1 = wx.TextCtrl(self, wx.ID_ANY, "")
grid_sizer_1.Add(self.text_ctrl_1, 0, wx.ALIGN_CENTER, 0)
Artist = wx.StaticText(self, wx.ID_ANY, u"アーティスト")
grid_sizer_1.Add(Artist, 0, wx.ALIGN_CENTER, 0)
self.text_ctrl_2 = wx.TextCtrl(self, wx.ID_ANY, "")
grid_sizer_1.Add(self.text_ctrl_2, 0, wx.ALIGN_CENTER, 0)
Rating = wx.StaticText(self, wx.ID_ANY, u"レーティング")
grid_sizer_1.Add(Rating, 0, wx.ALIGN_CENTER, 0)
self.text_ctrl_3 = wx.TextCtrl(self, wx.ID_ANY, "")
grid_sizer_1.Add(self.text_ctrl_3, 0, wx.ALIGN_CENTER, 0)
Ripped = wx.StaticText(self, wx.ID_ANY, u"リップ")
grid_sizer_1.Add(Ripped, 0, wx.ALIGN_CENTER, 0)
self.text_ctrl_4 = wx.TextCtrl(self, wx.ID_ANY, "")
grid_sizer_1.Add(self.text_ctrl_4, 0, wx.ALIGN_CENTER, 0)
sizer_2 = wx.StdDialogButtonSizer()
sizer_1.Add(sizer_2, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
self.button_OK = wx.Button(self, wx.ID_OK, "")
self.button_OK.SetDefault()
sizer_2.AddButton(self.button_OK)
self.button_CANCEL = wx.Button(self, wx.ID_CANCEL, "")
sizer_2.AddButton(self.button_CANCEL)
sizer_2.Realize()
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.SetAffirmativeId(self.button_OK.GetId())
self.SetEscapeId(self.button_CANCEL.GetId())
self.Layout()
self.Bind(wx.EVT_BUTTON, lambda event: self.OnAddCD(), self.button_OK)
self.Bind(wx.EVT_BUTTON, lambda event: self.Destroy(), self.button_CANCEL)
# end wxGlade
def OnAddCD(self):
pub.sendMessage('Controller.read',
x = ['OnAddCD',
self.text_ctrl_1.GetLineText(0),
self.text_ctrl_2.GetLineText(0),
self.text_ctrl_3.GetLineText(0),
self.text_ctrl_4.GetLineText(0)],
env = self.env)
self.Destroy()
# end of class ControllerDialog
class NewFile(wx.FileDialog):
def __init__(self, *args, **kwds):
# begin wxGlade: NewFile.__init__
self.env = args[0].env
kwds["style"] = kwds.get("style", 0) | wx.FD_SAVE
wx.FileDialog.__init__(self, *args, **kwds)
self.SetTitle(u"新規作成")
self.Layout()
# end wxGlade
# end of class NewFile
class OpenFile(wx.FileDialog):
def __init__(self, *args, **kwds):
# begin wxGlade: OpenFile.__init__
self.env = args[0].env
kwds["style"] = kwds.get("style", 0) | wx.FD_OPEN
wx.FileDialog.__init__(self, *args, **kwds)
self.SetTitle(u"開く...")
self.Layout()
# end wxGlade
# end of class OpenFile
class SaveFileAs(wx.FileDialog):
def __init__(self, *args, **kwds):
# begin wxGlade: SaveFileAs.__init__
self.env = args[0].env
kwds["style"] = kwds.get("style", 0) | wx.FD_SAVE
wx.FileDialog.__init__(self, *args, **kwds)
self.SetTitle("SaveFileAs")
self.Layout()
# end wxGlade
# end of class SaveFileAs
class SimpleDatabase(wx.App):
def OnInit(self):
self.View = View(None, wx.ID_ANY, "")
self.SetTopWindow(self.View)
self.View.Show()
return True
# end of class SimpleDatabase
if __name__ == "__main__":
m = Model()
SimpleDatabase = SimpleDatabase(0)
c = Controller()
SimpleDatabase.MainLoop()
To be honest, to use wx.ListCtrl was much more difficult than wx.FileDialog… It took a time to figure out what is happened.
This implementation is still incomplete('cause I do not know how to do with right clicks on wx.ListCtrl); however, my basic purpose was done successfully.
Anyway, thank you very much!!
