Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Publicaciones sobre urssus (publicaciones antiguas, página 6)

urssus at 27 days old

Af­ter 27 days of de­vel­op­men­t, uRSSus is fi­nal­ly a re­al­ly us­able ap­p. I miss noth­ing from Akre­ga­tor, al­though there are a few unim­ple­ment­ed things. It was bro­ken the alst cou­ple of days, be­cause I was rewrit­ing large chunks of its gut­s, but the new guts work much bet­ter ;-)

The miss­ing pieces (I know about):

  • Fav­i­­cons

  • Save col­umn width in post list

  • Show to­­tal posts in feed list

  • Drag&­­Drop is not re­li­able (weird things hap­pen ev­ery few tries)

  • Metafeeds (ex. your un­read post­s/ your starred post­s) are not up­­­dat­ed au­­to­­mat­i­­cal­­ly

  • Del.i­­cio.us sync is not work­ing at all

  • No help

  • No de­­cent con­­fig di­a­log (but a func­­tion­al re­­place­­men­t!)

  • Very oc­­ca­­sion­al DB lock­­ing.

  • Log­ging is bro­ken

  • Need to im­­ple­­ment back­­up of the DB

That's not re­al­ly more than a day or two of work.

Right now (re­vi­sion 570) it's work­ing pret­ty good, so it's a good re­vi­sion to try, if you feel up to it.

Urssus goes meta!

I have been de­lay­ing this func­tion­al­i­ty be­cause I ex­pect­ed it to be hard to do, then... it was very easy! Now you can cre­ate "metafeed­s" based on queries. Here's the first ex­am­ple: "Post.im­por­tan­t==True".

urssus20

