wxPython:“超级” wx.SpinCtrl 具有浮点值,布局在 sizer 内

发布于 2024-09-10 02:56:18 字数 2086 浏览 2 评论 0原文

wx.SpinCtrl 仅限于在整数上旋转,而不是在浮点数上旋转。因此,我正在构建一个 wx.TextCtrl + wx.SpinButton 组合类,它使我能够在浮动之间旋转。我能够以编程方式调整它们的大小和布局,以便组合看起来与普通的 wx.SpinCtrl 完全相同。

我从 wx.TextCtrl 继承此组合,因为我希望其父面板捕获 wx.EVT_TEXT 事件。如果您能改进我的这个论点,我将不胜感激。

wx.SpinButton 中的 wx.EVT_SPIN_UPwx.EVT_SPIN_DOWN 事件都是内部实现,父框架不关心这些事件。

现在,我只是碰壁了。我的组合类不能很好地与 sizers 配合使用。在 .Add() 将组合类添加到 wx.GridBagSizer 后,只有 wx.TextCtrl 被放置在 wx 中.GridBagSizerwx.SpinButton 独自留下。不过,wx.EVT_SPIN* 绑定工作得很好。

我的问题是布局。如果我希望 wx.GridBagSizer 将其视为一个小部件,我应该如何编写该类?

这是我的组合类代码:

class SpinnerSuper(wx.TextCtrl):
  def __init__(self, parent, max):
    wx.TextCtrl.__init__(self, parent=parent, size=(48, -1))
    spin = wx.SpinButton(parent=parent, style=wx.SP_VERTICAL, size=(-1, 21))
    self.OnInit()
    self.layout(spin)
    self.internalBindings(spin)
    self.SizerFlag = wx.ALIGN_CENTER

    self.min = 0
    self.max = max

  def OnInit(self):
    self.SetValue(u"0.000")

  def layout(self, spin):
    pos = self.GetPosition()
    size = self.GetSize()
    RightEdge = pos[0] + size[0]
    TopEdge = pos[1] - (spin.GetSize()[1]/2 - size[1]/2)
    spin.SetPosition((RightEdge, TopEdge))

  def internalBindings(self, spin):
    spin.Bind(wx.EVT_SPIN_UP, self.handlerSpinUp(self), spin)
    spin.Bind(wx.EVT_SPIN_DOWN, self.handlerSpinDown(self), spin)

  def handlerSpinUp(CallerObject, *args):
    def handler(CallerObject, *data):
        text = data[0]
        prev = text.GetValue()
        next = float(prev) + 0.008
        text.SetValue("{0:0.3f}".format(next))
    return lambda event: handler(CallerObject, *args)

  def handlerSpinDown(CallerObject, *args):
    def handler(CallerObject, *data):
        text = data[0]
        prev = text.GetValue()
        next = float(prev) - 0.008
        text.SetValue("{0:0.3f}".format(next))
    return lambda event: handler(CallerObject, *args)

wx.SpinCtrl is limited to spinning across integers, and not floats. Therefore, I am building a wx.TextCtrl + wx.SpinButton combo class which enables me to spin across floats. I am able to size and layout both of them programmatically so that the combo looks exactly the same as an ordinary wx.SpinCtrl.

I am subclassing this combo from the wx.TextCtrl because I want its parent panel to catch wx.EVT_TEXT events. I would appreciate if you can improve on this argument of mine.

The wx.EVT_SPIN_UP and wx.EVT_SPIN_DOWN events from the wx.SpinButton are both internal implementations and the parent frame doesn't care about these events.

Now, I just hit a brick wall. My combo class doesn't work well with sizers. After .Add()ing the combo class to a wx.GridBagSizer, only the wx.TextCtrl is laid out within the wx.GridBagSizer. The wx.SpinButton is left on its own by itself. The wx.EVT_SPIN* bindings work very well, though.

My problem is the layout. How should I write the class if I want the wx.GridBagSizer to treat it as one widget?

Here is my combo class code:

