用glade和pygtk模板化溢出的内容? [英] Templating overflowing content with glade and pygtk?

查看:78
本文介绍了用glade和pygtk模板化溢出的内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试开发一个多轨"GUI(类似于多轨音频编辑器);但是,我想首先在 glade 中开发它,并检查溢出的内容(在这种情况下,多个轨道")将如何与 scollbars 一起工作.然后,在 Python 中实例化时,我首先想将这些多条轨道"中的第一个作为模板",然后删除所有这些多条轨道"——然后允许用户根据模板"添加新的",例如,单击添加"按钮.

I'm trying to develop a "multitrack" GUI (similar to multitrack audio editors); however, I'd like to develop it in glade first, and check how overflowing content (in this case, multiple "tracks") will behave with scollbars. Then, upon instantiation in Python, I'd first like to take the first of these "multiple tracks" as a "template", then delete all these multiple "tracks" - then allow the user to add new ones based on the "template" by, say, clicking an "Add" button.

从 Gtk 调色板来看,在我看来 handlebox 是用作轨道"基础的正确对象(我最终想在这些轨道中绘制).到目前为止,我唯一设法解决的问题(考虑到关于 glade UI 使用的教程很少),就是让滚动条在 GUI 中运行 - 这是滚动窗口部分的屏幕截图仅(对应文件如下):

