📄 x9991.htm
字号:
>Next, we build a frame to hold the three buttons <I
CLASS="EMPHASIS"
>Clear</I
>,
<I
CLASS="EMPHASIS"
>Fetch</I
>, and <I
CLASS="EMPHASIS"
>Exit</I
>, and a scrollable
list to contain the item titles as we receive them:</P
><P
><PRE
CLASS="SCREEN"
># Button frame
my $buttons = $main->Frame();
$buttons->pack(qw/-side bottom -fill x/);
# Headline list
my $list = $main->Scrolled(qw/Listbox -scrollbars e -height 40 -setgrid 1/); </PRE
></P
><P
>Defining the buttons, one at a time, brings our attention to the Tk
UI event model, in that we define the handlers using the
<TT
CLASS="LITERAL"
>-command</TT
> argument of the
<TT
CLASS="FUNCTION"
>Button()</TT
> method. The handlers' jobs are quite
small, so we can get away with writing them "in-line":</P
><P
><PRE
CLASS="SCREEN"
># Clear button
my $button_clear = $buttons->Button(
-text => 'Clear',
-underline => '0',
-command => sub
{
@list = (); $list->delete(0, 'end')
},
); </PRE
></P
><P
>If called, the <I
CLASS="EMPHASIS"
>Clear</I
> button will clear the scrollable
display by calling the <TT
CLASS="FUNCTION"
>delete()</TT
> method on our
<TT
CLASS="LITERAL"
>$list</TT
> object, and emptying the corresponding array
of URLs.</P
><P
>The <I
CLASS="EMPHASIS"
>Fetch</I
> button extracts the URL from the item
that is highlighted in the scrollable list (using the
<TT
CLASS="FUNCTION"
>curselection()</TT
> method to retrieve the index value)
which is then used to look up the <TT
CLASS="LITERAL"
>@list</TT
> array, and
calls the external browser program in the background, passing it that URL.
Many browsers accept a URL as the first argument, if your choice of
browser doesn't, you'll need to modify this call slightly.</P
><P
><PRE
CLASS="SCREEN"
># Fetch Button
my $button_fetch = $buttons->Button(
-text => 'Fetch',
-underline => '0',
-command => sub
{
system(
join(" ", (BROWSER, $list[$list->curselection], "&"))
)
},
); </PRE
></P
><P
>The <I
CLASS="EMPHASIS"
>Exit</I
> button, if pressed, uses
<TT
CLASS="FUNCTION"
>destroy()</TT
> to, well, destroy the main window.
This will cause Tk's main event loop to come to an end, passing
control back to the statement in the script following where that
main event loop was launched (with <TT
CLASS="FUNCTION"
>MainLoop()</TT
>).</P
><P
><PRE
CLASS="SCREEN"
># Exit button
my $button_exit = $buttons->Button(
-text => 'exit',
-underline => '0',
-command => [$main => 'destroy'],
); </PRE
></P
><P
>Having created all the buttons, and packed everything into our
window with the <TT
CLASS="FUNCTION"
>pack()</TT
> method:</P
><P
><PRE
CLASS="SCREEN"
>$button_clear->pack(qw/-side left -expand 1/);
$button_fetch->pack(qw/-side left -expand 1/);
$button_exit->pack(qw/-side left -expand 1/);
$list->pack(qw/-side left -expand 1 -fill both/); </PRE
></P
><P
>we announce to the JSM that we're available:</P
><P
><PRE
CLASS="SCREEN"
>$connection->PresenceSend(); </PRE
></P
><P
>All that remains for us to do is start Tk's main event loop. We include
a call to the <TT
CLASS="LITERAL"
>Net::Jabber</TT
> <TT
CLASS="FUNCTION"
>Disconnect()</TT
>
method for when the <I
CLASS="EMPHASIS"
>Exit</I
> button is pressed and control
returns to the script, so we can gracefully end our Jabber connection:</P
><P
><PRE
CLASS="SCREEN"
>MainLoop();
$connection->Disconnect;
exit(0); </PRE
></P
><P
>We defined the <TT
CLASS="FUNCTION"
>check_headlines()</TT
> function as the
function to invoke every five seconds.</P
><P
><PRE
CLASS="SCREEN"
>sub check_headlines {
$connection->Process(1);
while (@headlines) {
my $headline = pop @headlines;
$list->insert(0, $headline->{title});
unshift @list, $headline->{link};
}
} </PRE
></P
><P
>To check for any messages that have arrived on the XML stream, we can
call the <TT
CLASS="FUNCTION"
>Process()</TT
> method on our connection
object. If there <I
CLASS="EMPHASIS"
>are</I
> any waiting messages, the
callback that we defined to handle
them—<TT
CLASS="FUNCTION"
>handle_message()</TT
>—will be called:</P
><P
><PRE
CLASS="SCREEN"
>sub handle_message {
my $msg = new Net::Jabber::Message($_[1]);
return unless $msg->GetType eq 'headline';
my ($oob) = $msg->GetX('jabber:x:oob');
push @headlines, {
link => $oob->GetURL(),
title => $msg->GetSubject(),
};
} </PRE
></P
><P
>This message handling callback will ignore everything but
<TT
CLASS="LITERAL"
><message/></TT
> elements that have the
value "<TT
CLASS="LITERAL"
>headline</TT
>" in the <TT
CLASS="LITERAL"
>type</TT
>
attribute. Remembering that a headline message, complete with
an <TT
CLASS="LITERAL"
><x/></TT
> extension, qualified with
the <TT
CLASS="LITERAL"
>jabber:x:oob</TT
> namespace, looks like this:</P
><P
><PRE
CLASS="SCREEN"
><message type='headline' to='dj@qmacro.dyndns.org'>
<subject>JabberCon Update 11:45am - Aug 20</subject>
<body>JabberCon Update - Monday Morning</body>
<x xmlns='jabber:x:oob'>
<url>http://www.jabbercentral.com/news/view.php?news_id=998329970</url>
<desc>JabberCon Update - Monday Morning</desc>
</x>
</message> </PRE
></P
><P
>we can see fairly easily what the <TT
CLASS="FUNCTION"
>GetX()</TT
> method
does. It returns, in list context, all the
<TT
CLASS="LITERAL"
><x/></TT
> elements contained in the element
represented by <TT
CLASS="LITERAL"
>$msg</TT
> that are qualified by the
<TT
CLASS="LITERAL"
>jabber:x:oob</TT
> namespace. We're only expecting there
to be one, which is why we plan to throw all but the first array item
away with the <TT
CLASS="LITERAL"
>($oob)</TT
> construction. After the
call to <TT
CLASS="FUNCTION"
>GetX()</TT
>, the object in <TT
CLASS="LITERAL"
>$oob</TT
>
represents this part of the message:</P
><P
><PRE
CLASS="SCREEN"
><x xmlns='jabber:x:oob'>
<url>http://www.jabbercentral.com/news/view.php?news_id=998329970</url>
<desc>JabberCon Update - Monday Morning</desc>
</x> </PRE
></P
><P
>The item's details—the URL and title—are pushed onto the
<TT
CLASS="LITERAL"
>@headlines</TT
> list, and our headline type message
handling function has done its job.
Control passes back to the <TT
CLASS="FUNCTION"
>check_headlines()</TT
> script,
to immediately after the call to the <TT
CLASS="FUNCTION"
>Process()</TT
> method.</P
><P
>The <TT
CLASS="FUNCTION"
>handle_message()</TT
> function may have
bee called multiple times, depending on how many elements had arrived;
so the <TT
CLASS="LITERAL"
>@headlines</TT
> array might contain more than one
item. We run through the array, <TT
CLASS="FUNCTION"
>pop()</TT
>ping off
each headline in turn, inserting the title into our scrollable list
object, and the URL into the corresponding position in our
<TT
CLASS="LITERAL"
>@list</TT
> array: </P
><P
><PRE
CLASS="SCREEN"
>$list->insert(0, $headline->{title});
unshift @list, $headline->{link}; </PRE
></P
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="JABTDG-CH-8-SECT-4.3"
>The script</A
></H2
><P
>Here's the script in its entirety.</P
><P
><PRE
CLASS="SCREEN"
>use Tk;
use Net::Jabber qw(Client);
use strict;
use constant SERVER => 'gnu.pipetree.com';
use constant PORT => 5222;
use constant USER => 'dj';
use constant PASSWORD => 'secret';
use constant RESOURCE => 'hlv';
use constant BROWSER => '/usr/bin/konqueror';
my @headlines;
my @list;
my $connection = Net::Jabber::Client->new();
$connection->Connect(
hostname => SERVER,
port => PORT,
) or die "Cannot connect ($!)\n";
my @result = $connection->AuthSend(
username => USER,
password => PASSWORD,
resource => RESOURCE,
);
if ($result[0] ne "ok") {
die "Ident/Auth with server failed: $result[0] - $result[1]\n";
}
$connection->SetCallBacks( message => \&handle_message );
my $main = MainWindow->new( -title => "Headline Viewer" );
$main->geometry('50x5+10+10');
$main->repeat(5000, \&check_headlines);
# Button frame
my $buttons = $main->Frame();
$buttons->pack(qw/-side bottom -fill x/);
# Headline list
my $list = $main->Scrolled(qw/Listbox -scrollbars e -height 40 -setgrid 1/);
# Clear button
my $button_clear = $buttons->Button(
-text => 'Clear',
-underline => '0',
-command => sub
{
@list = (); $list->delete(0, 'end')
},
);
# Fetch Button
my $button_fetch = $buttons->Button(
-text => 'Fetch',
-underline => '0',
-command => sub
{
system(
join(" ", (BROWSER, $list[$list->curselection], "&"))
)
},
);
# Exit button
my $button_exit = $buttons->Button(
-text => 'exit',
-underline => '0',
-command => [$main => 'destroy'],
);
$button_clear->pack(qw/-side left -expand 1/);
$button_fetch->pack(qw/-side left -expand 1/);
$button_exit->pack(qw/-side left -expand 1/);
$list->pack(qw/-side left -expand 1 -fill both/);
$connection->PresenceSend();
MainLoop();
$connection->Disconnect;
exit(0);
sub check_headlines {
$connection->Process(1);
while (@headlines) {
my $headline = pop @headlines;
$list->insert(0, $headline->{title});
unshift @list, $headline->{link};
}
}
sub handle_message {
my $msg = new Net::Jabber::Message($_[1]);
return unless $msg->GetType eq 'headline';
my ($oob) = $msg->GetX('jabber:x:oob');
push @headlines, {
link => $oob->GetURL(),
title => $msg->GetSubject(),
};
} </PRE
></P
></DIV
></DIV
><H3
CLASS="FOOTNOTES"
>Notes</H3
><TABLE
BORDER="0"
CLASS="FOOTNOTES"
WIDTH="100%"
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN9996"
HREF="x9991.htm#AEN9996"
>[1]</A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>Alright, I said it. "Leverage." It's the only occasion in the book, ok?</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN10077"
HREF="x9991.htm#AEN10077"
>[2]</A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>(<I
CLASS="EMPHASIS"
>Konqueror</I
>, my browser of choice in the KDE environment).</P
></TD
></TR
></TABLE
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="x9016.htm"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="book1.htm"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="c10187.htm"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>RSS punter</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="c7982.htm"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Pointers for further development</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -