python - 无法将项目添加到我的 Gtk.Window 之外的 GtkListBox

标签 python twisted gtk3 pygobject

我正在尝试使用 Twisted 在 Python 2.7 中制作一个小型 GTK 3 irc 客户端。目前,我有一个非常基本的客户端,可以成功连接到 irc 网络并在主文本区域中显示一些内容。我目前正在尝试实现多 channel 支持,但是在加入 channel 时用包含 channel 的条目填充 GtkListBox 似乎不起作用。

Python代码如下:

ma​​in.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"))

服务器.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>

ma​​in_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 类中运行。

感谢任何帮助!

最佳答案

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

关于python - 无法将项目添加到我的 Gtk.Window 之外的 GtkListBox,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28388886/

相关文章:

扭曲的大文件传输

python - 设置 Gtk3 MenuItem 的悬停背景颜色

python - 对话框中的 GTK 标签包装

python - 键/值(一般)和 Tokyo Cabinet (python tc 特定)问题

python - 选择列表中包含的子字符串(没有语法字符)

python - 使用 drawContours() thickness=-1 填充轮廓将不起作用

python - 扭曲的 gevent eventlet - 我什么时候使用它们

python - Twisted 中是否有用于使用 IMAP4 下载邮件附件的 API?

python - VS上的`Module ' cv2 ' has no ' cvtColor ' member pylint(no-member)`

Windows 上的 Python3 和 GTK3