From the Gtk palette, it seems to me that handlebox is the right object to use as a base for a "track" (I'd want to draw in these tracks eventually). The only thing I managed to get through so far (given how few tutorials can be found on glade UI usage), is to get the scrollbars to behave within the GUI - here's a screenshot of the scrolled window section only (corresponding file is below):

正确的结构似乎是:

scrolled window
  viewport
    vbox
      handlebox 
        drawingarea
      handlebox ...

... 我所要做的就是将(所有)handlebox 的高度请求"设置为 150px(我想要一个恒定的高度,并根据窗口缩放宽度);并将其包装/展开设置为否".此外,将 scrolledwindow 水平和垂直滚动条策略设置为始终" - 否则不会显示滚动条(否则我错误地尝试放置额外的滚动条以查看它).最后,要使滚动条工作,请准确单击其箭头 - 在 Glade 中拖动滚动条不起作用(至少在我使用的 Ubuntu 11.04 上的 Glade3 3.8.0 上不起作用).

... and all I have to do is set the "Height request" of (all) handlebox to 150px (I want a constant height, and width scaling according to window); and set its Packing/Expand to "No". Also, set the scrolledwindow Horizontal and Vertical Scrollbar Policy to "Always" - otherwise the scrollbars are not shown (and I was otherwise wrongly trying to place an additional scrollbar to get to see it). Finally, to get the scrollbar to work, click exactly on its arrowheads - dragging the scroll bar doesn't work from within Glade (at least not on glade3 3.8.0 on Ubuntu 11.04 I use).

到目前为止一切顺利 - 至少我可以在 glade 中看到溢出的内容表现得如我所愿,但是:

So far so good - at least I can see the overflowing content behave as I want in glade, but:

  • 这是要使用的正确 glade UI 结构吗?我看到了一个 Layout 对象和一个 Frame 对象——这些可能更适合这里吗?(尝试过,无法真正弄清楚)
  • 在 Python 中读取 .glade 文件后,如何继续从 handlebox1 中提取"模板,并按需复制它?
  • 我是否还需要在添加/删除曲目时更改 vbox 的分区?如果是这样,有没有办法在不使用 vbox 的情况下实现与上述相同的布局来添加/删除轨道?
  • 目前我对轨道宽度随窗口宽度缩放感到满意;但是我是否应该决定让它们固定宽度大于窗口宽度,我尝试将手柄框的宽度请求设置为 1000,并且水平滚动条似乎在 Glade 中正常工作;宽度请求就是它的全部吗?
  • 如果我想让用户通过拖动重新排列垂直轨道顺序,是否需要特殊处理程序?
  • Is this the right glade UI structure to use? I see a Layout object, and a Frame object too - would those maybe be more appropriate here? (tried them, couldn't really figure them out)
  • Once the .glade file is read in Python, how to a proceed in "extracting" a template from handlebox1, and duplicating it on demand?
  • Would I also have to change the partitioning of the vbox upon add/delete of a track? If so, is there a way to achieve the same layout as above for adding/deleting tracks, without using a vbox?
  • Currently I'm happy with width of the tracks scaling with width of the window; but should I decide I want them fixed width bigger than width of the window, I tried setting Width Request of the handlebox to say 1000, and horizontal scrollbar seems to work properly in Glade; would Width Request be all that there is to it?
  • Are special handlers needed if I want to let the user rearrange vertical track order by dragging?

还有一个附带问题 - 有没有办法直接从 Glade(只是在一个空窗口"中)快速预览"一个 Glade 对象,而无需编写实例化脚本 - 也许通过使用一些快捷方式?

And a side question - is there a way to quickly "preview" a Glade object directly from Glade (just in an "empty window"), without writing an instantiation script - maybe by using some shortcut?

这是multitrack.glade的代码(在GtkBuilder中):

Here's the code of multitrack.glade (in GtkBuilder):

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="2.24"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkScrolledWindow" id="scrolledwindow1">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <child>
          <object class="GtkViewport" id="viewport1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkVBox" id="vbox1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <object class="GtkHandleBox" id="handlebox1">
                    <property name="height_request">150</property>
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkDrawingArea" id="drawingarea1">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                      </object>
                    </child>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkHandleBox" id="handlebox2">
                    <property name="height_request">150</property>
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <placeholder/>
                    </child>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">1</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkHandleBox" id="handlebox3">
                    <property name="height_request">150</property>
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <placeholder/>
                    </child>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">2</property>
                  </packing>
                </child>
              </object>
            </child>
          </object>
        </child>
      </object>
    </child>
  </object>
</interface>

推荐答案

老兄,这真是……好吧,要以编程方式进行适当的模板化,您需要递归复制 Gtk 对象,这些对象不易受到深度复制的影响... 所以我写了一个这样的函数,deep_clone_widget,包含在下面的源代码中;供参考:

Boy, this was something... well, to do a proper templating programaticaly, you'd need to recursively copy Gtk Objects, which are not susceptible to deepcopy... So I wrote one such function, deep_clone_widget, included in the source below; for reference:

当然,没有经过广泛测试,但似乎对我有用.有趣的是,直到对手柄框和绘图区域进行完整的深度克隆",手柄框才不会拉伸以适应窗口的宽度!

Of course, not extensively tested, but seems to work for me. Interestengly, until one does a full "deep clone" of handlebox and drawing area, the handleboxes do not stretch to fit the width of the window!

好消息是 - 可以添加到 Vbox,无需管理;但似乎拖动行为将是一个挑战...但我仍然想知道这是否是要使用的正确 Gtk/Glade UI 层次结构(以及是否有从 Glade 预览的快捷方式)

Good thing is - can just add to Vbox, no need to manage it; but it seems the dragging behavior will be a challenge... But I'd still like to know if this is the right Gtk/Glade UI hierarchy to use (and if there is a shortcut to preview from Glade)

下面的代码也会输出起始层次结构:

The code below will also output the starting hierarchy:

window1 :: GtkWindow
-scrolledwindow1 :: GtkScrolledWindow
--viewport1 :: GtkViewport
---vbox1 :: GtkVBox
----handlebox1 :: GtkHandleBox
-----drawingarea1 :: GtkDrawingArea
----handlebox2 :: GtkHandleBox
----handlebox3 :: GtkHandleBox

... 和结束层次结构:

... and the ending hierarchy:

window1 :: GtkWindow
-scrolledwindow1 :: GtkScrolledWindow
--viewport1 :: GtkViewport
---vbox1 :: GtkVBox
----hb1_template :: GtkHandleBox
-----drawingarea1 :: GtkDrawingArea
----hb1_template :: GtkHandleBox
-----drawingarea1 :: GtkDrawingArea
----hb1_template :: GtkHandleBox
-----drawingarea1 :: GtkDrawingArea
----hb1_template :: GtkHandleBox
-----drawingarea1 :: GtkDrawingArea

...希望确认深度克隆代码没问题.

... hopefully confirming that the deep clone code is ok.

这是使用上面的 multitrack.glade 的代码 multitrack.py:

Here's the code multitrack.py, that uses the multitrack.glade above:

# needs gtk-builder (not for libglade)
import pygtk
pygtk.require("2.0")
import gtk
import copy
from pprint import pprint
import inspect


def main():
  global window
  gladefile = "/tmp/multitrack.glade"
  wTree = gtk.Builder()
  wTree.add_from_file(gladefile)
  window = wTree.get_object("window1")
  if not(window): return

  print "E: " + str( get_descendant(window, "nofind", level=0, doPrint=True) )
  doCopy()
  print "E: " + str( get_descendant(window, "nofind", level=0, doPrint=True) )

  window.connect("destroy", gtk.main_quit)
  window.set_size_request(600, 300)
  window.show_all() # must have!
  gtk.main()


def doCopy():
  global window
  # get template object
  hb1_ref = get_descendant(window, "handlebox1", level=0, doPrint=False)
  #hb1_template = copy.deepcopy(hb1_ref) # GObject non-copyable
  hb1_template = deep_clone_widget(hb1_ref)
  gtk.Buildable.set_name(hb1_template, "hb1_template")
  # get the container to be cleared
  vb1 = get_descendant(window, "vbox1", level=0, doPrint=False)
  # delete pre-existing in vbox (incl. hb1_ref)
  for i in vb1.get_children():
    vb1.remove(i)
  # create new content
  hb1_a = deep_clone_widget(hb1_template)
  vb1.pack_start(hb1_a, expand=False, fill=True, padding=0)
  hb1_b = deep_clone_widget(hb1_template)
  vb1.pack_start(hb1_b, expand=False, fill=True, padding=0)
  hb1_c = deep_clone_widget(hb1_template)
  vb1.pack_start(hb1_c, expand=False, fill=True, padding=0)
  hb1_d = deep_clone_widget(hb1_template)
  vb1.pack_start(hb1_d, expand=False, fill=True, padding=0)
  if 0: #small deep_clone_test
    print ".....>"
    vb1_ref = get_descendant(window, "vbox1", level=0, doPrint=False)
    vb1_copy = deep_clone_widget(vb1_ref)
    print "EEEEE "+ str( get_descendant(vb1_copy, "nofind", level=0, doPrint=True) )
    print ".....<"


# https://stackoverflow.com/questions/20461464/how-do-i-iterate-through-all-gtk-children-in-pygtk-recursively
def get_descendant(widget, child_name, level, doPrint=False):
  if widget is not None:
    if doPrint: print("-"*level + str(gtk.Buildable.get_name(widget)) + " :: " + widget.get_name())
  else:
    if doPrint:  print("-"*level + "None")
    return None
  if(gtk.Buildable.get_name(widget) == child_name):
    return widget;
  if (hasattr(widget, 'get_child') and callable(getattr(widget, 'get_child')) and child_name != ""):
    child = widget.get_child()
    if child is not None:
      return get_descendant(child, child_name,level+1,doPrint)
  elif (hasattr(widget, 'get_children') and callable(getattr(widget, 'get_children')) and child_name !=""):
    children = widget.get_children()
    found = None
    for child in children:
      if child is not None:
        found = get_descendant(child, child_name,level+1,doPrint)
        if found: return found

def deep_clone_widget(widget, inparent=None):
  dbg = 0
  widget2 = clone_widget(widget)
  if inparent is None: inparent = widget2
  if (hasattr(widget, 'get_child') and callable(getattr(widget, 'get_child'))):
    child = widget.get_child()
    if child is not None:
      if dbg: print "A1 inp", inparent.get_name(), "w2", widget2.get_name()
      childclone = deep_clone_widget(child, widget2)
      if dbg: print "A2", childclone.get_name()
      widget2.add( childclone )
      #return inparent
  elif (hasattr(widget, 'get_children') and callable(getattr(widget, 'get_children')) ):
    children = widget.get_children()
    for child in children:
      if child is not None:
        if dbg: print "B1 inp", inparent.get_name(), "w2", widget2.get_name()
        childclone = deep_clone_widget(child, widget2)
        if dbg: print "B2", childclone.get_name()
        inparent.add( childclone )
        #return childclone
  return widget2

# https://stackoverflow.com/questions/1321655/how-to-use-the-same-widget-twice-in-pygtk
def clone_widget(widget):
  print(" > clone_widget in: " + str(gtk.Buildable.get_name(widget)) + " :: " + widget.get_name() )
  widget2=widget.__class__()
  # these must go first, else they override set_name from next stage
  for pspec in widget.props:
    if pspec.name not in ['window', 'child', 'composite-child', 'child-detached', 'parent']:
      #print("  > " + pspec.name)
      try:
        widget2.set_property(pspec.name, widget.get_property(pspec.name))
      except Exception as e:
        print e
  # here set_name is obtained
  for prop in dir(widget):
    if prop.startswith("set_") and prop not in ["set_buffer"]:
      #print("  ! " + prop + " ")
      prop_value=None
      try:
        prop_value=getattr(widget, prop.replace("set_","get_") )()
      except:
        try:
          prop_value=getattr(widget, prop.replace("set_","") )
        except:
          continue
      if prop_value == None:
        continue
      try:
        #print("  > " + prop + " " + prop_value )
        if prop != "set_parent": # else pack_start complains: assertion `child->parent == NULL' failed
          getattr(widget2, prop)( prop_value )
      except:
        pass
  gtk.Buildable.set_name(widget2, gtk.Buildable.get_name(widget))
  ## style copy:
  #for pspec in gtk.widget_class_list_style_properties(widget):
  #  print pspec, widget.style_get_property(pspec.name)
  #  #gtk.widget_class_install_style_property(widget2, pspec) #nope, for class only, not instances!
  # none of these below seem to change anything - still getting a raw X11 look after them:
  widget2.ensure_style()
  widget2.set_style(widget.get_style().copy())
  widget2.set_style(gtk.widget_get_default_style())
  widget2.modify_style(widget.get_modifier_style())
  #widget2.set_default_style(widget.get_default_style().copy()) # noexist; evt. deprecated? https://stackoverflow.com/questions/19740162/how-to-set-default-style-for-widgets-in-pygtk
  # this is the right one, so we don't get raw X11 look:
  widget2.set_style(widget.rc_get_style())
  return widget2

if __name__ == "__main__":
  main()

这篇关于用glade和pygtk模板化溢出的内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