geekdoc-python-zh/docs/pythonlibrary/shortening-urls-with-wxpyth...

12 KiB
Raw Permalink Blame History

使用 wxPython 缩短 URL

原文:https://www.blog.pythonlibrary.org/2009/12/24/shortening-urls-with-wxpython/

今年夏天,我在 Ars Technica 上偶然发现了一篇关于使用 PyGTK 创建 URL 缩短器的文章。我认为这很有趣,但是我不使用 PyGTK。所以在那时我决定使用 wxPython 编写自己的代码,并使用本文的代码来缩短代码。我很快拼凑了一些东西,然后把这篇文章放在一边,有点忘记了。今天,我决定继续完成它,并创建一个应用程序,可以使用其他流行的网址缩短程序来缩短网址。

apink ars

首先,我将向你展示我的原始程序,它基本上是模仿 Ars Technica 的。


import re
import urllib
import urllib2
import wx

class ArsShortener(wx.Frame):

    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, 
                          'wxArsShortner', size=(300,70))

        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)

        self.txt = wx.TextCtrl(panel, wx.ID_ANY, "", size=(300, -1))
        self.txt.Bind(wx.EVT_TEXT, self.onTextChange)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.txt, 0, wx.EXPAND, 5)
        panel.SetSizer(sizer)

    #----------------------------------------------------------------------
    def onTextChange(self, event):
        """"""
        text = self.txt.GetValue()
        textLength = len(text)
        if re.match("^https?://[^ ]+", text) and textLength > 20:
            apiurl = "http://is.gd/api.php?" + urllib.urlencode(dict(longurl=text))
            shorturl = urllib2.urlopen(apiurl).read() 
            self.txt.SetValue(shorturl)

if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = ArsShortener()
    frame.Show()
    app.MainLoop()

正如您所看到的,几乎一半的代码与 wxPython 相关,而另一半来自 Ars 文章。首先,我们导入 Python 正则表达式模块 re用于验证目的。它检查粘贴的 URL 以确保它是 URL 的正确格式,而不仅仅是垃圾(参见 onTextChange 方法)。urllib 模块用于访问 is.gd 网站,并向其传递我们想要缩短的 URL。一旦我们得到结果我们调用文本控件的 SetValue 方法向用户显示它。条件语句还检查 URL 的长度是否足够长,以保证缩短它。

你会注意到我有三行注释掉了。我这样做是为了让应用程序可以调整大小,并让文本控件随着框架一起增长,但后来认为这可能是蹩脚的。因此,我将其注释掉。然而,我想你,亲爱的读者,可能会发现知道如何做到这一点是有用的。

缩短其他网站的 URL

现在我们来看看我的扩展示例。它可以使用 is.gd、bit.ly 或古老的 tinyurl 来缩短 url。对于本文的这一部分除了 wx 之外,您还需要以下模块:

我确信这些网站也有使用 urllib 模块的方法,但是我想看看这些第三方模块是否简化了事情。请注意,如果你想使用 bit.ly他们要求你注册一个 API 密匙。我不会公布我的,但我会告诉你如何在我的程序中使用它。所以,事不宜迟,继续表演吧:


import re
import urllib
import urllib2
import wx

bitlyFlag = True
tinyurlFlag = True

try:
    import bitly
except ImportError:
    bitlyFlag = False

try:
    import tinyurl
except ImportError:
    tinyurlFlag = False

########################################################################
class MainPanel(wx.Panel):
    """
    """

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
        self.frame = parent

        # create the widgets
        self.createLayout()

    #----------------------------------------------------------------------
    def createLayout(self):
        """
        Create widgets and lay them out
        """
        choices = ["is.gd"]
        if bitlyFlag:
            choices.append("bit.ly")
        if tinyurlFlag:
            choices.append("tinyurl")
        choices.sort()

        # create the widgets
        self.urlCbo = wx.ComboBox(self, wx.ID_ANY, "is.gd", 
                                  choices=choices,
                                  size=wx.DefaultSize,
                                  style=wx.CB_DROPDOWN)
        self.inputUrlTxt = wx.TextCtrl(self, value="Paste long url here")
        self.inputUrlTxt.Bind(wx.EVT_SET_FOCUS, self.onFocus)
        self.outputUrlTxt = wx.TextCtrl(self, style=wx.TE_READONLY)

        shortenBtn = wx.Button(self, label="Shorten URL")
        shortenBtn.Bind(wx.EVT_BUTTON, self.onShorten)
        copyBtn = wx.Button(self, label="Copy to Clipboard")
        copyBtn.Bind(wx.EVT_BUTTON, self.onCopy)

        # create the sizers
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)

        # layout the widgets
        mainSizer.Add(self.urlCbo, 0, wx.ALL, 5)
        mainSizer.Add(self.inputUrlTxt, 0, 
                      wx.ALL|wx.EXPAND, 5)
        mainSizer.Add(self.outputUrlTxt, 0, 
                      wx.ALL|wx.EXPAND, 5)
        btnSizer.Add(shortenBtn, 0, wx.ALL|wx.CENTER, 5)
        btnSizer.Add(copyBtn, 0, wx.ALL|wx.CENTER, 5)
        mainSizer.Add(btnSizer, 0, wx.ALL|wx.CENTER, 5)
        self.SetSizer(mainSizer)

    #----------------------------------------------------------------------
    def onCopy(self, event):
        """
        Copies data to the clipboard or displays an error
        dialog if the clipboard is inaccessible.
        """
        text = self.outputUrlTxt.GetValue()
        self.do = wx.TextDataObject()
        self.do.SetText(text)
        if wx.TheClipboard.Open():
            wx.TheClipboard.SetData(self.do)
            wx.TheClipboard.Close()
            status = "Copied %s to clipboard" % text
            self.frame.statusbar.SetStatusText(status)
        else:
            wx.MessageBox("Unable to open the clipboard", "Error")

    #----------------------------------------------------------------------
    def onFocus(self, event):
        """
        When control is given the focus, it is cleared
        """
        self.inputUrlTxt.SetValue("")

    #----------------------------------------------------------------------
    def onShorten(self, event):
        """
        Shortens a url using the service specified.
        Then sets the text control to the new url.
        """
        text = self.inputUrlTxt.GetValue()
        textLength = len(text)

        if re.match("^https?://[^ ]+", text) and textLength > 20:
            pass
        else:
            wx.MessageBox("URL is already tiny!", "Error")
            return

        url = self.urlCbo.GetValue()
        if url == "is.gd":
            self.shortenWithIsGd(text)
        elif url == "bit.ly":
            self.shortenWithBitly(text)
        elif url == "tinyurl":
            self.shortenWithTinyurl(text)

    #----------------------------------------------------------------------
    def shortenWithBitly(self, text):
        """
        Shortens the URL in the text control using bit.ly

        Requires a bit.ly account and API key
        """
        bitly.API_LOGIN = "username"
        bitly.API_KEY = "api_key"
        url = bitly.shorten(text)
        self.outputUrlTxt.SetValue(url)

    #----------------------------------------------------------------------
    def shortenWithIsGd(self, text):
        """
        Shortens the URL with is.gd using urllib and urllib2
        """

        apiurl = "http://is.gd/api.php?" + urllib.urlencode(dict(longurl=text))
        shorturl = urllib2.urlopen(apiurl).read() 
        self.outputUrlTxt.SetValue(shorturl)

    #----------------------------------------------------------------------
    def shortenWithTinyurl(self, text):
        """
        Shortens the URL with tinyurl
        """
        print "in tinyurl"
        url = tinyurl.create_one(text)
        self.outputUrlTxt.SetValue(url)

