简体   繁体   English

使用Glade和pygtk处理溢出的内容?

[英]Templating overflowing content with glade and pygtk?

I'm trying to develop a "multitrack" GUI (similar to multitrack audio editors); 我正在尝试开发“多轨” GUI(类似于多轨音频编辑器); however, I'd like to develop it in glade first, and check how overflowing content (in this case, multiple "tracks") will behave with scollbars. 不过,我想开发它在glade首先,检查内容如何四溢(在这种情况下,多个“轨道”)将与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. 然后,在Python中实例化时,我首先要将这些“多个轨道”中的第一个作为“模板”,然后删除所有这些多个“轨道”-然后允许用户基于“模板”添加新的例如,点击“添加”按钮。

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). 从Gtk调色板中,在我看来, handlebox是用作“轨道”基础的正确对象(我想最终绘制这些轨道)。 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): 到目前为止,我设法解决的唯一一件事(考虑到在glade UI的用法上找不到多少教程)是使滚动条在GUI中起作用-这是仅滚动窗口部分的屏幕截图(下面是相应的文件) :

multitrack.glade.png

The right structure seems to be: 正确的结构似乎是:

scrolled window
  viewport
    vbox
      handlebox 
        drawingarea
      handlebox ...

... 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); ...,而我要做的就是将(所有) handlebox的“高度请求”设置为150px(我想要一个恒定的高度,并根据窗口缩放宽度); 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). 另外,将scrolledwindow水平和垂直滚动条策略设置为“始终”-否则,滚动条将不会显示(否则我错误地尝试放置其他滚动条以查看它)。 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内部拖动滚动条不起作用(至少在我使用的Ubuntu 11.04上的glade3 3.8.0上无效)。

So far so good - at least I can see the overflowing content behave as I want in glade , but: 到目前为止,一切都很好-至少我可以看到林间glade内容表现出我想要的glade ,但是:

  • Is this the right glade UI structure to use? 这是使用的正确的glade UI结构吗? I see a Layout object, and a Frame object too - would those maybe be more appropriate here? 我看到了一个Layout对象和一个Frame对象-这些在这里是否更合适? (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? 一旦.glade文件在Python阅读,如何将在“提取”从模板进行handlebox1 ,并复制它的需求呢?
  • Would I also have to change the partitioning of the vbox upon add/delete of a track? 添加或删除曲目后,是否还需要更改vbox的分区? If so, is there a way to achieve the same layout as above for adding/deleting tracks, without using a vbox? 如果是这样,是否有一种方法可以在不使用vb​​ox的情况下实现与上述相同的布局以添加/删除曲目?
  • 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; 但是,如果我决定让它们的固定宽度大于窗口的宽度,则尝试将手柄箱的“宽度请求”设置为1000,水平滚动条在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? 如果我想让用户通过拖动重新排列垂直轨道顺序,是否需要特殊的处理程序?

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? 还有一个附带的问题-是否有一种方法可以直接从Glade快速“预览” Glade对象(仅在“空窗口”中),而无需编写实例化脚本-也许可以使用一些快捷方式?

Here's the code of multitrack.glade (in GtkBuilder): 这是multitrack.glade的代码(在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>

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; 伙计,这有点...好吧,要进行适当的模板编程,您需要递归地复制不易进行deep_clone_widget复制的Gtk对象...所以我写了一个这样的函数deep_clone_widget ,包含在下面的源代码中; 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! 有趣的是,直到人们对把手箱和绘图区域进行了完整的“深度克隆”,把手箱才能够拉伸以适应窗口的宽度!

Good thing is - can just add to Vbox, no need to manage it; 好东西是-可以直接添加到Vbox,而无需对其进行管理; 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) 但似乎拖动行为将是一个挑战...但是我仍然想知道这是否是使用的正确的Gtk / Glade UI层次结构(以及是否存在从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. ...希望确认深度克隆代码是可以的。

Here's the code multitrack.py , that uses the multitrack.glade above: 这是代码multitrack.py ,它使用上面的multitrack.glade

# 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()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM