zoukankan      html  css  js  c++  java
  • python gui之tkinter界面设计pythonic设计

    ui的设计,控件id的记录是一件比较繁琐的事情。

    此外,赋值和读取数据也比较繁琐,非常不pythonic。

    有没有神马办法优雅一点呢?life is short。

    鉴于控件有name属性,通过dir(Entry_obj)得知,存放在一个_name的属性里面。于是就有了以下代码:

    Entry(frame,name='your_id1').grid(row=x1,column=y1)
    Entry(frame,name='your_id2').grid(row=x2,column=y2)
    ...
    Entry(frame, name='your_idn').grid(row=xn,column=yn)
    

    当然还有其他输入控件,此处略。

    可以通过 frame.grid_slaves()得到其上面所有的控件。

    然后可以通过 __class__来判断具体类型。可以在此基础上判断哪些控件是输入控件,如下:

    for ctrl in frame.grid_slaves():
        if ctrl.__class__.__name__ in ('Entry','Text',....):
            return True
    return False
    

      

    此外,我们还需要一个映射表,来关联界面控件和我们的模型。

    mapper={'model_attr1':ctrol_id1, 'model_attr2':ctrol_id2,...., 'model_attrn':ctrol_idn}

    通过这个映射表,我们可以方便的进行表单数据的设置和读取,假设所有输入控件皆为Entry,于是就有了一下读取表单的代码:

        def get_form_data(self):
            vals = {}.fromkeys(self.mapper.keys(),False)
            ctrls = dict([(x._name,x) for x in self.input_ctrls])
            for k,v in self.mapper.items():
                ctrl = ctrls.get(v,False)
                if ctrl and ctrl.get():
                    vals.update({k:ctrl.get()})
                    #print vals
            logging.debug('product form data:%s'%vals)
            return vals
    

    关于input_ctrls的说明,因为控件的从属关系,本质上应该是一个树。

    可以采用一个设计:

    将控件放入到frame中,所有的frame放入到一个列表中frame_list。

    frame中的控件用grid方式布局。于是就有了以下读取界面上所有输入控件的代码:

        def _is_input(self, ctrl_obj):
            """
            是否为输入控件
            :param ctrl_obj:
            :return:
            """
            if ctrl_obj:
                name = ctrl_obj._name
                matches= ['txt_','cb_']
                if (name.split('_')[0]+'_') in matches:
                    return True
            return False
    
        def _get_input_ctrls(self, container):
            if container:
                cs = container.grid_slaves()
                cs.extend(container.pack_slaves())
                for c in cs:
                    if self._is_input(c):
                        self.input_ctrls.append(c)
                    else:
                        self._get_input_ctrls(c)
    
        def get_input_ctrls(self):
            self.input_ctrls=[]
            for f in self.frames:
                self._get_input_ctrls(f)
            return self.input_ctrls
    

     是否输入控件采用了根据_name属性进行的判断。此处需要有coding规范约束。

    实际上可以改写_is_input逻辑,根据__class__来进行枚举判断。

    相应的设置表单的值逻辑如下:

        def set_data(self, data):
            if not isinstance(data,dict):
                raise TypeError
            if not self.mapper:
                raise "set_mapper method must be called before this method being called."
            ctrls = dict([(x._name,x) for x in self.get_input_ctrls()])
            for k,v in self.mapper.items():
                if True:#data.get(k,None) is not None:
                    ctrl_obj = ctrls.get(v)
                    if ctrl_obj:
                        state = ctrl_obj['state']
                        ctrl_obj.configure(state='normal')
                        if isinstance(ctrl_obj,NewCBtn):
                            ctrl_obj.checked(data.get(k))
                        elif isinstance(ctrl_obj,NewCBox):
                            vals = data.get(k)
                            if isinstance(vals,(list,tuple)):
                                val= vals[-1]
                                if not ctrl_obj.dictionary:
                                    t={vals[-1]:vals[0]}
                                    ctrl_obj.set_dict(t)
                                ctrl_obj.set(val)
                            else:
                                ctrl_obj.value(vals)
                        elif isinstance(ctrl_obj,NewImage):
                            ctrl_obj.Image(data.get(k))
                        elif isinstance(ctrl_obj,Text):
                            ctrl_obj.delete("1.0",END)
                            ctrl_obj.insert("1.0",data.get(k))
                        elif isinstance(ctrl_obj,Entry):
                            ctrl_obj.delete(0,END)
                            ctrl_obj.insert(0, str(data.get(k) or ''))
                        else:
                            logging.warning('unkown control type object:%s'%ctrl_obj.__class__.__name__)
    
                        ctrl_obj.configure(state=state)

    其中,类Newxxx为自己定义的类:

    NewCBox

    class NewCBox(ttk.Combobox):
        def __init__(self, master, dictionary={}, *args, **kw):
            if not kw:kw={'values':sorted(list(dictionary.keys()))}
            else:kw.update({'values':sorted(list(dictionary.keys()))})
            ttk.Combobox.__init__(self, master, *args,  **kw)
            self.dictionary = dictionary
            #self.bind('<<ComboboxSelected>>', self.selected) #purely for testing purposes
            self.bind('<Control-a>', self.select_all_input)
    
        def select_all_input(self,event):
            #选择输入文本,不起作用
            #print 'Ctrl+A'
            text = self.get()
            vals = self.cget('values')
            for i in range(len(vals)):
                if vals[i]==text:
                    self.current(i)
    
        def set_dict(self, dictionary):
            self.dictionary=dictionary
            self['values']= sorted(list(dictionary.keys()))
    
        def value(self,new_val=None):
            if new_val is None:
                key = self.get()
                d= self.dictionary
                k1=key.encode('UTF-8')#ASCII码就是UTF-8编码的最少字节的版本
                k2=key.encode('GB2312')
                k3=key.encode('CP936')
                return d.get(key,d.get(k1,d.get(k2,d.get(k3,False))))else:
                for k,v in self.dictionary.items():
                    if str(new_val)==str(v):
                        self.set(k)
                        return k
        # def selected(self, event): #Just to test
        #     print(self.value())

    NewImage

    import io
    class NewImage(Label):
        def __init__(self, master=None,image_path=r'nullImage.png', cnf={}, **kw):
            image = Image.open(image_path)
            self.default_image = ImageTk.PhotoImage(image)
            self.current_image = self.default_image
            Label.__init__(self, image=self.current_image, master=master, cnf=cnf,**kw)
    
        def Image(self,base64_source=None,encoding='base64'):
            if not base64_source is None:
                if base64_source:
                    base64_source = resize_img(base64_source,size=(100,100))
                    image_stream = io.BytesIO(base64_source.decode(encoding))
                    image = Image.open(image_stream)
                    self.current_image =ImageTk.PhotoImage(image)#PhotoImage(file='pro.gif')# PhotoImage(data=background_stream.getvalue())
                    self['image']=self.current_image
                else:
                    self['image']=self.default_image
            return self['image']

    NewCBtn

    class NewCBtn(Checkbutton):
        def __init__(self, master,*args, **kw):
            self.value = IntVar()
            if not kw:kw={'variable':self.value}
            else:kw.update({'variable':self.value})
            Checkbutton.__init__(self,master,*args,**kw)
    
        def checked(self,value=None):
            if value is not None:
                self.value.set(value and 1 or 0)
            return self.value.get()
  • 相关阅读:
    asyncio
    pytz
    celery
    xml
    jsonpath
    requests
    SQLite 数据库存储
    SQLite 数据库存储
    Android 记住密码功能
    Android 记住密码功能
  • 原文地址:https://www.cnblogs.com/Tommy-Yu/p/4179040.html
Copyright © 2011-2022 走看看