@shaobaobaoer
2020-03-23T15:12:00.000000Z
字数 7536
阅读 852
数据库原理
参考了网上的方法,过程如下
1.设置SQLSERVER混合模式(SQL Server 身份验证和Windows 身份验证)
2.启用SQL服务TCP/IP,启用协议与IP地址
3.启用SQL服务SQLSERVER Browser /Sql Server(实例名)
4.CMD命令输入:netstat -a 如果找到有“0.0.0.0:1433”,就说明SqlServer在监听了
5.防火墙应用通过window防火墙进行通信,添加应用
C:\Program Files\Microsoft SQLServer\MSSQL10.MSSQLSERVER\MSSQL\Binn\sqlservr.exe
6.防火墙-高级设置,添加入站规则1433端口
使用python连接的方法很简单。利用pymssql库就可以轻松连接,方法如下
self.conn = pymssql.connect(MSSQL_HOST, MSSQL_USER,MSSQL_PASSWORD, MSSQL_DATABASE)
其中包括了一些超参数,比如地址,用户,密码和DB名字,我的设置如下(我的数据库在虚拟机里,所以是192.168的IP地址)
MSSQL_HOST = "192.168.25.134"MSSQL_USER = "shaobao"MSSQL_PASSWORD = "toor"MSSQL_DATABASE = "newdb"
面对这样一个问题,我们最主要的任务是分清楚我们需要哪些界面。其实详细划分一下的话,也不是很困难。
我主要用的是WXpython来建立的GUI程序。根据题目要求,我需要很多的子窗口以及一个主窗口,我命名这些窗口分别如下所示。父子类别用\t来分隔开
Index.py主界面MessageDialog.py辅助提示框StudentLogin.py登陆界面StudentChooseClass.py学生选课界面StudentScoreList.py学生成绩界面ScoreManageMent.py管理界面ScoreAnalyze.py成绩分析界面ManageClassDetail.py课程管理界面ClassDetailInputDialog.py添加课程界面ManageStudentDetail.py学生管理界面StudentDetailInputDialog.py添加学生信息界面
此外,我们将在最外层的Mian_frame中绑定这些页面并添加事件。大致的代码如下所示
def OnInit(self):# Init All framesself.Index = Index(parent=None)self.ScoreManageMent = ScoreManageMent(parent=None)self.ScoreManageMent.col2teacher_dict = self.DBA.get_c()self.ScoreManageMent.m_choice_show_selectable_columnsChoices \= list(self.ScoreManageMent.col2teacher_dict.keys())self.ScoreManageMent.m_choice_show_selectable_columns.SetItems(self.ScoreManageMent.m_choice_show_selectable_columnsChoices)self.StudentChooseClass = StudentChooseClass(parent=None)self.StudentLogin = StudentLogin(parent=None)self.StudentScoreList = StudentScoreList(parent=None)self.ScoreAnalyze = ScoreAnalyze(parent=None)self.ManageClassDetail = ManageClassDetail(parent=None)self.ManageStudentDetail = ManageStudentDetail(parent=None)# Init Routerself.__bind_router()self.__bind_event()# Show Indexself.Index.Show(show=True)return True
很显然,原生的WXpython并没有路由方法,甚至Wxpython对于单界面的GUI有着更好的支持。要保持当前打开的窗体为一个,需要定义一些函数,比如某个窗体关闭,某个窗体会打开。对此,我们需要自定义一些事件。我参照了网络后端架构的后端的路由函数。将一些事件固定得绑定到这个函数,就可以实现这样子的功能。具体的方法如下所示
def OnRouter_change(self, event, value='Index'):self.__current_active_frame = valueif self.__current_active_frame == "Index":self.Index.Show()self.StudentLogin.Hide()self.StudentChooseClass.Hide()self.StudentScoreList.Hide()self.ScoreManageMent.Hide()self.SetTopWindow(self.Index)elif self.__current_active_frame == "StudentLogin":self.Index.Hide()self.StudentLogin.Show()self.StudentChooseClass.Hide()self.StudentScoreList.Hide()self.ScoreManageMent.Hide()self.SetTopWindow(self.StudentLogin)...
很显然,我将会判断传入OnRouter_change事件的传入值。通过传入值来判断,让哪些页面Hide。让哪一个页面Show。
然而,在wxpython的原声实现中,并不支持给事件传值。因为事件绑定的是一个函数名而传参。形如这样的形式
self.StudentChooseClass.drop_class_button.Bind(wx.EVT_BUTTON,self.OnDropSelectedClass_StudentChooseClass)
最后,我在wxpython advancer 文档中查阅到,如果需要给事件传递参数,需要用到lambda方法,并且附上默认得event参数。
解决方案如下
self.Bind(wx.EVT_BUTTON,lambda event: self.OnRouter_change(event, 'StudentLogin'),self.Index.m_button_Index2StudentLogin)
在这个页面中。我们需要将每门课的成绩输出一个平均分,做成一个纵向的直方图。我想到我能够利用matplotlib库的作图工具来完成这个操作。生成一个平均分的纵向直方图诚然不难。原生的代码如下
label_list, data_list = self.DBA.get_avg_s_group_by_class()width = 0.4ind = np.linspace(1.5, 7.5, data_list.__len__())for i in range(data_list.__len__()):self.axes.barh(ind[i] - width / 2, data_list[i],width,color=self.color_list[i % self.color_list.__len__()],label=data_list[i])self.axes.set_yticks(ind)self.axes.set_yticklabels(label_list)self.axes.set_ylabel('课程名称')self.axes.set_xlabel('平均成绩')self.axes.set_title('成绩分布')self.axes.legend()
然而,图片是图片,GUI是GUI。如果把图片保存到本地,用GUI再次读取,也是个不错的方法,但是这样一点也不geek。应该如何把连个结合起来呢?
实际上,matplotlib已经为我们想到了方法,其实matplotlib可以用一个后端,叫做WXAgg。该后端完美支持了全平台的wxGUI框架。python自然也不例外。只需要在包引入上动些脑经就可以了
matplotlib.use('WXAgg')from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvasfrom matplotlib.figure import Figureimport matplotlib.pyplot as pltclass ScoreAnalyze(wx.Frame):def __init__(self, parent):...self.figure = Figure()self.axes = self.figure.add_subplot(111)
这样,axes就会调用我们生成图像的句柄,将图像返回到GUI界面中并打应出来。
实际上,在matplotlib中对于非西文字体的支持非常差,动不动就乱码。实际上这也是在matplotlib库利用中非常头大的问题。用英文自然也可以,但是这样就无法达到老师的要求了。
比较简单的方法,是定义plt的字体。将字体设置为SimHei来让中文不乱码。需要每次引用的时候都写一遍,虽然绝非一劳永逸,但是也能缓解这样的问题。
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
还有直接修改源文件的方法,再次不多赘述。
最终图片就能顺利打印出来了

DML语句,包括了增删改查。查自然是没什么问题。重要的就是增删改,毕竟wxpython不是PB。前端的修改必须通过SQL语句返回后端去,如何实现这些方法呢?

行吧,毕竟我们没有PB这么好用的数据窗口,那就多加几个键分开处理。因此,我增加了新增,保存,删除和退出键。把增删改给分开来
删除键,就是点了删除该咋办呢?如何生成SQL语句呢?
讲道理,应该是需要做一个弹出框,让用户输入主键后删除,并且设置为NOT NULL的主键,不应该允许用户在插入的时候将其清空。对此,我们需要用到之前弹出框文件中自定义的一种弹出框。
对此我新建立了一个临时的弹出框,让用户输入主键,随后返回给DB类,让它生成delete语句
def delete_action(self, event):dlgtext = wx.TextEntryDialog(self, '请输入删除的课程号号','提示信息', 'C1')# dlgtext.SetValue("Python is the best!")if dlgtext.ShowModal() == wx.ID_OK:SNO = dlgtext.GetValue()self.DBA.manage_c_delete_data(SNO)dlgtext.Destroy()self.OnRefresh_Data()def manage_c_delete_data(self, CNO):cursor = self.conn.cursor(as_dict=True)cursor.execute("DELETE FROM C WHERE CNO = '{}'".format(CNO))
这样就能做到删除了
新增也是一个头大的东西,如果让用户在前端的表格中输入东西并返回语句的话,做一些数据的检查很困难。
对此,我需要自定义一个弹出框,让用户来输入东西。
就像这个样子

