无法将项添加到Gtk.Window之外的GtkListBox

时间:2015-02-07 23:44:55

标签: python twisted gtk3 pygobject

我试图在Python 2.7中使用扭曲的小型GTK 3 irc客户端。目前,我有一个非常基本的客户端,可以成功连接到irc网络并在主文本区域显示一些东西。我目前正在努力实现多渠道支持,但是当一个频道加入时,用一个包含频道的条目填充GtkListBox似乎不起作用。

python代码如下:

main.py

from twisted.internet import gtk3reactor

gtk3reactor.install()

from twisted.internet import reactor

from gi.repository import Gtk, GObject
import time
from ConnectDialog import ConnectDialog

# twisted imports
from twisted.words.protocols import irc
from twisted.internet import protocol


class Client(irc.IRCClient):

    def __init__(self):
        self.channels = []

    def _get_nickname(self):
        return self.factory.username

    def _get_password(self):
        return self.factory.password

    nickname = property(_get_nickname)
    password = property(_get_password)

    def connectionMade(self):
        irc.IRCClient.connectionMade(self)
        self.log("[Connected established at %s]" %
                 time.asctime(time.localtime(time.time())))

    def connectionLost(self, reason):
        irc.IRCClient.connectionLost(self, reason)
        self.log("[Disconnected at %s]" %
                 time.asctime(time.localtime(time.time())))

    # callbacks for events

    def signedOn(self):
        """Called when bot has succesfully signed on to server."""
        self.log("Successfuly connected!")
        self.join(self.factory.channel)

    def joined(self, channel):
        self.addChannel(channel)
        self.log("[You have joined %s]" % channel)

    def privmsg(self, user, channel, msg):
        """This will get called when the bot receives a message."""
        if not any(channel in s for s in self.channels):
            self.addChannel(channel) # multiple channels for znc

        user = user.split('!', 1)[0]
        self.log("<%s> %s" % (user, msg))

    def action(self, user, channel, msg):
        """This will get called when the bot sees someone do an action."""
        user = user.split('!', 1)[0]
        self.log("* %s %s" % (user, msg))

    # irc callbacks

    def irc_NICK(self, prefix, params):
        """Called when an IRC user changes their nickname."""
        old_nick = prefix.split('!')[0]
        new_nick = params[0]
        self.log("%s is now known as %s" % (old_nick, new_nick))


    # For fun, override the method that determines how a nickname is changed on
    # collisions. The default method appends an underscore.
    def alterCollidedNick(self, nickname):
        """
        Generate an altered version of a nickname that caused a collision in an
        effort to create an unused related name for subsequent registration.
        """
        return nickname + '_'

    def log(self, message):
        end_iter = self.factory.messages_buffer.get_end_iter()
        timestamp = time.strftime("[%H:%M:%S]", time.localtime(time.time()))
        self.factory.messages_buffer.insert(end_iter, '%s %s\n' % (timestamp, message))

    def addChannel(self, channel):
        row = Gtk.ListBoxRow()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
        row.add(hbox)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        hbox.pack_start(vbox, True, True, 0)

        label1 = Gtk.Label(channel, xalign=0)
        label2 = Gtk.Label("Some more info text here", xalign=0)
        vbox.pack_start(label1, True, True, 0)
        vbox.pack_start(label2, True, True, 0)

        button = Gtk.Button("Close")
        button.props.valign = Gtk.Align.CENTER
        hbox.pack_start(button, False, True, 0)
        GObject.idle_add(self.add_to_chan_list, row)
        #self.factory.chan_list.add(row)
        self.channels.append(channel)

    def add_to_chan_list(self, row):
        self.factory.chan_list.add(row)
        return False

class IRCFactory(protocol.ClientFactory):
    """A factory for Clients.

    A new protocol instance will be created each time we connect to the server.
    """

    # the class of the protocol to build when new connection is made
    protocol = Client

    def __init__(self, username, channel, password, messages_buffer, chan_list, parent):
        self.channel = channel
        self.username = username
        self.password = password
        self.chan_list = chan_list
        self.messages_buffer = messages_buffer
        self.parent = parent

    def clientConnectionLost(self, connector, reason):
        """If we get disconnected, reconnect to server."""
        connector.connect()

    def clientConnectionFailed(self, connector, reason):
        print "connection failed:", reason
        reactor.stop()


class MainWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Gnome IRC")
        self.set_border_width(10)
        self.set_default_size(800, 600)

        hb = Gtk.HeaderBar()
        hb.set_show_close_button(True)
        hb.props.title = "Gnome IRC"
        self.set_titlebar(hb)

        button = Gtk.Button("Connect")
        button.connect("clicked", self.on_connect_clicked)
        hb.pack_start(button)

        builder = Gtk.Builder()
        builder.add_from_file("main_view.glade")
        self.message_entry = builder.get_object("message_entry")
        self.messages_view = builder.get_object("messages")
        self.ircview = builder.get_object("ircviewpane") # GtkBox
        self.chan_list = builder.get_object("channel_list") # GtkListBox

        self.add(self.ircview)
        self.connect("delete_event", self.on_quit)


    def on_connect_clicked(self, widget):
        dialog = ConnectDialog(self)
        dialog.connect('response', self.dialog_response_cb)
        dialog.show()

    def dialog_response_cb(self, dialog, response):

        if response == Gtk.ResponseType.OK:
            server = dialog.address_entry.get_text()
            port = int(dialog.port_entry.get_text())
            nickname = dialog.nick_entry.get_text()
            password = dialog.password.get_text()
            channel = "#rymate"

            dialog.destroy()

            factory = IRCFactory(nickname, channel, password,
                                 self.messages_view.get_buffer(),
                                 self.chan_list, self)

            # connect factory to this host and port
            reactor.connectTCP(server, port, factory)

            # run bot
            # reactor.run()

        elif response == Gtk.ResponseType.CANCEL:
            dialog.destroy()

    def on_quit(self, *args):
        Gtk.main_quit()
        reactor.callFromThread(reactor.stop)


win = MainWindow()
win.show_all()
#Gtk.main()
reactor.run()

ConnectDialog.py

from gi.repository import Gtk


class ConnectDialog(Gtk.Dialog):
    def __init__(self, parent):
        Gtk.Dialog.__init__(self, "Connect to a Server", parent, 0,
                            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                             Gtk.STOCK_OK, Gtk.ResponseType.OK), use_header_bar=1)

        builder = Gtk.Builder()
        builder.add_from_file("server.glade")
        self.address_entry = builder.get_object("address")
        self.port_entry = builder.get_object("port")
        self.nick_entry = builder.get_object("username")
        self.password = builder.get_object("password")
        self.get_content_area().add(builder.get_object("ServerForm"))