########################################################################
class UrlFrame(wx.Frame):
    """
    wx.Frame class
    """
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        title = "URL Shortener"
        wx.Frame.__init__(self, None, wx.ID_ANY, 
                          title=title, size=(650, 220))
        panel = MainPanel(self)
        self.statusbar = self.CreateStatusBar()
        self.SetMinSize((650, 220))       

#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = UrlFrame()
    frame.Show()
    app.MainLoop()

这段代码比我的简单示例要长得多,但是它内置了更多的逻辑。我马上实现了一些异常处理,以防程序员没有安装 bitly 或 tinyurl 模块。如果没有,则设置一个标志,防止添加这些选项。您将在 MainPanel 类的 createLayout 方法中看到这一点。这就是我们将选项添加到组合框将使用的选择列表的地方。根据您安装的内容,您会在下拉列表中看到一到三个选项。

下一个有趣的地方是输入 url 文本控件绑定到焦点事件的地方。当我们粘贴一个 url 到文本控件中时,我们用它来清除文本控件。还要注意,输出文本控件被设置为只读模式。这可以防止用户弄乱新的 url。最后我们到达最后两个部件:缩短 URL 的按钮和复制到剪贴板的按钮。

让我们快速看一下 onCopy 方法中发生了什么,因为它是下一个:


def onCopy(self, event):
    """
    Copies data to the clipboard or displays an error
    dialog if the clipboard is inaccessible.
    """
    text = self.outputUrlTxt.GetValue()
    self.do = wx.TextDataObject()
    self.do.SetText(text)
    if wx.TheClipboard.Open():
        wx.TheClipboard.SetData(self.do)
        wx.TheClipboard.Close()
        status = "Copied %s to clipboard" % text
        self.frame.statusbar.SetStatusText(status)
    else:
        wx.MessageBox("Unable to open the clipboard", "Error")

如您所见,这从输入文本控件中获取了当前文本,并从中创建了一个 TextDataObject。然后我们尝试打开剪贴板如果成功我们使用剪贴板的 SetData 方法将 TextDataObject 放入其中。最后,我们通过改变框架的状态栏文本来提醒用户我们做了什么。

在 onShorten 方法中,我重用了 Ars 程序中的正则表达式来检查用户是否粘贴了有效的 url我们还检查了 url 的长度,看它是否真的需要缩短。我们从 combobox 中获取 shortener url 类型,然后使用一个条件将我们想要缩短的 url 传递给适当的缩短方法。 shortenWithIsGd 方法与第一个例子基本相同,所以我们将跳过这个例子。 shortenWithBitly 方法显示我们需要设置 LOGIN 和 API_KEY 属性,然后才能缩短 url。一旦我们完成了我们就调用 bitly 的缩短方法。在 shortenWithTinyurl 方法中,它甚至更简单:您在这里需要做的就是调用 Tinyurl 的 create_one 方法。

包扎

现在你知道了通过几种方法来缩短你的长 URL 的基础。您可以随意添加自己的特性或其他缩短 API 来改进您自己使用的应用程序。注意,还有另一个 Python 小模块。虽然它的主页上说它需要的只是 simplejson 模块,但是如果你真的试图使用这个模块,如果你没有安装 django ,你会收到一个错误。他们似乎意识到了这个问题,但出于这个原因,我选择使用 Opie 的 bitly 模块。祝编码愉快!

注意:这段代码是在 Windows Vista 上使用带有 Python 2.5 的 wxPython 2.8.10.1(unicode)进行测试的。

下载