由于窗体代码非常复杂,所以不贴了。具体会在后文的github下载链接中查看。
点击提交后,会出发这样一个函数,主要就是检查主键是否为空。MessageDialog_CANCEL是我们编写的一个通用提示框类。
def GetInputValue(self):if self.m_textCtrl_SNO.GetValue() == "":MessageDialog_CANCEL("SNO 不可为空", "错误信息")return [self.m_textCtrl_SNO.GetValue(),self.m_textCtrl_SNAME.GetValue(),self.m_textCtrl_SEX.GetValue(),self.m_textCtrl_AGE.GetValue(),self.m_textCtrl_SDEPT.GetValue(),str(__import__("random").randint(80, 760)),self.m_textCtrl_LOGN.GetValue(),self.m_textCtrl_PSWD.GetValue()]
在审核通过后,会通过一个句柄传递给DBA的数据库控制类。生成INSERT 语句并执行。
def insert_action(self, event):dlgtext = StudentDetailInputDialog(None)if dlgtext.ShowModal() == wx.ID_OK:insert_data = dlgtext.GetInputValue()if insert_data[0] == " ":return Falseelse:self.DBA.manage_s_insert_data(insert_data)self.OnRefresh_Data()
修改数据可能是最为困难的了。需要判断哪些列被修改,并返回SQL语句
这个和之前在修改成绩的时候的方法很类似,修改成绩也是这么实现的。
def update_action(self, event):if self.lock_for_grid == False:self.lock_for_grid = Trueself.m_button_insert.Disable()self.m_button_delete.Disable()self.m_button_exit.Disable()self.m_button_update.SetLabel("提交")self.m_grid_manage_student_detail.EnableEditing(True)self.renew_raw_list_value()else:self.DBA.manage_s_update_data(numpy.argwhere(changed_list!=raw_list),self.column_value)self.m_grid_manage_student_detail.EnableEditing(False)self.m_button_update.SetLabel("保存")self.m_button_insert.Enable()self.m_button_delete.Enable()self.m_button_exit.Enable()self.lock_for_grid = Falseself.OnRefresh_Data()

https://github.com/ninthDevilHAUNSTER/DB_PB_homework
这次PB实验,实际上我是用的WXPYTHON做的。我学会了如何用可视化工具来编写自己的GUI窗口,而不是之前那样子直接开局一个记事本,代码全靠敲。尽管用GUI界面建立了窗口,但是还是要写很多的事件绑定代码。其中,我写了主窗体以及路由函数,小组里余下三位同学写了登陆,选课和成绩分布。我写了管理界面,并整合了他们的代码。将小组的代码绑定路由并修改了一番。
获益还是很多的,比如说我们组加起来的总代码量超过了2229行。
github.com/AlDanial/cloc v 1.80 T=0.50 s (74.0 files/s, 11786.0 lines/s)-------------------------------------------------------------------------------Language files blank comment code-------------------------------------------------------------------------------Python 28 771 377 2229
再比如说我们一共写了10个主窗体...嗯没错,GUI都是我画的。
最后,我觉得这个实验还是很有趣的,毕竟全程用python写就很有挑战性。实际上在改这个功能上,我本来想用sqlmap的页面相似度算法,但是发现自己需要判断非常多的东西。最终还是放弃了异或运算。转向了numpy的怀抱,发现这个东西非常好用。