2015-02-07 70 views
1

我試圖在Python 2.7中用扭曲來製作一個小型的GTK 3 irc客戶端。目前,我有一個非常基本的客戶端,可以成功連接到irc網絡並在主文本區域顯示一些內容。我目前正在努力實現多通道支持,但是在連接通道時填充包含通道的條目的GtkListBox似乎不起作用。無法將項目添加到我的Gtk.Window之外的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類。

任何幫助表示讚賞!

+0

顯示的ListBoxItem中可以包含一個鏈接到'main_view.glade'某處的情況下?沒有它的例子不會運行。 – Glyph 2015-02-08 00:13:21

+0

哦,當然是'server.glade'。 – Glyph 2015-02-08 00:13:50

+0

這段代碼的一個主要問題是你正在執行'Gtk.main()'作爲主循環,然後在回調中嘗試執行'reactor.run()'。這最多可以創建一個嵌套的主循環,該循環由Gtk支持,但不是Twisted所支持的,並且會導致'dialog_response_cb'永久掛起。這可能會造成奇怪的行爲。 – Glyph 2015-02-08 00:18:56

回答

1

原來這只是我需要調用row.show_all(),以便在UI

相關問題