【发布时间】:2021-07-21 23:58:21
【问题描述】:
我知道这可能是一个基本问题,但我刚刚进入 GUI 构建,并且不需要在课堂上涉猎太多。我有一些代码(见下文)来处理程序的 GUI 部分(CLI 版本已经完美运行,但是每当他们看到 CLI 时,管理人员都会在山上运行......),但我不太明白锻炼。仅供参考,在 Windows 10 上运行 python 3.9.1 和 wxPython 4.1.1 Phoenix(尽管wx.version 也吐出wxWidgets 3.1.5,如果相关的话)
很多代码都是基于示例/其他人在这里问的东西,不幸的是我找不到原始帖子来感谢他们,但这真的只是为了学习,一旦我掌握了我的东西'我正在做我会从头开始写一切。所以对原作者,我很抱歉,但我非常感谢你!
该程序将显示一个“开始”屏幕(框架),只有一个标题和一个按钮。单击该按钮后,将打开一个模式文件对话框,用户在其中选择要使用的 .csv 数据文件,起始帧消失,并且在右窗格中出现一个新帧,其中包含数据视图(在网格大小调整器中)和左窗格中的一些下拉菜单和单选按钮。最后一个下拉列表 (self.inputNum) 将绑定到一个事件处理程序,该处理程序将添加许多额外的下拉列表供用户选择要分析的不同目标(当然数量取决于他们在 @ 987654330@ 组合框)。但是,在运行脚本时,我得到一个错误
Traceback (most recent call last):
File "C:\Python\Scripts\wxLOD_2class_panelbased.py", line 98, in _OnStart
self.frm2 = Frame2(None)
File "C:\Python\Scripts\wxLOD_2class_panelbased.py", line 154, in __init__
self.CreateGrid(datalist, cols)
AttributeError: 'Frame2' object has no attribute 'CreateGrid'
我在这里搜索,发现有人有类似的问题(在同一类的 init 中调用类方法),但“正确”的答案是使用 @987654332 调用该方法@(OP 一直称它为 classmethodname(args),我已经在这样做了。
提前为草率的格式道歉。我试图尽可能多地清理它,但由于我不知道错误来自哪里,我不想离我用来构建我目前拥有的混乱的(工作)示例太远。请参阅下面的脚本、基于“Frame2”的稍微修改的脚本以及屏幕截图。
我的代码(我知道在一行中导入模块是不受欢迎的,但是我为这篇文章压缩了很多):
import os, sys, csv, time, math, warnings, collections, wx, wx.grid, scipy
import numpy as np
import matplotlib.pyplot as plt
import win32gui as wg
import pandas as pd
from datetime import datetime
from scipy.stats import norm
from tkinter import filedialog
from tkinter import *
from decimal import *
pd.options.mode.chained_assignment = None # default='warn'
class StartFrame(wx.Frame):
"""App controller class"""
FRAME_MIN_SIZE = (900,600)
def __init__(self, parent):
wx.Frame.__init__(self, parent=parent,
id=wx.ID_ANY, title="LOD Calculator", size=wx.Size(900,600),
style=wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.TAB_TRAVERSAL)
self.SetSizeHints(wx.DefaultSize, wx.DefaultSize)
self.startpnl = wx.Panel(self)
self.startvsizer = wx.BoxSizer(wx.VERTICAL)
self.startbtnsizer = wx.BoxSizer(wx.HORIZONTAL)
# make title and subtitle, format fonts
self.st = wx.StaticText(self.startpnl, style=wx.ALIGN_CENTER, label="DCB LoD Calculator")
self.font = self.st.GetFont()
self.font.PointSize += 10
self.font = self.font.Bold()
self.st.SetFont(self.font)
self.stsub = wx.StaticText(self.startpnl, style=wx.ALIGN_CENTER, label="Probit/Linear Regression Method")
self.fontsub = self.stsub.GetFont()
self.fontsub.PointSize += 2
self.stsub.SetFont(self.fontsub)
# make the "begin" button to start the script
self.btn = wx.Button(self.startpnl, wx.ID_ANY, "Start Analysis",\
size = (200,60))
self.btn.Bind(wx.EVT_BUTTON, self._OnStart)
self.startbtnsizer.AddStretchSpacer()
self.startbtnsizer.Add(self.btn, 0, wx.CENTER)
self.startbtnsizer.AddStretchSpacer()
self.startvsizer.Add(self.st, wx.SizerFlags().Expand().Border(wx.ALL, 25))
self.startvsizer.Add(self.stsub, wx.SizerFlags().Expand().Border(wx.ALL, 25))
self.startvsizer.AddStretchSpacer()
self.startvsizer.Add(self.startbtnsizer, wx.SizerFlags().Expand().Border(wx.ALL, 25))
self.startvsizer.AddStretchSpacer()
self.startpnl.SetSizerAndFit(self.startvsizer)
# create a menu & status bar
self.makeMenuBar()
self.CreateStatusBar()
self.SetStatusText("PLACEHOLDER -- STATUS BAR")
self.Center(wx.BOTH)
def makeMenuBar(self):
fileMenu = wx.Menu()
helloItem = fileMenu.Append(-1, "&Hello...\tCtrl-H",
"Help string shown in status bar for this menu item")
fileMenu.AppendSeparator()
exitItem = fileMenu.Append(wx.ID_EXIT)
helpMenu = wx.Menu()
aboutItem = helpMenu.Append(wx.ID_ABOUT)
menuBar = wx.MenuBar()
menuBar.Append(fileMenu, "&File")
menuBar.Append(helpMenu, "&Help")
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.OnHello, helloItem)
self.Bind(wx.EVT_MENU, self.OnExit, exitItem)
self.Bind(wx.EVT_MENU, self.OnAbout, aboutItem)
def OnExit(self, event):
"""Close frame & terminate app"""
self.Close(True)
def OnHello(self, event):
"""oh hey what's up"""
wx.MessageBox("Stuff would go here",\
"The dialog with the stuff would have this title")
def OnAbout(self, event):
"""for displaying 'about' dialog"""
wx.MessageBox("Main dialog box text for About",\
"About title text", wx.OK|wx.ICON_INFORMATION)
def _OnStart(self,event):
self.Hide()
self.frm2 = Frame2(None)
self.frm2.Show()
class Frame2(wx.Frame):
"""Data load frame"""
FRAME_MIN_SIZE = (900,600)
def __init__(self, parent):
wx.Frame.__init__(self, parent=parent,
id=wx.ID_ANY, title="LOD Calculator", size=wx.Size(900,600),
style=wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.TAB_TRAVERSAL)
self.SetSizeHints(wx.DefaultSize, wx.DefaultSize)
self.dirname = os.getcwd()
filedlg = wx.FileDialog(self, 'Choose a file', os.getcwd(),\
'', 'CSV files (*.csv)|*.csv|All files(*.*)|*.*', wx.FD_OPEN)
if filedlg.ShowModal() == wx.ID_OK:
self.dirname = filedlg.GetDirectory()
self.filename = filedlg.GetFilename()
self.path = os.path.join(self.dirname, self.filename)
self.file = open(self.path, 'r')
dialect = csv.Sniffer().sniff(self.file.read(1024),\
delimiters = ";|,")
self.file.seek(0)
csvfile = csv.reader(self.file, dialect)
filedata = []
filedata.extend(csvfile)
self.file.seek(0)
datasample = self.file.read(2048)
self.file.seek(0)
if csv.Sniffer().has_header(datasample):
self.cols = next(csvfile)
self.datalist = []
self.datalist.extend(filedata[1:len(filedata)])
else:
self.cols = []
for idx in range(len(next(csvfile))):
self.cols.append(f'col_{i}')
self.file.seek(0)
self.datalist = filedata
self.file.close()
# Create Sizers
Sizer1 = wx.BoxSizer(wx.HORIZONTAL)
paraLsizer = wx.BoxSizer(wx.VERTICAL)
paraRsizer = wx.BoxSizer(wx.VERTICAL)
# Create Panels
self.paraLpnl = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition,\
wx.DefaultSize, wx.TAB_TRAVERSAL)
self.paraRpnl = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition,\
wx.DefaultSize, wx.TAB_TRAVERSAL)
### FOLLOWING LINE SPITS THE ERROR
### HAVE TRIED USING BOTH self.datalist/self/cols AND JUST datalist/cols - NO EFFECT
self.CreateGrid(self.datalist, self.cols)
# Create Widgets
self.reImportButton = wx.Button(self.paraLpnl,\
wx.ID_ANY, u"Import New CSV File", wx.DefaultPosition,\
wx.DefaultSize, 0)
self.paraInput = wx.StaticText(self.paraLpnl,\
label="Select input column:", style = wx.ALIGN_CENTER_HORIZONTAL)
self.inputcb = wx.ComboBox(self.paraLpnl, wx.ID_ANY, value=cols[0],\
choices=cols, style = wx.CB_READONLY)
self.logbtn = wx.RadioButton(self.paraLpnl, wx.ID_ANY, label='Check button\
if input is in log10(conc.)')
self.logbtn.SetValue(False)
numInputs = ['1','2','3','4','5','6','7','8','9']
self.inputNumLabel = wx.StaticText(self.paraLpnl, \
label="Select number of targets", style = wx.ALIGN_CENTER_HORIZONTAL)
self.inputNum = wx.ComboBox(self.paraLpnl, wx.ID_ANY, value='', \
choices = numInputs, style = wx.CB_READONLY)
# Add stuff to sub-sizers, set them, and fit stuff
paraLsizer.Add(self.reImportButton, 0, wx.ALL, 5)
paraLsizer.Add(self.paraInput, 0, wx.ALL, 5)
paraLsizer.Add(self.inputcb, 0, wx.ALL, 5)
paraLsizer.Add(self.logbtn, 0, wx.ALL, 5)
paraLsizer.Add(self.inputNumLabel, 0, wx.ALL, 5)
paraLsizer.Add(self.inputNum, 0, wx.ALL, 5)
self.paraLpnl.SetSizer(paraLsizer)
self.paraLpnl.Layout()
paraLsizer.Fit(self.paraLpnl)
paraRsizer.Add(self.grid, 1, wx.EXPAND)
self.paraRpnl.SetSizer(paraRsizer)
self.paraRpnl.Layout()
paraRsizer.Fit(self.paraRpnl)
# Add panels (containing sub-sizers) to main sizer
Sizer1.Add(self.paraLpnl, 0, wx.EXPAND |wx.BOTTOM, 5)
Sizer1.Add(self.paraRpnl, 1, wx.EXPAND |wx.ALL, 5)
# Set main sizer for the window; add status and menu bars
self.SetSizer(Sizer1)
StartFrame.makeMenuBar(self)
StartFrame.CreateStatusBar(self)
StartFrame.SetStatusText(self, "PLACEHOLDER -- STATUS BAR")
# Finally, lay the whole window out and center it.
self.Layout()
self.Centre(wx.BOTH)
#create the grid
def createGrid(self, datalist, colnames):
if getattr(self, 'grid', 0): self.grid.Destroy()
self.grid = wx.grid.Grid(self.paraRpnl, 0)
self.grid.CreateGrid(len(datalist), len(colnames)) #create grid, same size as file (rows, cols)
#fill in headings
for i in range(len(colnames)):
self.grid.SetColLabelValue(i, colnames[i])
#populate the grid
for row in range(len(datalist)):
for col in range(len(colnames)):
try:
self.grid.SetCellValue(row,col,datalist[row][col])
except:
pass
self.grid.AutoSizeColumns(False) # size columns to data (from cvsomatic.py)
self.twiddle()
def twiddle(self):
x,y = self.GetSize()
self.SetSize((x, y+1))
self.SetSize((x,y))
def Exit(self, event):
if getattr(self, 'file',0):
self.file.close()
self.Close(True)
import wx.lib.mixins.inspection
app = wx.App()
frm = StartFrame(None)
frm.Show()
wx.lib.inspection.InspectionTool().Show()
app.MainLoop()
我的“Frame2”基于以下稍微编辑但可以工作的代码(不知道他为什么将它分成 3 个类,或者 csv_view(wx.App) 类的用途是什么):
import wx, os, sys, csv
import numpy as np
import wx.grid
class MyFrame3(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, id = wx.ID_ANY,\
title=wx.EmptyString, pos=wx.DefaultPosition, size=wx.Size(900,600),\
style=wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.TAB_TRAVERSAL)
self.SetSizeHints(wx.DefaultSize, wx.DefaultSize)
Sizer1 = wx.BoxSizer(wx.HORIZONTAL)
self.Left_Panel = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition,\
wx.DefaultSize, wx.TAB_TRAVERSAL)
LeftSizer = wx.BoxSizer(wx.VERTICAL)
self.ImportButton = wx.Button(self.Left_Panel,\
wx.ID_ANY, u"Import CSV File", wx.DefaultPosition, wx.DefaultSize, 0)
LeftSizer.Add(self.ImportButton, 0, wx.ALL, 5)
self.Left_Panel.SetSizer(LeftSizer)
self.Left_Panel.Layout()
LeftSizer.Fit(self.Left_Panel)
Sizer1.Add(self.Left_Panel, 0, wx.EXPAND |wx.ALL, 5)
self.Right_Panel = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition,\
wx.DefaultSize, wx.TAB_TRAVERSAL)
RightSizer = wx.BoxSizer(wx.VERTICAL)
self.Right_Panel.SetSizer(RightSizer)
self.Right_Panel.Layout()
RightSizer.Fit(self.Right_Panel)
Sizer1.Add(self.Right_Panel, 1, wx.EXPAND |wx.ALL, 5)
self.SetSizer(Sizer1)
self.Layout()
self.menubar = wx.MenuBar(0)
self.fileMenu = wx.Menu()
self.importMenu = wx.MenuItem(self.fileMenu,\
wx.ID_ANY, u"Import", wx.EmptyString, wx.ITEM_NORMAL)
self.fileMenu.Append(self.importMenu)
self.menubar.Append(self.fileMenu, u"&File")
self.SetMenuBar(self.menubar)
self.Centre(wx.BOTH)
# Connect Events
self.ImportButton.Bind(wx.EVT_BUTTON, self.ImportFunc)
self.Bind(wx.EVT_MENU, self.ImportFunc, id = self.importMenu.GetId())
class csv_view(wx.App):
def OnInit(self):
self.frame=MyFrame3(None, -1, 'PyStereo', size=(900,600))
self.SetTopWindow(self.frame)
return True
class MyFrame(MyFrame3):
def __init__(self, parent, size = wx.Size(900,600)):
MyFrame3.__init__(self, parent)
self.dirname = os.getcwd()
# Import/Open CSV
def ImportFunc(self, event):
dlg=wx.FileDialog(self, 'Choose a file', self.dirname, '','CSV files (*.csv)|*.csv|All files(*.*)|*.*',wx.FD_OPEN)
if dlg.ShowModal() == wx.ID_OK:
self.dirname=dlg.GetDirectory()
self.filename=dlg.GetFilename()
self.file=open(os.path.join(self.dirname, self.filename), 'r')
#check for file format with sniffer
dialect = csv.Sniffer().sniff(self.file.read(1024), delimiters=";|,")
self.file.seek(0)
csvfile=csv.reader(self.file,dialect)
filedata = [] #put contents of csvfile into a list
filedata.extend(csvfile)
self.file.seek(0)
#grab a sample and see if there is a header
sample=self.file.read(2048)
self.file.seek(0)
if csv.Sniffer().has_header(sample): #if there is a header
colnames=next(csvfile) # label columns from first line
datalist=[] # create a list without the header
datalist.extend(filedata[1:len(filedata)]) #append data without header
else:
row1=next(csvfile) #if there is NO header
colnames=[]
for i in range(len(row1)):
colnames.append('col_%d' % i) # label columns as col_1, col_2, etc
self.file.seek(0)
datalist=filedata #append data to datalist
self.file.close()
self.createGrid(datalist, colnames)
grid_sizer = wx.BoxSizer(wx.VERTICAL)
grid_sizer.Add(self.grid, 1, wx.EXPAND)
self.Right_Panel.SetSizer(grid_sizer)
self.Right_Panel.Layout()
#create the grid
def createGrid(self, datalist, colnames):
if getattr(self, 'grid', 0): self.grid.Destroy()
self.grid=wx.grid.Grid(self.Right_Panel, 0)
self.grid.CreateGrid(len(datalist), len(colnames)) #create grid, same size as file (rows, cols)
#fill in headings
for i in range(len(colnames)):
self.grid.SetColLabelValue(i, colnames[i])
#populate the grid
for row in range(len(datalist)):
for col in range(len(colnames)):
try:
self.grid.SetCellValue(row,col,datalist[row][col])
except:
pass
self.grid.AutoSizeColumns(False) # size columns to data (from cvsomatic.py)
self.twiddle()
def twiddle(self): # from http://www.velocityreviews.com/forums/t330788-how-to-update-window-after-wxgrid-is-updated.html
x,y = self.GetSize()
self.SetSize((x, y+1))
self.SetSize((x,y))
def Exit(self, event):
if getattr(self, 'file',0):
self.file.close()
self.Close(True)
import wx.lib.mixins.inspection
app = wx.App(0)
Frame_02 = MyFrame(None)
Frame_02.Show()
wx.lib.inspection.InspectionTool().Show()
app.MainLoop()
我的应用程序在启动时的屏幕(在您尝试在“开始分析”按钮生成的模式对话框中选择一个 .csv 文件之前一直有效),我用来指导我打开第二帧的应用程序,同样导入 csv 后的应用程序。
此外,任何不直接涉及我的问题的改进/建议/更正都非常受欢迎。我是这方面的开发新手,需要我能得到的所有帮助!
【问题讨论】:
标签: python-3.x csv user-interface wxpython