class SpinnerSuper(wx.TextCtrl):
  def __init__(self, parent, max):
    wx.TextCtrl.__init__(self, parent=parent, size=(48, -1))
    spin = wx.SpinButton(parent=parent, style=wx.SP_VERTICAL, size=(-1, 21))
    self.OnInit()
    self.layout(spin)
    self.internalBindings(spin)
    self.SizerFlag = wx.ALIGN_CENTER

    self.min = 0
    self.max = max

  def OnInit(self):
    self.SetValue(u"0.000")

  def layout(self, spin):
    pos = self.GetPosition()
    size = self.GetSize()
    RightEdge = pos[0] + size[0]
    TopEdge = pos[1] - (spin.GetSize()[1]/2 - size[1]/2)
    spin.SetPosition((RightEdge, TopEdge))

  def internalBindings(self, spin):
    spin.Bind(wx.EVT_SPIN_UP, self.handlerSpinUp(self), spin)
    spin.Bind(wx.EVT_SPIN_DOWN, self.handlerSpinDown(self), spin)

  def handlerSpinUp(CallerObject, *args):
    def handler(CallerObject, *data):
        text = data[0]
        prev = text.GetValue()
        next = float(prev) + 0.008
        text.SetValue("{0:0.3f}".format(next))
    return lambda event: handler(CallerObject, *args)

  def handlerSpinDown(CallerObject, *args):
    def handler(CallerObject, *data):
        text = data[0]
        prev = text.GetValue()
        next = float(prev) - 0.008
        text.SetValue("{0:0.3f}".format(next))
    return lambda event: handler(CallerObject, *args)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

來不及說愛妳 2024-09-17 02:56:19

如果您希望控件成为
由尺寸确定者正确管理。查看创建自定义控件

您还可以查看 wxPython 附带的 FloatSpin
(在 wx.lib.agw 中)从版本 2.8.9.2 开始。

回应您的评论:

  • 实现 DoGetBestSize() 不需要直接绘制位图。您只需要找到一种方法,如何确定新小部件的最佳尺寸。通常你只需使用
    它所组成的两个小部件的大小(文本+微调器)作为基础。
  • 要让 sizer 将两个小部件视为一个小部件,您可以将它们放置在另一个 sizer 中。
  • 使用 wxPython 实现自定义小部件的推荐方法是从 wx.PyControl 派生新小部件,向其中添加一个 sizer,然后将要组合的两个小部件添加到该 sizer。

You need to override DoGetBestSize() if you want your control to be
correctly managed by sizers. Have a look at CreatingCustomControls.

You could also have a look at FloatSpin that ships with wxPython
(in wx.lib.agw) from version 2.8.9.2 upwards.

In response to your comments:

  • Implementing DoGetBestSize() does not require drawing bitmaps directly. You just need to find a way, how you can determine the best size of your new widget. Typically you'd just use
    the sizes of the two widgets it is composed of (text + spinner) as basis.
  • To let sizers treat two widgets as one, you can place them in another sizer.
  • The recommended way to implement a custom widget with wxPython is to derive your new widget from wx.PyControl, add a sizer to it and add the two widgets you want to combine to that sizer.
飞烟轻若梦 2024-09-17 02:56:19

正如 Kit 评论中提到的,FloatSpin 是现在的最佳选择。

它已集成在最近的版本中。

这是一个简单的用法示例:

import wx
from wx.lib.agw.floatspin import FloatSpin

class Example_FloatSpin(wx.Frame):
    def __init__(self, parent, title):
        super(Example_FloatSpin, self).__init__(parent, title=title, size=(480, 250))
        panel = wx.Panel(self)

        vbox = wx.BoxSizer(wx.VERTICAL)
        spin = FloatSpin(panel, value=0.0, min_val=0.0, max_val=8.0, increment=0.5, digits=2, size=(100,-1))
        vbox.Add(spin, proportion=0, flag=wx.CENTER, border=15)
        panel.SetSizer(vbox)

        self.Centre()
        self.Show() 


if __name__ == '__main__':
    app = wx.App()
    Example_FloatSpin(None, title='Check FloatSpin')
    app.MainLoop()

As mentionned in Kit's comments, FloatSpin is now the way to go.

It has been integrated in recent versions.

Here is a simple example of usage:

import wx
from wx.lib.agw.floatspin import FloatSpin

class Example_FloatSpin(wx.Frame):
    def __init__(self, parent, title):
        super(Example_FloatSpin, self).__init__(parent, title=title, size=(480, 250))
        panel = wx.Panel(self)

        vbox = wx.BoxSizer(wx.VERTICAL)
        spin = FloatSpin(panel, value=0.0, min_val=0.0, max_val=8.0, increment=0.5, digits=2, size=(100,-1))
        vbox.Add(spin, proportion=0, flag=wx.CENTER, border=15)
        panel.SetSizer(vbox)

        self.Centre()
        self.Show() 


if __name__ == '__main__':
    app = wx.App()
    Example_FloatSpin(None, title='Check FloatSpin')
    app.MainLoop()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文