server.glade

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
  <requires lib="gtk+" version="3.12"/>
  <object class="GtkGrid" id="ServerForm">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="margin_left">5</property>
    <property name="margin_right">5</property>
    <property name="margin_top">5</property>
    <property name="margin_bottom">5</property>
    <property name="hexpand">True</property>
    <property name="row_spacing">10</property>
    <property name="column_spacing">10</property>
    <child>
      <object class="GtkLabel" id="label1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="halign">end</property>
        <property name="label" translatable="yes">Address</property>
      </object>
      <packing>
        <property name="left_attach">0</property>
        <property name="top_attach">0</property>
      </packing>
    </child>
    <child>
      <object class="GtkLabel" id="label2">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="halign">end</property>
        <property name="label" translatable="yes">Port</property>
      </object>
      <packing>
        <property name="left_attach">0</property>
        <property name="top_attach">1</property>
      </packing>
    </child>
    <child>
      <object class="GtkLabel" id="label3">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="halign">end</property>
        <property name="label" translatable="yes">IRC Password</property>
      </object>
      <packing>
        <property name="left_attach">0</property>
        <property name="top_attach">2</property>
      </packing>
    </child>
    <child>
      <object class="GtkLabel" id="label4">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="halign">end</property>
        <property name="label" translatable="yes">Username</property>
      </object>
      <packing>
        <property name="left_attach">0</property>
        <property name="top_attach">3</property>
      </packing>
    </child>
    <child>
      <object class="GtkLabel" id="label5">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="halign">end</property>
        <property name="label" translatable="yes">Real Name</property>
      </object>
      <packing>
        <property name="left_attach">0</property>
        <property name="top_attach">4</property>
      </packing>
    </child>
    <child>
      <object class="GtkEntry" id="address">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="hexpand">True</property>
        <property name="input_purpose">url</property>
      </object>
      <packing>
        <property name="left_attach">1</property>
        <property name="top_attach">0</property>
      </packing>
    </child>
    <child>
      <object class="GtkEntry" id="port">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="hexpand">True</property>
        <property name="input_purpose">number</property>
      </object>
      <packing>
        <property name="left_attach">1</property>
        <property name="top_attach">1</property>
      </packing>
    </child>
    <child>
      <object class="GtkEntry" id="password">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="hexpand">True</property>
        <property name="visibility">False</property>
        <property name="invisible_char">*</property>
        <property name="placeholder_text" translatable="yes">Optional</property>
        <property name="input_purpose">password</property>
      </object>
      <packing>
        <property name="left_attach">1</property>
        <property name="top_attach">2</property>
      </packing>
    </child>
    <child>
      <object class="GtkEntry" id="username">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="hexpand">True</property>
      </object>
      <packing>
        <property name="left_attach">1</property>
        <property name="top_attach">3</property>
      </packing>
    </child>
    <child>
      <object class="GtkEntry" id="realname">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="hexpand">True</property>
      </object>
      <packing>
        <property name="left_attach">1</property>
        <property name="top_attach">4</property>
      </packing>
    </child>
  </object>
</interface>

main_view.glade

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
  <requires lib="gtk+" version="3.12"/>
  <object class="GtkBox" id="ircviewpane">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="margin_left">4</property>
    <property name="margin_right">4</property>
    <property name="margin_top">4</property>
    <property name="margin_bottom">4</property>
    <property name="spacing">5</property>
    <child>
      <object class="GtkScrolledWindow" id="scrolledwindow2">
        <property name="width_request">310</property>
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="shadow_type">in</property>
        <child>
          <object class="GtkViewport" id="viewport1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkListBox" id="channel_list">
                <property name="width_request">290</property>
                <property name="visible">True</property>
                <property name="app_paintable">True</property>
                <property name="can_focus">False</property>
                <property name="vexpand">True</property>
                <property name="border_width">2</property>
              </object>
            </child>
          </object>
        </child>
      </object>
      <packing>
        <property name="expand">False</property>
        <property name="fill">True</property>
        <property name="position">0</property>
      </packing>
    </child>
    <child>
      <object class="GtkBox" id="box1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="hexpand">True</property>
        <property name="vexpand">True</property>
        <property name="orientation">vertical</property>
        <property name="spacing">5</property>
        <child>
          <object class="GtkScrolledWindow" id="scrolledwindow1">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="hscrollbar_policy">never</property>
            <property name="shadow_type">in</property>
            <child>
              <object class="GtkTextView" id="messages">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="hexpand">True</property>
                <property name="vexpand">True</property>
                <property name="pixels_above_lines">2</property>
                <property name="pixels_below_lines">2</property>
                <property name="pixels_inside_wrap">2</property>
                <property name="editable">False</property>
                <property name="wrap_mode">word</property>
                <property name="left_margin">2</property>
                <property name="right_margin">2</property>
                <property name="cursor_visible">False</property>
                <property name="accepts_tab">False</property>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkEntry" id="message_entry">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
      <packing>
        <property name="expand">False</property>
        <property name="fill">True</property>
        <property name="position">1</property>
      </packing>
    </child>
  </object>
</interface>

据我所知,添加到GtkListBox的代码可以在窗口中工作,但不能在扭曲的ircclient类中工作。

任何帮助表示赞赏!

1 个答案:

答案 0 :(得分:1)

事实证明,这只是我需要调用row.show_all()以便在UI中显示listboxitem的情况