Of course, be­yond a set of stan­dard metafeed­s, a UI to al­low the us­er to cre­ate them will be hard but at least the ba­sic func­tion­al­i­ty is there (BTW, it's 15 (fif­teen!) lines of code)

uRSSus: first post 0.2.10 features (has screenie)

It's run­ning pre­ty sta­ble (at least for me) so I de­cid­ed to fix some is­sues and add some fea­tures. First a screen­shot:

urssus19

And now tex­t:

  • A new fea­­ture that al­­so fix­es an is­­sue:

    vgar­­vardt asked about sort­ing by feed,­­for ex­am­­ple when read­­ing a fold­er. Im­­ple­­men­t­ed it us­ing a "feed" col­umn that you can en­able/dis­­able. Since your choice is per­­sis­ten­t, there's no need to add a con­­fig­u­ra­­tion op­­tion to the di­a­log.

  • Im­­por­­tant is now Starred. I rel­­ly like google's idea of us­ing stars at hand to mark things im­­por­­tan­t. So, there they are. The star icon I did my­­self, in Inkscape, try­ing to re­spect the Rein­hardt style. I think it looks nice.

  • Google News feed­s! Yes, I know I could just go to http://news.­­google.­­com, make a search and sub­­scribe to the re­­sults page. Can my mom? Nope. So, when adding new feed, just choose "Cre­ate google news feed" en­ter some key­­word­s, and it's ready.

    I need to add "cus­­tom field­­s" to the feed con­­fig di­alog, though, since once it's cret­ed it's just a reg­u­lar feed with a long URL.

  • Al­­so did some per­­for­­mance and re­li­a­­bil­i­­ty work, and cur­rent SVN (r476) is ac­­tu­al­­ly quite a bit nicer than 0.2.10 al­ready. So there may be an­oth­er re­lease soon­ish.

uRSSus 0.2.10 is out!

Yes friend­s, my desk­top feed agre­ga­tor uRSSus has a brand new re­lease.

You can get it at http://urssus.­google­code.­com or http://pyp­i.python.org or via easy_in­stal­l.

There is a rather large bug in that the con­fig­u­ra­tion di­a­log may or may not work for you. Don't wor­ry, there is no life-or-death op­tion there, just ig­nore it.

As al­ways, I am look­ing for a hand mak­ing it work well on Win­dows and Mac.

Let me know if you like it (or if you don't).

uRSSus: now with configuration dialog goodness!

I felt that a "user-friend­ly" con­fig­u­ra­tion di­a­log for uRSSus was not a good way to spend ef­fort, be­cause I ex­pect op­tions to change of­ten for a while, and I did­n't want an out­dat­ed di­alog, ei­ther. The so­lu­tion? Au­to­gen­er­ate a medi­ocre con­fig­u­ra­tion di­alog!

First, de­fine what op­tions uRSSus sup­ports (Up­date: sug­ges­tion by Ale­jan­dro Cu­ra, us­ing tu­ples works much bet­ter!):

options = (
  ('ui',
    (
      ('alwaysShowFeed',  ('bool', False, "Always show a link to the post's feed when diplaying a post")),
      ('hideOnTrayClick', ('bool', True, "Hide the main window when clicking on the tray icon")),
    )
  ),
  ('options',
    (
      ('defaultRefresh'      ,  ('int', 1800, "How often feeds should be refreshed by default (in seconds).", 300, None )),
      ('maxPostsDisplayed'   ,  ('int', 1000, "Limit the display to this many posts. If set too high, opening 'All Feeds' may take forever", 0, None)),
      ('defaultExpiration'   ,  ('int',    7, "How long should articles be kept by default. (In days)", 0, 9999)),
    )
  ),
  ('twitter',
    (
      ('username',  ('string', None, 'Your Twitter user name.' )),
      ('password',  ('password', None, 'Your Twitter password.' )),
    )
  )
)

Then a lit­tle mag­ic, and this comes out:

urssus13

How mag­ic? This mag­ic:

class ConfigDialog(QtGui.QDialog):
  def __init__(self, parent):
    QtGui.QDialog.__init__(self, parent)
    # Set up the UI from designer
    self.ui=UI_ConfigDialog()
    self.ui.setupUi(self)
    pages=[]
    sections=[]
    self.values={}

    for sectionName, options in config.options:
      # Create a page widget/layout for this section:
      page=QtGui.QScrollArea()
      layout=QtGui.QGridLayout()
      row=-2
      for optionName, definition in options:
        row+=2
        if definition[0]=='bool':
          cb=QtGui.QCheckBox(optionName)
          cb.setChecked(config.getValue(sectionName, optionName, definition[1]))
          layout.addWidget(cb, row, 0, 1, 2)
          self.values[sectionName+'/'+optionName]=[cb, lambda(cb): cb.isChecked()]


        elif definition[0]=='int':
          label=QtGui.QLabel(optionName+":")
          label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
          spin=QtGui.QSpinBox()
          if definition[3] is not None:
            spin.setMinimum(definition[3])
          else:
            spin.setMinimum(-99999)
          if definition[4] is not None:
            spin.setMaximum(definition[4])
          else:
            spin.setMaximum(99999)
          spin.setValue(config.getValue(sectionName, optionName, definition[1]))
          layout.addWidget(label, row, 0, 1, 1)
          layout.addWidget(spin, row, 1, 1, 1)
          self.values[sectionName+'/'+optionName]=[spin, lambda(spin): spin.value()]

        elif definition[0]=='string':
          label=QtGui.QLabel(optionName+":")
          label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
          text=QtGui.QLineEdit()
          text.setText(config.getValue(sectionName, optionName, definition[1]))
          layout.addWidget(label, row, 0, 1, 1)
          layout.addWidget(text, row, 1, 1, 1)
          self.values[sectionName+'/'+optionName]=[text, lambda(text): unicode(text.text())]

        elif definition[0]=='password':
          label=QtGui.QLabel(optionName+":")
          label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
          text=QtGui.QLineEdit()
          text.setEchoMode(QtGui.QLineEdit.Password)
          text.setText(config.getValue(sectionName, optionName, definition[1]))
          layout.addWidget(label, row, 0, 1, 1)
          layout.addWidget(text, row, 1, 1, 1)
          self.values[sectionName+'/'+optionName]=[text, lambda(text): unicode(text.text())]

        help=QtGui.QLabel(definition[2])
        help.setWordWrap(True)
        layout.addWidget(help, row, 2, 1, 1)
        separator=QtGui.QFrame()
        separator.setFrameStyle(QtGui.QFrame.HLine|QtGui.QFrame.Plain)
        layout.addWidget(separator, row+1, 0, 1, 3)
      page.setLayout(layout)
      pages.append(page)
      sections.append(sectionName)

    for page, name in zip(pages,sections) :
      # Make a tab out of it
      self.ui.tabs.addTab(page, name)
    self.ui.tabs.setCurrentIndex(1)
    self.ui.tabs.removeTab(0)

My first ev­er thing that I just don't see how it would work with­out a lamb­da ;-)


Contents © 2000-2024 Roberto Alsina