Opinion on new library

168 views
Skip to first unread message

Jack Burridge

unread,
Nov 15, 2016, 12:58:56 PM11/15/16
to wxPython-users
Hi Guys/Girls

I recently created a module to easily bind data to a wxpython GUI (https://github.com/jackburridge/pywatch) its far from complete but I thought I'd show it to you people for options etc.

The idea is that you have a root object that is watchable, pywatch gives you the object WatchableDict, this is meant to emulate the kind of behaviour you would see in AngularJS with $scope objects. You can then use classes within the submodule pywatch.wx to bind a property of this object to widgets within your GUI for example in the simple frame bellow:

class MyFrame(wx.Frame):
   
def __init__(self, parent):
        wx
.Frame.__init__(self, parent, title=u"My Frame")


       
self.model = WatchableDict()
       
self.model["text"] = ""


        sizer
= wx.BoxSizer(wx.VERTICAL)


       
self.text_ctrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
        sizer
.Add(self.text_ctrl, 0, 0, 0)
        pywatch
.wx.TextCtrlWatcher(self.text_ctrl,self.model,"text")


       
self.static_text = wx.StaticText(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
        sizer
.Add(self.static_text, 0, 0, 0)
        pywatch
.wx.LabelWatcher(self.static_text, self.model, ["text"])


       
self.SetSizer(sizer)


       
self.Centre(wx.BOTH)

When ever the text within text_ctrl is changed the text within static_text is automatically changed.

If anyone has any ideas on how I could improve this module, that would be great :)

John Fabiani

unread,
Nov 15, 2016, 1:54:39 PM11/15/16
to wxpython-users
have you looked at Dabo? www.dabodev.com

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jack Burridge

unread,
Nov 15, 2016, 5:21:29 PM11/15/16
to wxPython-users

I had a quick look, but before spending time learning the WHOLE frame work is it possible to do things such as this:



This has the ability to add the two spinners as they change. The source is here https://github.com/jackburridge/pywatch/blob/multiwatch/example/example06.py

If dabo can do stuff like this easily that would be great
To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-user...@googlegroups.com.

sebastián lópez

unread,
Nov 16, 2016, 9:11:37 AM11/16/16
to wxPython-users
Hello,

I'm thinking about your aproach and I guess it can be confuse to the user to use a diferent watcher depending of the control type

SpinWatcher --> SpinCtrl
LabelWatcher --> LabelCtrl
ValueWatcher --> ***Ctrl
.....
dateWatcher --> DateCtrl


So I think it could be better to provide a single watcher
Watcher ---> SpinCtrl
Watcher ---> LabelCtrl
Watcher ---> **Ctrl

Here is my aproach

Message has been deleted
Message has been deleted

sebastián lópez

unread,
Nov 16, 2016, 9:52:59 AM11/16/16
to wxPython-users
Here is the code

import wx
from easyDialog import Ctrl
class Panel(wx.Frame):
    def __init__(self, parent, id=wx.ID_ANY, *args, **kwargs):
        wx.Frame.__init__(self, parent, id, *args, **kwargs)
        self.gauge = Ctrl.Gauge(0, maxvalue=100, horz=wx.HORIZONTAL)
        self.result = Ctrl.StaticText('0')
        self.spin1 = Ctrl.SpinCtrl(0, 50, 0).\
            Bind(wx.EVT_SPINCTRL, lambda evt: self.gauge.SetValue( self.spin1.Value+self.spin2.Value)).\
            Bind(wx.EVT_SPINCTRL, lambda evt: self.result.SetValue( str( self.spin1.Value+self.spin2.Value)))
        self.spin2 = Ctrl.SpinCtrl(0, 50, 0).\
            Bind(wx.EVT_SPINCTRL, lambda evt: self.gauge.SetValue( self.spin1.Value+self.spin2.Value)).\
            Bind(wx.EVT_SPINCTRL, lambda evt: self.result.SetValue( str( self.spin1.Value+self.spin2.Value)))

        # ----- > The layout
        mp = Ctrl.MakePairs
        sizer= mp([
                   (self.spin1, Ctrl.StaticText( '+'), self.spin2),  # first controls row
                   mp( [self.result], horzCenter=True, proportion=1), # second controls row
                   self.gauge, # third controls row
                  ], # The controls as list or tuple
                  horz=wx.VERTICAL, # the Layout
                  title='Hello', # an staticboxsizer with a string
                  flag=wx.ALL|wx.EXPAND, # control's flag
                  horzCenter=True) # to horizontally center the sizer container
        # ----- <
        self.SetSizer( sizer( self)) # sizer(self) do the stuff, i.e. the callbacks, the Layout, ....
        self.Layout()

Chris Barker - NOAA Federal

unread,
Nov 16, 2016, 10:44:43 AM11/16/16
to wxpytho...@googlegroups.com
You may want to take a look at Enthought Tool Suite traits and traitsUI:


I think they have been focused on QT for the UI recently, but the underlying traits system is pretty powerful.

-CHB

Sent from my iPhone

Jack Burridge

unread,
Nov 16, 2016, 12:39:58 PM11/16/16
to wxPython-users
Sebastián,
Would it be possible to see your full code? Also the method you suggest is not thread safe, so the data could not be modified outside of the event loop

sebastian lópez

unread,
Nov 16, 2016, 4:16:08 PM11/16/16
to wxPython-users

Yes, I attached the code,

Actually it's tested under python 2.7 and wxpython 3.0
example.zip

sebastian lópez

unread,
Nov 16, 2016, 9:31:09 PM11/16/16
to wxPython-users
It's possible to rewrite the code into this

import wx
from easyDialog import Ctrl
class Panel(wx.Frame):
    def __init__(self, parent, id=wx.ID_ANY, *args, **kwargs):
        wx.Frame.__init__(self, parent, id, *args, **kwargs)
        self.gauge = Ctrl.Gauge(0, maxvalue=100, horz=wx.HORIZONTAL)
        self.result = Ctrl.StaticText('0')
        self.spin1 = Ctrl.SpinCtrl(0, 50, 0).Bind(wx.EVT_SPINCTRL, self.On_change)
        self.spin2 = Ctrl.SpinCtrl(0, 50, 0).Bind(wx.EVT_SPINCTRL, self.On_change)

        # ----- > The layout
        mp = Ctrl.MakePairs
        sizer= mp([
                   (self.spin1, Ctrl.StaticText( '+'), self.spin2),
                   mp( [self.result], horzCenter=True, proportion=1),
                   self.gauge,
                  ],
                  horz=wx.VERTICAL,
                  title='Hello',
                  flag=wx.ALL|wx.EXPAND,
                  horzCenter=True)
        # ----- <
        self.SetSizer( sizer( self))
        self.Layout()
    def On_change(self, evt):
              self.gauge.SetValue( self.spin1.Value + self.spin2.Value)
              self.result.SetValue( str( self.spin1.Value + self.spin2.Value))


In case you want to make it threat safe you should customize the callback.


El miércoles, 16 de noviembre de 2016, 12:39:58 (UTC-5), Jack Burridge escribió:

Michael Salin

unread,
Nov 16, 2016, 9:52:43 PM11/16/16
to wxPython-users

I think you have to exploit wx.propgrid as a GUI part in your SDK. It helps to display a lot of data in a column and it looks nice.

sebastian lópez

unread,
Nov 17, 2016, 5:40:05 AM11/17/16
to wxPython-users
I did it, but I want to have an oppinion about the library in order to know if it is good enough to be uploaded. Also it would be nice to combined both projects.

Jack Burridge

unread,
Nov 18, 2016, 6:43:39 AM11/18/16
to wxPython-users
I definitely like your opinion on a single type of watcher but I would do it for things like enable, value, selection, etc

Jack Burridge

unread,
Nov 18, 2016, 6:49:41 AM11/18/16
to wxPython-users
Yeah my idea was to move onto that, but two little questions
1. How would you sort the dict, i was thinking a key would be nice
2. would you base the type on the current type, for example in the dict
{"text":"string","number":0,"bool":True}
and infer it or:
{"text":{"type":str,"value":"string"},"number":{"type":int,"min":0,"max":10},"bool":{"type":bool,"value":True}}
Message has been deleted

sebastian lópez

unread,
Nov 18, 2016, 8:36:38 AM11/18/16
to wxPython-users
Hello. When I was developing the easyDialog library I attemp to create a fubctuion that identify the control type and and then use an appropiated method, but when you added a new control or a custom control then you have to update It. So I preffer to be moved into a new type of CtrlBase that had a generic property Value, in the case you have a textCtrl the Value return a string in the other hand if you used a listCtrl the Value method return a string with the selected ítem and so on.

Jack Burridge

unread,
Nov 18, 2016, 12:24:46 PM11/18/16
to wxPython-users
Ahh! Something like this:

value_events = {
    wx.SpinCtrl: wx.EVT_SPIN,
    wx.Slider: wx.EVT_SLIDER,
    wx.CheckBox: wx.EVT_CHECKBOX,
    wx.ToggleButton: wx.EVT_TOGGLEBUTTON,
    wx.TextCtrl: wx.EVT_TEXT
}


class ValueChanger(ValueWatcher):
    def __init__(self, widget, watchable, watcher):
        event = [value_events[widget_class] for widget_class in value_events if isinstance(widget,widget_class)]
        if not event:
            raise Exception('Widget type is not recognised')
        ValueWatcher.__init__(self, widget, watchable, watcher)
        widget.Bind(event[0], self.on_change)

    def on_change(self, event):
        self.set_value(self.widget.GetValue())
        event.Skip()

sebastian lópez

unread,
Nov 21, 2016, 10:04:14 PM11/21/16
to wxPython-users
It looks better

Now I'm finish to make some fixes to share my code.

About that i would  like to share you the code I used to create this GUI

from easyDialog import Ctrl
from Modulos.coreMod import _BasePanel
from imagenes import imageEmbed
import wx
_ = wx.GetTranslation
imag= imageEmbed(size=(16, 16))
class Panel(_BasePanel):
    name = _('FOR-MAN-001')# formato de mantenimiento
    image = imag.dashboard
    def __init__(self, parent, id = wx.ID_ANY, *args, **kwargs):
        _BasePanel.__init__(self, parent, id, *args, **kwargs)
        mp, st, tc, bm = Ctrl.MakePairs, Ctrl.StaticText, Ctrl.TextCtrl, Ctrl.BitmapButton
        sb, spa, flex, grid = Ctrl.StaticBitmap, Ctrl.Spacer, Ctrl.FlexGridSizer, Ctrl.GridSizer
        imag = imageEmbed(size=(590, 80))
        header = mp([sb(imag.formulario),
                    mp([st('Especificaciones')], horzCenter=True),
                    ],
                    horz=wx.VERTICAL,
                    flag=wx.EXPAND|wx.ALL)
        flexi = flex([st('Año del modelo'), st('Odómetro'),
                     Ctrl.IntTextCtrl('2016').tooltip(_('Año de fabricación')), 
                     mp([tc(size=(125,-1)).tooltip(_('Valor inicial\ndel odómetro')),
                         Ctrl.Choice(['Kilometros','Millas'],0)],
                        border=0),
                     st('Fabricante'), st('Placa'),
                     tc('').tooltip(_('Del vehículo')),
                     tc('', size=wx.Size(210, -1)).tooltip(_('Número de la\nplaca del automotor')),
                     st('Modelo'), st('Código'),
                     tc('').tooltip(_('Modelo acorde\ncon el fabricante')),
                     tc('', size=wx.Size(210, -1)).tooltip(_('Que se le asignará\nen la empresa para\nidentificarlo')),
                     st('Color'), st('Conductor'),
                     tc('Negro').tooltip(_('Color actual')),
                     Ctrl.User().tooltip(_('Seleccione un usuario\nRegistrado en la base')),
                     ], 2)
        body_left = mp([flexi],
                  proportion=0,
                  horzCenter=True,
                  title= _('Información del vehículo'),
                  flag=wx.ALL,
                  )
        imag = imageEmbed(size=(200, 200))
        body_rigth = mp([sb(imag.equipo)], title=_('Imágen del vehículo'), flag=wx.ALL|wx.EXPAND)
        body = mp([body_left,  body_rigth], flag=wx.ALL|wx.EXPAND, horzCenter=True)
        
        body_midle_top = mp([flex([st(_('Numero de Tarjeta')),
                                 tc(size=wx.Size(117,-1)).tooltip(_('Tarjete de operación')),
                                 st(_('Fecha de vencimiento')), Ctrl.DatePickerCtrl(size=wx.Size(117, -1)),
                                 ],2)],
                            horz=wx.VERTICAL, flag=wx.EXPAND, title=_('Licencia'))
        body_midle_bottom = mp([flex([st(_('Empresa/compañía')),
                                     tc(size=wx.Size(125,-1)).tooltip(_('Empresa que aseguró\nel vehículo')),
                                    st(_('Número de\nla cuenta')),
                                    tc(size=wx.Size(125,-1)).tooltip(_('O el número\ndel seguro')),
                                    st(_('Fecha Vencimiento')),
                                    Ctrl.DatePickerCtrl(size=wx.Size(125,-1)).\
                                        tooltip(_('Fecha de vencimiento\ndel seguro')),
                                    ],2)
                                ],
                                title=_('Aseguradora'))
        body_midle_left = mp([body_midle_top, body_midle_bottom], horz=wx.VERTICAL, flag=wx.EXPAND)
        
        body_mid_rigtop = mp([flex([st(_('Motor')),
                                   tc(size=wx.Size(205,-1)).tooltip(_('cm cúbicos\nnúmero de válvulas')),
                                   st(_('Transmisión')),
                                   Ctrl.Choice(['Mecánica', 'Automática'],0, size=wx.Size(205,-1)).\
                                        tooltip(_('tipo de transmisión\ndel vehículo')),
                                   st(_('Tamaño de\nla llanta')),
                                   tc(size=wx.Size(205,-1)),
                                   ],2)
                             ],
                             title=_('Mecanica'))
        body_mid_rigbottom = mp([tc(style=wx.TE_MULTILINE,size=wx.Size(290, 56)).\
                                   tooltip(_('Observaciones con respecto\nal vehículo'))],
                                flag=wx.EXPAND|wx.ALL, title=_('Observaciones'))
        body_midle_rigth = mp([body_mid_rigtop, body_mid_rigbottom], horz=wx.VERTICAL, flag=wx.EXPAND)
        body_midle = mp([body_midle_left, body_midle_rigth], flag=wx.ALL, horzCenter=True)
        main_sizer = mp([header, body, body_midle],
                  title=_('Datos básicos'),
                  horzCenter=True,
                  horz=wx.VERTICAL)
        self.Sizer= main_sizer
pnl= Panel(ntb)
ntb.AddPage(pnl, pnl.name, True)
wx.MessageBox('{}'.format(pnl.Value))

I hope It's not too complicated, but you have to consider to obtain Its values you just have to use the command

>>> pnl.Value

In the other hand you can also use it to set the GUI values.

>>> pnl.Value = ['some list of values',dateObject,'',...,]


It would be nice to hear an expert opinion.

Jack Burridge

unread,
Nov 24, 2016, 7:48:25 AM11/24/16
to wxPython-users
It doesn't look to complicated but it is inherently hard to read
Message has been deleted

sebastian lópez

unread,
Nov 25, 2016, 9:28:28 AM11/25/16
to wxPython-users
 I agree with you.

So to reduce the frustation about the aproach I decided to rename the Ctrl to wxs and the MakePairs to Sizer
Also I decided to include the translation method or _ = wx.GetTranslation into the control definitions by default so

Ctrl.MakePairs becomes into wxs.Sizer  and

wxs.StaticText(_('Some text')) can be replace by using  wxs.StaticText('Some Text')

Finally, you still can use wx control inside the wxs.Sizer container.

Next tomorrow I will Upload the libraries easyDialog and imagenes.
from easyDialog import wxs
import wx
from Modulos.coreMod import _BasePanel
from imagenes import imageEmbed
imag= imageEmbed(size=(16, 16))
class Panel(_BasePanel):
    name = 'FOR-MAN-001'# formato de mantenimiento
    image = imag.dashboard
    def __init__(self, parent, id = wx.ID_ANY, *args, **kwargs):
        _BasePanel.__init__(self, parent, id, *args, **kwargs)
        st, tc, bm = wxs.StaticText, wxs.TextCtrl, wxs.BitmapButton
        sb, spa, flex = wxs.StaticBitmap, wxs.Spacer, wxs.FlexGridSizer, wxs.GridSizer
        imag = imageEmbed(size=(590, 80))
        header = wxs.Sizer([sb(imag.formulario),
                           wxs.Sizer(st('Especificaciones'), horzCenter=True)],
                           horz=wx.VERTICAL,
                           flag=wx.EXPAND|wx.ALL)
        flexi = flex([st('Año del modelo'),
                     st('Odómetro'),
                     wxs.IntTextCtrl('2016').tooltip('Año de fabricación'),
                     wxs.Sizer([tc(size=(125,-1)).tooltip('Valor inicial\ndel odómetro'),
                               wxs.Choice(['Kilometros','Millas'],0)],
                               border=0),
                     st('Fabricante'),
                     st('Placa'),
                     tc('').tooltip('Del vehículo'),
                     tc('', size=wx.Size(210, -1)).tooltip('Número de la\nplaca del automotor'),
                     st('Modelo'),
                     st('Código'),
                     tc('').tooltip('Modelo acorde\ncon el fabricante'),
                     tc('', size=wx.Size(210, -1)).tooltip('Que se le asignará\nen la empresa para\nidentificarlo'),
                     st('Color'),
                     st('Conductor'),
                     tc('Negro').tooltip('Color actual'),
                     wxs.User().tooltip('Seleccione un usuario\nRegistrado en la base'),
                     ], 2)
        b_left = wxs.Sizer(flexi,
                          proportion=0,
                          horzCenter=True,
                          title='Información del vehículo',
                          flag=wx.ALL)
        imag = imageEmbed(size=(200, 200))
        b_rigth = wxs.Sizer(sb(imag.equipo),
                           title='Imágen del vehículo',
                           flag=wx.ALL|wx.EXPAND)
        body = wxs.Sizer([b_left,  b_rigth],
                         flag=wx.ALL|wx.EXPAND,
                         horzCenter=True)
        bm_top = wxs.Sizer(flex([st('Numero de Tarjeta'),
                               tc(size=wx.Size(117,-1)).tooltip('Tarjete de operación'),
                               st('Fecha de vencimiento'), 
                               wxs.DatePickerCtrl(size=wx.Size(117, -1)),
                               ], 2),
                               horz=wx.VERTICAL,
                               flag=wx.EXPAND,
                               title='Licencia')
        bm_bottom = wxs.Sizer(flex([st('Empresa/compañía'),
                                  tc(size=wx.Size(125,-1)).tooltip('Empresa que aseguró\nel vehículo'),
                                  st('Número de\nla cuenta'),
                                  tc(size=wx.Size(125,-1)).tooltip('O el número\ndel seguro'),
                                  st('Fecha Vencimiento'),
                                  wxs.DatePickerCtrl(size=wx.Size(125,-1)).tooltip('Fecha de vencimiento\ndel seguro'),
                                  ], 2),
                              title='Aseguradora')
        bm_left = wxs.Sizer([bm_top, bm_bottom],
                            horz=wx.VERTICAL,
                            flag=wx.EXPAND)
        bm_rigtop = wxs.Sizer(flex([st('Motor'),
                                   tc(size=wx.Size(205,-1)).tooltip('cm cúbicos\nnúmero de válvulas'),
                                   st('Transmisión'),
                                   wxs.Choice(['Mecánica', 'Automática'],0, size=wx.Size(205,-1)).tooltip('tipo de transmisión\ndel vehículo'),
                                   st('Tamaño de\nla llanta'),
                                   tc(size=wx.Size(205,-1)),
                                   ], 2),
                                title='Mecanica')
        bm_rigbottom = wxs.Sizer(tc(style=wx.TE_MULTILINE,size=wx.Size(290, 56)).tooltip('Observaciones con respecto\nal vehículo'),
                                flag=wx.EXPAND|wx.ALL,
                                title='Observaciones')
        bm_rigth = wxs.Sizer([bm_rigtop, bm_rigbottom],
                             horz=wx.VERTICAL,
                             flag=wx.EXPAND)
        body_midle = wxs.Sizer([bm_left, bm_rigth],
                                flag=wx.ALL,
                                horzCenter=True)
        main_sizer = wxs.Sizer([header, body, body_midle],
                               title='Datos básicos',
                               horzCenter=True,
                               horz=wx.VERTICAL)
        self.Sizer = main_sizer
pnl= Panel(ntb)
ntb.AddPage(pnl, pnl.name, True)
wx.MessageBox('{}'.format(pnl.Value))

sebastian lópez

unread,
Nov 25, 2016, 11:57:08 AM11/25/16
to wxPython-users
Finally and to avoid becoming into spam it's just missing to connect your library with this one, and to define the license type, I think It could be GPL or wxWindows Library Licence

My mail: selobu at gmail dot com
Reply all
Reply to author
Forward
0 new messages