SaltyCrane Blog — Notes on JavaScript and web development

.emacs

(custom-set-variables
  ;; custom-set-variables was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(c-basic-offset 3)
 '(column-number-mode t)
 '(cua-mode t nil (cua-base))
 '(scroll-bar-mode nil)
 '(tool-bar-mode nil)
 '(transient-mark-mode t))

;;================================================
;; server mode
(server-start)

;; custimizations
(setq inhibit-startup-message t)
(setq default-truncate-lines t)
(setq default-tab-width 4)
(setq dabbrev-case-replace nil)     ; make sure case is preserved when expanding

;; from http://geosoft.no/development/emacs.html
(setq search-highlight           t) ; Highlight search object
(setq query-replace-highlight    t) ; Highlight query object
(setq mouse-sel-retain-highlight t) ; Keep mouse high-lightening 

;;================================================
;; Behaviour
;; from http://www.postulate.org/emacs.php

;; don't automatically add new lines when scrolling down at
;; the bottom of a buffer
(setq next-line-add-newlines nil)

;; scroll just one line when hitting the bottom of the window
(setq scroll-step 1)
(setq scroll-conservatively 1)

;; move around a little 
(defun cursor-up-slightly () (interactive) (previous-line 7))
(defun cursor-down-slightly () (interactive) (next-line 7))
(global-set-key [C-down] 'cursor-down-slightly)
(global-set-key [C-up] 'cursor-up-slightly)

(defun scroll-up-slightly () (interactive) (scroll-up 7))
(defun scroll-down-slightly () (interactive) (scroll-down 7))
(global-set-key [M-down] 'scroll-down-slightly)
(global-set-key [M-up] 'scroll-up-slightly)

(defun scroll-right-slightly () (interactive) (scroll-right 5))
(defun scroll-left-slightly () (interactive) (scroll-left 5))
(global-set-key [M-right] 'scroll-right-slightly)
(global-set-key [M-left] 'scroll-left-slightly)

;; do scroll horizontally when at edge of screen
(setq auto-hscroll-mode t)
(setq hscroll-margin 0)

;;================================================
;; key bindings
(global-set-key "\C-f" 'isearch-forward)
(global-set-key "\C-s" 'save-buffer)
(global-set-key "\C-w" 'kill-this-buffer)
(global-set-key [C-prior] 'previous-buffer) ; that's CTRL+PGUP
(global-set-key [C-next] 'next-buffer)     ; that's CTRL+PGDOWN
(global-set-key "\M- " 'dabbrev-expand)

;;================================================
;; cc mode
(setq c-default-style '((c-mode . "bsd")))

;;================================================
;; setup frame position and size
(setq initial-frame-alist
   '((top . 0) (left . 600)
  (width . 80) (height . 60)))

(setq default-frame-alist
   '((top . 0) (left . 0)
  (width . 80) (height. 40)
  )
)

7 Eclipse Killer Features; 5 Eclipse Annoyances

Here are my pros and cons after using Eclipse for about a year. I've been using mostly 3.2 and recently updated to version 3.3. For the most part it is great. There are some annoyances that hopefully can be fixed. Maybe I need to learn Java and start working on the code. I've also thought about switching to Emacs. I used Emacs for a short time on a Solaris box and liked it a lot. I can't seem to make the switch now, though, because a.) it's not pretty enough, and b.) I don't want to take the time to learn it again.

Eclipse Killer Features:

  • Search across entire workspace for selected text with CTRL+ALT+G. It's the fastest interface I've used for selected text, it organizes the search results, highlights all items, and shows pretty markers in the margin. (see below.)
  • Pretty markers to show search items, compile errors/warnings, modified text, bookmarks, etc.
  • Code completion with CTRL+SPACE. It is pretty.
  • Automatic variable renaming with ALT+SHIFT+R. It's faster and more reliable than search and replace.
  • Good keyboard support. "Quick Access" is a very nice addition in Eclipse 3.3. Hit CTRL+3 and then type the name of any Eclipse command. (See Quick Access (Ctrl+3) is bliss! for more info.) To open a file, hit CTRL+SHIFT+R and type in the name of the file. The dialog provides filename completion. Fast.
  • Good integration with SVN using Subclipse: allows me to compare revisions, view the repository history, open an old revision next to my working copy, and take a glance at pretty file icons to tell me the state of files. (This no longer applies since I switched to Mercurial.)
  • Put views almost anywhere you want (docked, detached, fast view). Switch between editors with CTRL+F6, switch between views with CTRL+F7, and switch between perspectives with CTRL+F7.

Eclipse Annoyances:

  • Can't open a file from the command line.
  • It is slow. In this article, Stevey Yegge even wrote a Eclipse haiku about it:

    startApplication()
    thenWaitFriggingForever()
    thenItGoesRealSlow()

    It's a great article, btw.
  • All the extra decorations take up a lot of screen real estate. (Can't completely customize everything. E.g. can't get rid of the scroll bars.)
  • No macros. (This is what Emacs is all about.)
  • No (good) Mercurial plugin.

Eclipse vs. Emacs conclusion: Sticking with Eclipse for now because it is prettier (and because I know how to use it.)

Eclipse plug-in update sites

Europa: http://download.eclipse.org/releases/europa/
Pydev: http://pydev.sourceforge.net/updates/
Anyedit: http://andrei.gmxhome.de/eclipse/
Columns: http://columns4eclipse.sourceforge.net/updates
Subclipse: http://subclipse.tigris.org/update_1.2.x

Eclipse 3.3 cannot open a file from the command line

I want to type "eclipse myfile" from the command line and have eclipse openmyfile. As of version 3.3, Eclipse can't do this. See this post on the eclipse mailing list and bug report 60289.
 
I like Eclipse a lot, but am considering using emacs. It would be nice if there were a fast, keyboard driven editor/IDE with *functional* graphical decorations. Graphical icons are useful, but it shouldn't mean that the application is slow and requires a mouse.
 
sidenote: ratpoison with Cgywin/X, screen, rxvt, and bash are working great.

UPDATE 7/23/07: Padclipse is a lightweight editor based on Eclipse that allows you to open a file from the command line. You can add plug-ins to Padclipse like Eclipse, however, I could not get all of the Eclipse functionality to work.

How to install ratpoison with Cygwin

Running the minimalist ratpoison window manager with Cygwin/X works. You will need to install the following Cygwin packages:

gcc-core
make
readline
xorg-x11-base
xorg-x11-devel

Here is how to install:
  1. Download ratpoison
  2. $ tar xvfz ratpoison-1.4.2.tar.gz
    $ cd ratpoison-1.4.2
    $ ./configure
    $ make
    $ make install
  3. Put the following lines in your startxwin.bat file (and remove the other old %RUN% commands):
    %RUN% XWin -rootless -clipboard -silent-dup-error
    %RUN% /usr/local/bin/ratpoison
  4. Run the startxwin.bat file

UPDATE 11/08/2007:

When I installed this the first time, I had no problems. The second time I installed, after getting a new PC, I got the following error message when running configure:

configure: error: *** Can't find X11 headers and libs
I thought the reason was because I forgot to install the xorg-x11-devel package which contains the X11 headers. But even after installing it, I still got the same error. I could not figure out the reason why configure was not finding the headers and libs. So after searching the GNU autoconf manual, I found a way to specify the X11 headers and libs on the command line.
./configure --x-includes=/usr/X11R6/include/X11/ --x-libraries=/usr/X11R6/lib/
This did the trick. I'm not sure why it worked the first time and not the second time. See the GNU Autoconf manual for more information about the configure script.

See also Cygwin, X, ratposoin, screen, rxvt setup

.screenrc and .Xdefaults (for rxvt)

Use SHIFT+PGUP and SHIFT+PGDOWN to scroll in rxvt. Here are my .Xdefaults and .screenrc config files. A lot of the .screenrc stuff is from Mark Pilgrim. I don't use his "nowplaying" script, but the caption seems to still work.
 
! ~/.Xdefaults
Rxvt*background: #000033
Rxvt*foreground: #ffffcc
Rxvt*font: 14
Rxvt*geometry: 80x40
Rxvt*jumpScroll: True
Rxvt*scrollBar: False
Rxvt*scrollTtyOutput: False
Rxvt*scrollTtyKeypress: True
 
# ~/.screenrc
shell $SHELL
 
# scrollback
defscrollback 5000
 
# Window numbering starts at 1
bind c screen 1
bind 0 select 10
screen 1
 
# Set the caption on the bottom line:
# window-list <nowplaying> <mm/dd/yy> <HH:MM>am/pm
# from http://diveintomark.org/public/2007/03/screenrc
caption always "%{= kw}%-w%{= BW}%n %t%{-}%+w %-= %1` %m/%d/%y %C:%s%a"
 
# use backtick for prefix key instead of Ctrl-A
escape ``
 
# use regular audible bell
vbell off
 
# skip intro
startup_message off
 
# detach on disconnect
autodetach on

rxvt, GNU screen and bash

rxvt is a better alternative to the default Cygwin cmd.exe setup.GNU screen is a full-screen window manager that, among other things, allows you to quickly switch between shell sessions. I had some trouble figuring out how to use them together along with bash, so here are my notes on how I did it.
 
In your Cygwin Windows shortcut put:
Target:C:\cygwin\bin\rxvt.exe -e bash --login -i
Start in:c:\cygwin\bin
At the end of your ~/.bash_profile, put the following line:
screen bash
Also, in your ~/.screenrc file (if you have one), make sure your shell command does not have a dash "-" in front. The dash in front will make screen treat the shell as a login shell, executing your "screen" command in your .bash_profile recursively. (see this thread on the zsh mailing list).
 
As a side note, I'm also trying out rsync. After only a small amout of use, I tend to agree with others that it rocks.

More PyQt example code

Here is some more PyQt example code.
################################################################
def main():
    app = QApplication(sys.argv)
    mw = MainWindow()
    sys.exit(app.exec_())

################################################################
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        # create stuff
        self.rw = ReportWidget()
        self.setCentralWidget(self.rw)
        self.sw = StartWindow()
        self.createActions()
        self.createMenus()
        self.createStatusBar()

        # create progress bar
        self.pb = QProgressBar(self.statusBar())
        self.statusBar().addPermanentWidget(self.pb)

        # connections
        self.connect(self.sw, SIGNAL("okClicked"),
                    self.rw.create)
        self.connect(self.rw.table, SIGNAL("progressChanged"),
                     self.update_progress)
        self.connect(self.rw.table, SIGNAL("displayFinished"),
                     self.hide_progress_bar)

        # format the main window
        self.setGeometry(100,100,750,550)

        # show windows
        self.show()
        self.sw.show()

    def update_progress(self, n, nrows):
        self.pb.show()
        self.pb.setRange(0, nrows)
        self.pb.setValue(n)
        self.statusBar().showMessage(self.tr("Parsing eventlog data..."))

    def hide_progress_bar(self):
        self.pb.hide()
        self.statusBar().showMessage(self.tr("Finished"))

    def about(self):
        QMessageBox.about(self, self.tr("About AIS Audit Tool"),
            self.tr("AIS Audit Tool\n\n"
                    "%s\n"
                    "%s\n"
                    "%s" % (__author__, __version__, __date__)))

    def createActions(self):
        self.exitAct = QAction(self.tr("E&xit;"), self)
        self.exitAct.setShortcut(self.tr("Ctrl+Q"))
        self.exitAct.setStatusTip(self.tr("Exit the application"))
        self.connect(self.exitAct, SIGNAL("triggered()"), self, SLOT("close()"))

        self.aboutAct = QAction(self.tr("&About;"), self)
        self.aboutAct.setStatusTip(self.tr("Show the application's About box"))
        self.connect(self.aboutAct, SIGNAL("triggered()"), self.about)

        self.aboutQtAct = QAction(self.tr("About &Qt;"), self)
        self.aboutQtAct.setStatusTip(self.tr("Show the Qt library's About box"))
        self.connect(self.aboutQtAct, SIGNAL("triggered()"), qApp, SLOT("aboutQt()"))

    def createMenus(self):
        self.fileMenu = self.menuBar().addMenu(self.tr("&File;"))
        self.fileMenu.addAction(self.exitAct)

        self.helpMenu = self.menuBar().addMenu(self.tr("&Help;"))
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

    def createStatusBar(self):
        sb = QStatusBar()
        sb.setFixedHeight(18)
        self.setStatusBar(sb)
        self.statusBar().showMessage(self.tr("Ready"))

################################################################
class StartWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)

        # date box
        self.label_date = QLabel()
        self.label_date.setText("Set date of last audit:")
        default = datetime.date.today() - datetime.timedelta(DEFAULT_DAYS_FROM_LAST_AUDIT)
        self.datebox = QDateEdit(QDate(default.year, default.month, default.day))

        # buttons
        spacer = QSpacerItem(20,40,QSizePolicy.Minimum,QSizePolicy.Expanding)
        self.button_ok = QPushButton()
        self.button_ok.setText("OK")
        self.button_ok.setDefault(True)
        button_cancel = QPushButton()
        button_cancel.setText("Cancel")

        # layout
        layout_right = QVBoxLayout(self)
        layout_right.addWidget(self.label_date)
        layout_right.addWidget(self.datebox)
        layout_right.addItem(spacer)
        layout_right.addWidget(self.button_ok)
        layout_right.addWidget(button_cancel)

        # connections
        self.connect(button_cancel, SIGNAL("clicked(bool)"),
                    self.close)
        self.connect(self.button_ok, SIGNAL("clicked(bool)"),
                    self.ok_clicked)

    def ok_clicked(self):
        self.close()
        year = self.datebox.date().year()
        month = self.datebox.date().month()
        day = self.datebox.date().day()
        dateobj = datetime.date(int(year),int(month),int(day))
        self.emit(SIGNAL("okClicked"), dateobj)

################################################################
class ReportWidget(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)
        self.cbUsers = QCheckBox("Hide SYSTEM users")
        self.cbSorting = QCheckBox("Sorting enabled")
        self.table = MyTable()
        self.textbrowser = QTextBrowser()
        self.textbrowser.setFontFamily("Courier")
        self.textbrowser.setFontPointSize(10)
        hlayout = QHBoxLayout()
        hlayout.addWidget(self.cbUsers)
        hlayout.addWidget(self.cbSorting)
        vlayout = QVBoxLayout()
        vlayout.setMargin(2)
        vlayout.addLayout(hlayout)
        vlayout.addWidget(self.table)
        self.setLayout(vlayout)
        self.setGeometry(100,100,750,550)

        # connections
        self.connect(self.cbUsers, SIGNAL("stateChanged(int)"),
                     self.cbUsersChanged)
        self.connect(self.cbSorting, SIGNAL("stateChanged(int)"),
                     self.cbSortingChanged)

    def create(self, dateobj):
        """ Parses the eventlog data, displays it in a table, and
            displays the user login/logout also """
        self.table.display_data(dateobj)

    def cbUsersChanged(self):
        state = self.cbUsers.checkState()
        if state == 0:
            self.table.show_system_users()
        elif state == 2:
            self.table.hide_system_users()

    def cbSortingChanged(self):
        state = self.cbSorting.checkState()
        if state == 0:
            self.table.setSortingEnabled(False)
        elif state == 2:
            self.table.setSortingEnabled(True)

################################################################
class MyTable(QTableWidget):
    """ Creates a custom table widget """
    def __init__(self, *args):
        QTableWidget.__init__(self, *args)
        self.setSelectionMode(self.ContiguousSelection)
        self.setGeometry(0,0,700,400)
        self.setShowGrid(False)
        self.other_users_list = []

    def hide_system_users(self):
        for n in self.other_users_list:
            self.setRowHidden(n, True)

    def show_system_users(self):
        for n in self.other_users_list:
            self.setRowHidden(n, False)

    def display_data(self, dateobj):
        """ Reads in data as a 2D list and formats and displays it in
            the table """

        print "Fetching data..."
        ep = EventlogParser()
        data = ep.parse_log(dateobj)
        print "Done."

        if len(data)==0:
            data = ["No data for this date range."]

        nrows = len(data)
        ncols = len(data[0])
        self.setRowCount(nrows)
        self.setColumnCount(ncols)
        self.setHorizontalHeaderLabels(['No.', 'Date','Time','Type','Event','User','Computer'])

        for i in xrange(len(data)):
            # update progress dialog
            if (i%20) == 0:
                self.emit(SIGNAL("progressChanged"), i, nrows)
                qApp.processEvents()

            # set each cell to be a QTableWidgetItem from the _process_row method
            items = self._process_row(data[i])
            for j in range(len(items)):
                self.setItem(i, j, items[j])
            self.setRowHeight(i, 16)

            # set column width first time through
            if i == 0:
                self.resizeColumnsToContents()
                self.setColumnWidth(4, 250)

        # format column width
        self.resizeColumnsToContents()
        self.setColumnWidth(4, 250)

        # emit signal for finished processing
        self.emit(SIGNAL("displayFinished"))

    def _process_row(self, row):
        """ Formats items in the row of the 2-D list data
            Input: the row of data from the EventlogParser in a list
            Returns a list of QTableWidgetItems to be one row in the table
        """

        icon = []
        for i in xrange(len(row)):
            # general formatting for all cells (may be overwritten)
            icon.append(QIcon())
            computer = row[6]

            # time processing
            if i == 2:
                try:
                    hour = int(re.split(r":", row[i])[0])
                except:
                    raise
                if hour <= EARLY_HOUR or hour >= LATE_HOUR:
                    backcolor_time = QColor(0,0,102)
                else:
                    backcolor_time = QColor("white")

            # success or failure processing
            elif i == 3:
                if row[i] == "8":
                    row[i] = "Success"
                    icon[i] = QIcon("success.png")
                elif row[i] == "16":
                    row[i] = "Failure"
                    icon[i] = QIcon("failure.png")
                else:
                    row[i] = "Unknown"
                    icon[i] = QIcon("unknown.png")

            # event processing
            elif i == 4:
                backcolor = QColor("white")
                if row[i] in RED_EVENTIDS:
                    backcolor = QColor("red")
                elif row[i] in ORANGE_EVENTIDS:
                    backcolor = QColor("orange")
                elif row[i] in YELLOW_EVENTIDS:
                    backcolor = QColor("yellow")
                elif row[i] in GREEN_EVENTIDS:
                    pass
                elif row[i] in OTHER_EVENTIDS:
                    backcolor = QColor("blue")
                try:
                    row[i] = row[i] + ": " + EVENT_DESC[row[i]]
                except:
                    pass

            # user processing
            elif i == 5:
                if row[i] in (computer, "", "SYSTEM", "NETWORK SERVICE", "LOCAL SERVICE", "ANONYMOUS LOGON"):
                    font = QFont("Arial", 8)
                    font.setBold(False)
                    textcolor = QColor("gray")
                    user = 'other'
                else:
                    font = QFont("Arial", 8)
                    font.setBold(True)
                    textcolor = QColor("black")
                    user = 'user'

        # create table widget item
        tableitem_list = []
        for i in xrange(len(row)):
            tableitem = QTableWidgetItem(row[i])
            if i == 2:
                tableitem.setBackgroundColor(backcolor_time)
            else:
                tableitem.setBackgroundColor(backcolor)
            tableitem.setTextColor(textcolor)
            tableitem.setFont(font)
            tableitem.setTextAlignment(Qt.AlignTop)
            tableitem.setToolTip(row[i])
            tableitem.setIcon(icon[i])
            tableitem_list.append(tableitem)

        return tableitem_list

################################################################
if __name__ == "__main__":
    main()

How to get code completion for PyQt using Pydev

Because PyQt doesn't come with the .py source files which Pydev needs for code completion, you need to add the PyQt modules as a "forced builtin lib". See the following FAQs at the pydev website: http://pydev.sourceforge.net/faq.html#ref_22 and http://pydev.sourceforge.net/faq.html#ref_15
 
Here are the steps:
  1. Go to "Window" -> "Preferences..." -> "Pydev" -> "Interpreter - Python"
  2. In the "Forced builtin libs" section, click "New..."
  3. Type in "PyQt4" and click "OK".
  4. Click "OK" to close the Preferences window.
Note: If you installed PyQt after installing Pydev, you will probably have to update the PYTHONPATH with the path to the PyQt libraries. You can do this easily by "Remove"ing the python interpreter and then re-adding it in again. (For reference, I am using Eclipse 3.2.2, Pydev 1.3.4, Python 2.5.1, and PyQt 4.2.3)

More example code for PyQt 4.2

Here is some more example code for PyQt 4.2.

################################################################
################################################################
def main():
    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(True)
    form = StartForm()
    form.show()
    report = ReportWindow()
    app.connect(form, SIGNAL("okClicked"),
                report.create)
    sys.exit(app.exec_())

################################################################
################################################################
class StartForm(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)

        # position the form on the screen
        self.move(1500, 50)

        # date box
        self.label_date = QLabel()
        self.label_date.setText("Set date of last audit:")
        default = datetime.date.today() - datetime.timedelta(DEFAULT_DAYS_FROM_LAST_AUDIT)
        self.datebox = QDateEdit(QDate(default.year, default.month, default.day))

        # buttons
        spacer = QSpacerItem(20,40,QSizePolicy.Minimum,QSizePolicy.Expanding)
        self.button_ok = QPushButton()
        self.button_ok.setText("OK")
        self.button_ok.setDefault(True)
        button_cancel = QPushButton()
        button_cancel.setText("Cancel")

        # layout
        layout_right = QVBoxLayout(self)
        layout_right.addWidget(self.label_date)
        layout_right.addWidget(self.datebox)
        layout_right.addItem(spacer)
        layout_right.addWidget(self.button_ok)
        layout_right.addWidget(button_cancel)

        # connections
        self.connect(button_cancel, SIGNAL("clicked(bool)"),
                    self.close)
        self.connect(self.button_ok, SIGNAL("clicked(bool)"),
                    self.ok_clicked)

    def ok_clicked(self):
        self.label_date.setText("Getting eventlog data...")
        year = self.datebox.date().year()
        month = self.datebox.date().month()
        day = self.datebox.date().day()
        delta = datetime.date.today() - datetime.date(int(year),int(month),int(day))
        self.emit(SIGNAL("okClicked"), delta.days)
        self.close()

################################################################
################################################################
class ReportWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)
        self.cbUsers = QCheckBox("Hide SYSTEM users")
        self.cbSorting = QCheckBox("Sorting enabled")
        self.tableview = EventlogTableView()

        vlayout = QVBoxLayout()
        vlayout.addWidget(self.cbUsers)
        vlayout.addWidget(self.cbSorting)
        vlayout.addWidget(self.tableview)
        self.setLayout(vlayout)
        self.setGeometry(100,100,750,550)

        # connections
        self.connect(self.cbUsers, SIGNAL("stateChanged(int)"),
                     self.cbUsersChanged)
        self.connect(self.cbSorting, SIGNAL("stateChanged(int)"),
                     self.cbSortingChanged)

    def create(self, ndays):
        """ Run dumpel.exe, parse and show the results in a table """
        ep = EventlogParser()
        logdata = ep.parseLog(ndays)
        model = EventlogTableModel(logdata, self)
        self.tableview.setModel(model)
        self.tableview.formatData()
        self.show()

    def cbUsersChanged(self):
        state = self.cbUsers.checkState()
        print "state= " + str(state)
        if state == 0:
            self.table.show_system_users()
        elif state == 2:
            self.table.hide_system_users()

    def cbSortingChanged(self):
        state = self.cbSorting.checkState()
        if state == 0:
            self.table.setSortingEnabled(False)
        elif state == 2:
            self.table.setSortingEnabled(True)

################################################################
################################################################
class EventlogTableView(QTableView):
    def __init__(self, *args):
        QTableView.__init__(self, *args)

    def formatData(self):
        """ Formats the data in the table view """
        self.resizeColumnsToContents()

        # format items
        model = self.model()
        self.nrows = model.rowCount(self)
        self.ncols = model.columnCount(self)
        for i in xrange(self.nrows):
            for j in xrange(self.ncols):
                index = model.index(i, j)

################################################################
################################################################
class EventlogTableModel(QAbstractTableModel):
    def __init__(self, datain, parent=None):
        QAbstractTableModel.__init__(self, parent)
        self.logdata = datain

    def rowCount(self, parent):
        return len(self.logdata)

    def columnCount(self, parent):
        return len(self.logdata[0])

    def data(self, index, role):
        if not index.isValid():
            return QVariant()
        elif role != Qt.DisplayRole:
            return QVariant()
        return QVariant(self.logdata[index.row()][index.column()])

################################################################
################################################################
class EventlogParser:
    def __init__(self):
        pass

    def parseLog(self, ndays):
        """ Runs dumpel.exe and stores the results in a data structure
            Example dumpel output:
                3/14/2006    1:00:51 PM    8    1    515    Security    NT AUTHORITY\SYSTEM        ANC9PLT561    Win
            Returns a 2-dimensional list
        """
        os.system("dumpel -f dumpel_results.txt -l security -d %s" % ndays)
        fin = open("dumpel_results.txt", "r")
        data = []
        for line in fin.readlines():
            items = re.split(r'\t+', line, maxsplit=8)

            # date processing
            items[0] = format_datetime(items[0], items[1])

            # success/failure processing
            if items[2] == "8":
                items[2] = "Success"
            elif items[2] == "16":
                items[2] = "Failure"
            else:
                items[2] = "Unknown"

            # event processing
            event = items[4]
            try:
                desc = event_desc[event]
            except:
                desc = 'need to look up this event id'
            items[4] = event + ": " + desc

            # user processing
            (dom, user) = re.split(r"\\", items[6])
            items[6] = user

            # delete unused columns
            items[7:8] = []
            items[5:6] = []
            items[3:4] = []

            data.append(items)
        print "Parse sucessful.\n"
        return data