R/importWizard.R

Defines functions getName4Data getMoreArgs whatDeli setColType setColName dropColumn writeCol4Matrix setState3BFrame moreArgs setState3TFrame getState3Frame showData4State2 setState2BFrame setQuote setQuoteList setSepRadios setState2MFrame getState2Frame setSkip popStartLine setState1MFrame showData4State1 setState1TFrame setState1BFrame getState1Frame finish getAFrame dropArgs addArgs setNewState changeState getTopCan initImportWizard setColInfos getColInfo assignColInfo getCState assignCState getShowNum assignShowNum getArgs assignArgs getLineData assignLineData setArgsList readFileByLines importWizard

Documented in addArgs assignArgs assignColInfo assignCState assignLineData assignShowNum changeState dropArgs dropColumn finish getAFrame getArgs getColInfo getCState getLineData getMoreArgs getName4Data getShowNum getState1Frame getState2Frame getState3Frame getTopCan importWizard initImportWizard moreArgs popStartLine readFileByLines setArgsList setColInfos setColName setColType setNewState setQuote setQuoteList setSepRadios setSkip setState1BFrame setState1MFrame setState1TFrame setState2BFrame setState2MFrame setState3BFrame setState3TFrame showData4State1 showData4State2 whatDeli writeCol4Matrix

# This function provides data import interfaces by mimicing MS Excel's
# Text Wizard. read.table will be used to import the data.
#
# filename - an optional character string for the name of the file to
# be imported
# maxRow - an integer for the maximum number of rows to be
# displayed. Large row numbers may slow down the machine
#
# Copyright 2002, J. Zhang, all rights reserved.

importWizard <- function(filename = NULL, maxRow = 400){
    argsSet <- FALSE
    # Creates an environment to make some variables available to all
    # related functions
    workEnv <- new.env(hash = TRUE, parent = emptyenv())
    # A string to keep track of the current state
    assignCState("state1", env = workEnv)

    if(!is.null(filename)){
        lineNums <- length(readFileByLines(filename))
        if(!is.null(lineNums)){
            argsSet <- setArgsList(filename, workEnv)
        }
    }else{
        # Otherwise, assign an empty list to argsList and colInfo
        assignArgs(list(), workEnv)
        #setColInfos(env = workEnv)
        lineNums <- maxRow
        argsSet <- TRUE
    }
    if(argsSet){
        # Number of row to be displayed
        assignShowNum(ifelse(maxRow > lineNums, lineNums,
                             maxRow), env = workEnv)
        # Initializes the interface
        initImportWizard(workEnv)
    }
}

readFileByLines <- function(filename){
    conn <- safeFileOpen(filename)
    if(inherits(conn, "connection")){
        lines <- readLines(conn)
        close(conn)
        return(lines)
    }else{
        tkmessageBox(title = "IO Error",
                     message = paste("can't open file because", conn),
                     icon = "error", type = "ok")
        return(NULL)
    }
}
# Using function guess.sep to figure out the the header, sep, and data
# type of a file and sets the argument list and colInfo
setArgsList <- function(filename, env, isFile = TRUE, init = TRUE){
    options(show.error.messages = FALSE, warn = -1)
    fileInfo <- try(guess.sep(file.name = filename, numLine = 40,
                              isFile = isFile))
    options(show.error.messages = TRUE, warn = 0)
    if(inherits(fileInfo, "try-error")){
        tkmessageBox(title = "Incorrect File Name",
                 message = paste("An error message:\n\n", fileInfo,
                           "\nwas generated while reading file",
                           filename, "."), icon = "error", type = "ok")
        return(FALSE)
    }else{
        env$rowNameCol <- 0
        if(init){
            argsList <- list()
            temp <- formals("read.table")
            temp[["file"]] <- filename
            temp[["header"]] <- fileInfo[["header"]]
            temp[["sep"]] <- fileInfo[["separator"]]
            #chrNBothtemp[["quote"]] <- ""
            # Reassign fill with the value of blank.lines.skip
            temp[["fill"]] <- !temp[["blank.lines.skip"]]
            argsList[["state1"]] <- as.list(temp)
        }else{
            argsList <- getArgs(env)
            argsList[["state1"]][["header"]] <- fileInfo[["header"]]
            argsList[["state1"]][["sep"]] <- fileInfo[["separator"]]
        }
        assignArgs(argsList, env)
        #setColInfos(fileInfo[["type"]], env)
        if(isFile){
            fileLines <- readFileByLines(filename)
            if(!is.null(fileLines)){
                assignLineData(fileLines[1:min(length(fileLines),
                                               getShowNum(env))], env)
                assignShowNum(min(length(fileLines), getShowNum(env)), env)
            }else{

            }
        }else{
            assignLineData(filename, env)
        }
        return(TRUE)
    }
}
# Set the temp data read as lines with a maxmun number
assignLineData <- function(lineData, env){
    assign("lineData", lineData, env = env)
}
# Get the temp data stroed as lines with a maxmun number
getLineData <- function(env){
    env$lineData
}
# Set and get methods for argument list
assignArgs <- function(value, env){
    assign("argsList", value, env = env)
}
getArgs <- function(env){
    env$argsList
}
# Set and get methods for number to show in the interface
assignShowNum <- function(value, env){
    assign("showNum", value, env)
}
getShowNum <- function(env){
    env$showNum
}
# Set and get methods for current state to keep track of the state
assignCState <- function(value, env){
    assign("currentState", value, env)
}
getCState <- function(env){
    env$currentState
}

# Set and get methods for colInfo that is a list of colInfo objects to
# keep column name, type, and drop info
assignColInfo <- function(value, env){
    assign("colInfos", value, env)
}
getColInfo <- function(env){
    env$colInfos
}
# Creates colInfo objects and sets the value of 'colInfos' list
setColInfos <- function(types, env){
#    initColInfo()
    if(missing(types)){
        assignColInfo(list(), env)
    }else{
        temp <- list()
        for(i in 1:length(types)){
            # name = "", type = types[i], dropOrNot = FALSE
            temp[[i]] <- colInfo("", types[i], FALSE)
        }
        assignColInfo(temp, env)
    }
}
# This function initializes the interface for importWizard by creating
# a widget with an empty top canvas and bottom frame filled with four buttons
initImportWizard <- function(env){
    # A list to be returned that contains an argument list and data
    # imported using read.table
    dataList <- NULL
    # A variable to keep the frame that is currently displayed
    #currentFrame <- NULL

    on.exit(end())
    # Destroy the window
    end <- function(){
        tkgrab.release(top)
        tkdestroy(top)
    }

    nextState <- function(){
        args <- getArgs(env)
        if(is.null(args[["state1"]][["file"]])){
            tkmessageBox(title = "Import Error",
                         message = "I do not know what file to import!",
                         icon = "error",
                         type = "ok")
        }else{
            tempFrame <- changeState(canvas, backBut, nextBut, env, TRUE,
                                 endBut, viewBut)
            tkdestroy(currentFrame)
            tkpack(tempFrame, fill = "both", expand = TRUE)
            currentFrame <<- tempFrame
        }
    }
    preState <- function(){
        tempFrame<- changeState(canvas, backBut, nextBut, env, FALSE,
                                endBut, viewBut)
        tkdestroy(currentFrame)
        tkpack(tempFrame, fill = "both", expand = TRUE)
        currentFrame <<- tempFrame
    }
    redraw <- function(){
        tempFrame <- getAFrame(canvas, env)
        tkdestroy(currentFrame)
        tkpack(tempFrame, fill = "both", expand = TRUE)
        currentFrame <<- tempFrame
    }
    finishClicked <- function(){
        dataList <<- finish(env)
        if(!is.null(dataList)){
            end()
        }
    }

    ## Set up the interface
    top <- tktoplevel()
    tktitle(top) <- "BioC Data Import Wizard"
    # Set the empty canvas that will be filled later
    canvas <- getTopCan(top, env)
    # Sets current frame to state1 now
    currentFrame <- getAFrame(canvas, env)
    tkpack(currentFrame, fill = "both", expand = TRUE)
    #tkcreate(canvas, "window", 0, 0, anchor = "nw", window = currentFrame)
    ## The bottom frame contains the buttons that allow users to
    ## navigate the importing process
    butFrame <- tkframe(top)
    viewBut <- tkbutton(butFrame, text = "View", width = 8,
                        state = "disabled", command = redraw)
    canBut <- tkbutton(butFrame, text = "Cancel", width = 8,
                       command = end)
    backBut <- tkbutton(butFrame, text = "< Back", width = 8,
                        state = "disabled", command = preState)
    nextBut <- tkbutton(butFrame, text = "Next >", width = 8,
                        command = nextState)
    endBut <- tkbutton(butFrame, text = "Finish", width = 8,
                       state = "disabled", command = finishClicked)
    tkpack(canBut, backBut, nextBut, endBut, side = "left")
    tkpack(butFrame, pady = 10, fill = "y", expand = TRUE)

    args <- getArgs(env)
    tkgrab.set(top)
    tkwait.window(top)
    return(invisible(dataList))
}
# Creates a top frame of an empty canvas
getTopCan <- function(base, env){
    WIDTH <- 730
    HEIGHT <- 400
    ## The canvas has widgets for the relevant
    ## arguments and preview of the original data. The content various
    ## depending on the state
    canvas <- makeViewer(base, vWidth = WIDTH, vHeight = HEIGHT,
                           vScroll = FALSE, hScroll = FALSE,
                           what = "canvas", side = "top")

    return(canvas)
}
# Changes the state and thus the interface
changeState <- function(canvas, backBut, nextBut, env, forward = TRUE,
                        endBut, viewBut){
    # Sets the current state
    setNewState(env, backBut, nextBut, forward, endBut, viewBut)
    if(forward){
        addArgs(env)
    }else{
        dropArgs(env)
    }
    return(getAFrame(canvas, env))
#    tkcreate(canvas, "window", 0, 0, anchor = "nw", window = newFrame)
}
# Sets the string for the new state (next or previous) and
# actviates/inactivates buttons depending on the state
setNewState <- function(env, backBut, nextBut, forward = TRUE,
                        endBut, viewBut){
    if(forward){
        if(getCState(env) == "state1"){
            assignCState("state2", env)
            tkconfigure(backBut, state = "normal")
            tkconfigure(viewBut, state = "normal")
        }else{
            assignCState("state3", env)
            tkconfigure(nextBut, state = "disabled")
            tkconfigure(endBut, state = "normal")
            tkconfigure(viewBut, state = "disabled")
        }
    }else{
        if(getCState(env) == "state2"){
            env$rowNameCol <- 0
            assignCState("state1", env)
            tkconfigure(nextBut, state = "normal")
            tkconfigure(backBut, state = "disabled")
            tkconfigure(viewBut, state = "disabled")
        }else{
            assignCState("state2", env)
            tkconfigure(nextBut, state = "normal")
            tkconfigure(endBut, state = "disabled")
            tkconfigure(viewBut, state = "normal")
        }
    }
}
# Add a new state arguments list to argsList
addArgs <- function(env){
    temp <- getArgs(env)
    if(length(temp) == 1){
        temp[["state2"]] <- temp[[length(temp)]]
        assignArgs(temp, env)
    }else{
        temp[["state3"]] <- temp[[length(temp)]]
        assignArgs(temp, env)
    }
}
# Drop a state arguments list from argsList when the back button is clicked
dropArgs <- function(env){
    temp <- getArgs(env)
    if(length(temp) > 1){
        temp <- temp[-length(temp)]
        assignArgs(temp, env)
    }
}
# Gets a frame based on which state is of interest
getAFrame <- function(base, env){
    switch(getCState(env),
           "state1" = return(getState1Frame(base, env)),
           "state2" = return(getState2Frame(base, env)),
           "state3" = return(getState3Frame(base, env)))
}
# The importing process ends. Return a list with argument list and
# data read using read.table as elements
finish <- function(env){
    args <- getArgs(env)[["state3"]]
    if(is.null(args[["quote"]])){
        args[["quote"]] <- "\"'"
    }
    dataName <- getName4Data(args[["file"]], "data frame")
    conn <- safeFileOpen(args[["file"]])
    if(inherits(conn, "connection")){
        args[["file"]] <- conn
        options(show.error.messages = FALSE)
        dataFile <- try(do.call(read.table, args))
        options(show.error.messages = TRUE)
        #close(args[["file"]])
        if(inherits(dataFile, "try-error")){
            tkmessageBox(title = "Import Error",
                     message = paste("An error message:\n\n", dataFile,
                     "\nwas generated while reading file ",
                     args[["file"]], "."), icon = "error", type = "ok")
        }else{
            if(env$rowNameCol != 0){
                rownames(dataFile) <- dataFile[, env$rowNameCol]
                dataFile <- dataFile[, - env$rowNameCol]
            }
            colInfos <- getColInfo(env)
            colNames <- NULL
            colToDrop <- NULL
            for(i in 1:length(colInfos)){
                if(dropOrNot(colInfos[[i]])){
                    colToDrop <- c(colToDrop, i)
                }else{
                    switch(colType(colInfos[[i]]),
                           "Character" = dataFile[, i] <-
                           as.character(dataFile[, i]),
                           "Numeric" = dataFile[, i] <-
                           as.numeric(dataFile[, i]))
                    colNames <- c(colNames, colName(colInfos[[i]]))
                }
            }
            # Drop the columns
            if(!is.null(colToDrop)){
                dataFile <- dataFile[, -colToDrop]
            }
            # In case there is only one column left
            if(is.null(ncol(dataFile))){
                dataFile <- data.frame(matrix(dataFile, ncol = 1))
                names(dataFile) <- colNames
            }else{
                colnames(dataFile) <- colNames
            }
            if(!is.null(dataName)){
                assign(dataName, dataFile, env = .GlobalEnv)
            }
            return(list(args = args, data = dataFile))
        }
    }else{
        tkmessageBox(title = "IO Error",
                     message = paste("Can't open file because", conn),
                     icon = "error", type = "ok")
    }
    return(NULL)
}
# Gets the frame containing the interface for the top frame of
# importWizard for state1
getState1Frame <- function(base, env){
    # A frame containing the interface that will be returned
    frame <- tkframe(base)
    # The bottom frame contains a list box showing the data. The
    # bottom frame is set first to make the list box available for
    # updating by the top frame
    bottomFrame <- tkframe(frame)
    dataViewer <- setState1BFrame(bottomFrame, env)
    # The mid frame contains the delimiter and number line
    # information.
    midFrame <- tkframe(frame)
    delims <- setState1MFrame(midFrame, env, dataViewer)
    # The top frame contains a entry box and a browse button that
    # allows for browing directories for a file name
    topFrame <- tkframe(frame)
    setState1TFrame(topFrame, dataViewer, delims, env, delims[["start"]])
    tkpack(topFrame, pady = 5, padx = 5, fill = "both", expand = TRUE)
    tkpack(midFrame, padx = 5, fill = "both", expand = TRUE)
    # Pack the bottom frame last
    tkpack(bottomFrame, pady = 5, padx = 5, fill = "both", expand = TRUE)
    return(frame)
}
# Sets the botton frame for state1
setState1BFrame <- function(frame, env){
    # A list box to show the original data
    viewFrame <- tkframe(frame)
    dataViewer <- makeViewer(viewFrame, vWidth = 50, vHeight = 10,
                            vScroll = TRUE, hScroll = TRUE,
                            what = "list", side = "top")
    tkpack(viewFrame, anchor = "w", pady = 10, fill = "both",
                                                      expand = TRUE)
    return(dataViewer)
}
# Sets the top frame for state1
setState1TFrame <- function(frame, viewer, delims, env, startList){
    fName <- tclVar()
    # Populate the entry box for file name when the brose button is
    # clicked
    browse <- function(){
        filename <- tclvalue(tkgetOpenFile())
        if(filename != ""){
            writeList(nameEntry, filename, clear = TRUE)
            argsSet <- setArgsList(filename, env)
            if(argsSet){
                showData4State1(viewer, env)
                if(!is.null(getArgs(env)[["state1"]][["sep"]])){
                    tkselect(delims[["delimit"]])
                }
                popStartLine(startList, env)
            }
        }
    }
    # Get the file
    getFile <- function(){
        argsSet <- setArgsList(tclvalue(fName), env)
        if(argsSet){
            showData4State1(viewer, env)
            if(!is.null(getArgs(env)[["state1"]][["sep"]])){
                tkselect(delims[["delimit"]])
            }
        }
    }

    # Frame to hole the widgets
    nameFrame <- tkframe(frame)
    label1 <- tklabel(nameFrame, text = "File name: ")
    tkpack(label1, side = "left")
    # An entry box to hold the result of fileBrowser
    nameEntry <- tkentry(nameFrame, width = 20, textvariable = fName)
    tkbind(nameEntry, "<KeyPress-Return>", getFile)
    # If a file name is given, fill the widget with data
    if(!is.null(getArgs(env)[["state1"]][["file"]])){
        writeList(nameEntry, getArgs(env)[["state1"]][["file"]], clear = TRUE)
        showData4State1(viewer, env)
        if(length(getArgs(env)[["state1"]][["sep"]]) != 0){
            tkselect(delims[["delimit"]])
        }
    }
    tkpack(nameEntry, side = "left", fill = "x", expand = TRUE)
    # A button to envoke fileBrowser
    browseBut <- tkbutton(nameFrame, width = 6, text = "Browse",
                          command = browse)
    getBut <- tkbutton(nameFrame, width = 6, text = "Get",
                       command = getFile)
    tkpack(browseBut, side = "left", fill = "x")
    tkpack(getBut, side = "left", fill = "x")
    tkpack(nameFrame, fill = "both", expand = TRUE)
}
# Show the data read in using readLines for state1
showData4State1 <- function(widget, env){
     #skip <- getArgs(env)[["state1"]][["skip"]]
     #if(!is.null(skip)){
         dataFile <- getLineData(env)
     #    showNum <- getShowNum(env)
     #    if(length(dataFile) > showNum){
     #        dataFile <- dataFile[(skip + 1):showNum]
     #    }else{
     #        dataFile <- dataFile[(skip + 1):length(dataFile)]
     #    }
     #}#else{
      #   dataFile <- getLineData(env)
     #}
     # Preventing the header to be shown
     if(getArgs(env)[["state1"]][["header"]]){
         dataFile <- dataFile[2:length(dataFile)]
     }
     # determines how many lines to show
     if(length(dataFile) > getShowNum(env)){
         writeList(widget, paste(1:getShowNum(env), ": ",
                             dataFile[1:getShowNum(env)], sep = ""), TRUE)
     }else{
         writeList(widget, paste(1:length(dataFile), ": ",
                                                dataFile, sep = ""), TRUE)
     }
}
# Sets the mid frame for state1
setState1MFrame <- function(frame, env, dataViewer){
    # Executed when values in start at row list box is clicked
    startClicked <- function(){
        args <- getArgs(env)
        if(length(args) != 0 && args[["state1"]][["file"]] != ""){
            setSkip(startList, env)
            #args <- getArgs(env)
            #skip <- as.numeric(as.character(tkget(startList,
            #                     tkcurselection(startList)))) - 1
            #skip <- as.numeric(args[["state1"]][["skip"]])

            #assignShowNum((getShowNum(env) + skip), env)
            #dataFile <- getLineData(env)
            #showNum <- getShowNum(env)
            #if(length(dataFile) > showNum){
            #    dataFile <- dataFile[(skip + 1):showNum]
            #}else{
            #    dataFile <- dataFile[(skip + 1):length(dataFile)]
            #}
            #setArgsList(args[["state1"]][["file"]], env, TRUE, FALSE)
            #        showData4State1(dataViewer, env)
        }
    }
    leftPan <- tkframe(frame)
    delimit <- tclVar()
    delimitRadio <- tkradiobutton(leftPan, text = paste("Delimited",
                                  " - Files are separated by a character",
                                  " such as a comma, tab ...", sep =""),
                                  value = "delim", variable = delimit,
                                  anchor = "nw")
    tkpack(delimitRadio, anchor = "w", expand = TRUE, fill = "x")
    fixedRadio <- tkradiobutton(leftPan, text = paste("Fixed",
                                " width - Fields are aligned in columns",
                                " with spaces between fields", sep = ""),
                                value = "fixed", variable = delimit,
                                anchor = "nw")
    tkpack(fixedRadio, anchor = "w", expand = TRUE, fill = "x")
    tkpack(leftPan, side = "top", anchor = "w",
           pady = 5, padx = 5, fill = "x", expand = TRUE)

    rightPan <- tkframe(frame, borderwidth = 2, relief = "groove")
    paraLabel2 <- tklabel(rightPan, text = paste("Start importing data from",
                          "line "))
    tkpack(paraLabel2, side = "left", anchor = "ne")
    startFrame <- tkframe(rightPan)
    startList <- makeViewer(startFrame, vWidth = 2, vHeight = 1,
                            what  = "list", side = "top")
    tkconfigure(startList, selectmode = "single")
    tkbind(startList, "<B1-ButtonRelease>", startClicked)
    popStartLine(startList, env)
    tkpack(startFrame, anchor = "w", side = "left",
                                          fill = "x", expand = TRUE)
    tkpack(tklabel(rightPan, text = " of the file shown below"),
           side = "left", expand = FALSE,  anchor = "ne")
    tkpack(rightPan, side = "top", padx = 5, pady = 5,
           expand = TRUE, fill = "x")
    return(list(delimit = delimitRadio, fixed = fixedRadio,
                start = startList))
}

popStartLine <- function(startList, env){
    args <- getArgs(env)
    if(length(args) != 0 && args[["state1"]][["file"]] != ""){
        writeList(startList, 1:(getShowNum(env) - 1), clear = TRUE)
        startLine <- getArgs(env)
        if(length(startLine) == 0 ||
           length(startLine[["state1"]][["skip"]]) == 0){
            line2Start <- 0
        }else{
            line2Start <- startLine[["state1"]][["skip"]]
        }
        tkselection.set(startList, line2Start)
    }
}

# Sets the value for skip when user selects line to start in
# state1
setSkip <- function(widget, env, state = "state1"){
    temp <- getArgs(env)
    temp[[state]][["skip"]] <- as.numeric(as.character(tkget(widget,
                                 tkcurselection(widget)))) - 1
    assignArgs(temp, env)
}
# Gets a frame for state2
getState2Frame <- function(base, env, state = "state2",
                           reset = FALSE){
    bottomFrame <- NULL

    applySep <- function(){
        tkdelete(dataView, "0.0", "end")
        showData4State2(dataView, env)
        #args <- getArgs(env)
        #setArgsList(args[["state2"]][["file"]], env,
        #            isFile = TRUE, init = FALSE, state = "state2")
    }
    frame <- tkframe(base)
    # Shows the name of the file
    label1 <- tklabel(frame, text = paste("File:",
                      getArgs(env)[[state]][["file"]]),
                      font = "Helvetica 11 bold")
    tkpack(label1, pady = 5, padx = 5)
    midFrame <- tkframe(frame)
    applyBut <- setState2MFrame(midFrame, env)
    tkbind(applyBut, "<B1-ButtonRelease>", applySep)
    tkpack(midFrame, pady = 5, padx = 5, fill = "x", expand = TRUE)
    bottomFrame <- tkframe(frame)
    dataView <- setState2BFrame(bottomFrame, env)
    tkpack(bottomFrame, fill = "both", expand = TRUE)
    return(frame)
}
# Sets the state2 mid frame containing radio buttons for delimiters
# and a list box for quote selection
setState2MFrame <- function(frame, env){
    if(env$rowNameCol == 0){
        rowNames <- tclVar()
    }else{
        rowNames <- tclVar(env$rowNameCol)
    }
    rowNameEntered <- function(){
        if(tclvalue(rowNames) != ""){
            options(warn = -1)
            if(is.na(as.numeric(tclvalue(rowNames)))){
                tkmessageBox(title = "Entry Error",
                             message = "Column number shold be numerical",
                             icon = "error", type = "ok")
                tkdelete(rowNameEntry, "0", "end")
            }else{
                if(as.numeric(tclvalue(rowNames)) > env$numCols){
                    tkmessageBox(title = "Entry Error",
                                 message = paste("Column number can't be",
                                 "greater than", env$numCols),
                                 icon = "error", type = "ok")
                    tkdelete(rowNameEntry, "0", "end")
                    if(env$rowNameCol != 0){
                        env$rowNameCol <- 0
                    }
                }else{
                    env$rowNameCol <- as.numeric(tclvalue(rowNames))
                }
            }
            options(warn = 0)
        }else{
            env$rowNameCol <- 0
        }
    }
    mainFrame <- tkframe(frame, borderwidth = 2, relief = "groove")
    # Radio buttons for delimiters
    leftFrame <- tkframe(mainFrame)
    applyBut <- setSepRadios(leftFrame, env)
    # A list for quote selecttion (" or/and ')
    rightFrame <- tkframe(mainFrame)
    setQuoteList(rightFrame, env)
    tkpack(leftFrame, side = "left", anchor = "w", fill = "x",
           expand  = TRUE)
    tkpack(rightFrame, side = "left", fill = "x", expand = TRUE)
    tkpack(mainFrame, side = "top")

    rowNameFrame <- tkframe(frame, borderwidth = 2, relief = "groove")
    tkpack(tklabel(rowNameFrame, text = "Use column number "), side = "left")
    rowNameEntry <- tkentry(rowNameFrame, textvariable = rowNames,
                            width = 4, background = "white")
    tkbind(rowNameEntry, "<KeyRelease>", rowNameEntered)
    tkpack(rowNameEntry, side = "left", expand = FALSE)
    tkpack(tklabel(rowNameFrame, text = " for row names"), side = "left",
           expand = FALSE)
    tkpack(rowNameFrame, side = "top", expand = TRUE, pady = 5,
           anchor = "w", fill = "x")

    return(applyBut)
}
# Sets the radio buttons for separators for state2 mid frame
setSepRadios <- function(frame, env, state = "state2"){

    rowNames <- tclVar()
    labelFrame <- tkframe(frame)
    label <- tklabel(labelFrame, text = "File Delimiter:")
    tkpack(label, side = "left", anchor = "nw")
    tkpack(labelFrame, side = "left")
    sepFrame <- tkframe(frame)
    sepVar <- tclVar()
    sepButs <- list()

    sepButFun <- function(){
        if(tclvalue(sepVar) != "other"){
            temp <- getArgs(env)
            temp[[state]][["sep"]] <- tclvalue(sepVar)
            assignArgs(temp, env)
            tkconfigure(otherEntry, state = "disabled")
        }else{
            tkconfigure(otherEntry, state = "normal")
        }
    }
    sepEntered <- function(){
        tkselect(sepButs[["other"]])
        temp <- getArgs(env)
        temp[[state]][["sep"]] <- as.character(tkget(otherEntry))
        assignArgs(temp, env)
    }
    sepButs[["tab"]] <- tkradiobutton(sepFrame, text = "Tab",
                              variable = sepVar, width = 9,
                              value = "\t", anchor = "nw",
                                      command = sepButFun)
    sepButs[["semi"]] <- tkradiobutton(sepFrame, text = "Semicolon",
                               variable = sepVar, width = 9,
                               value = ";", anchor = "nw",
                                       command = sepButFun)
    sepButs[["comma"]] <- tkradiobutton(sepFrame, text = "Comma",
                              variable = sepVar, value = ",",
                              width = 9, anchor = "nw",
                                        command = sepButFun)
    sepButs[["space"]] <- tkradiobutton(sepFrame, text = "Space",
                              variable = sepVar, value = "\"\"",
                              width = 9, anchor = "nw",
                                        command = sepButFun)
    # Puts the buttons in two rows. First row now
    tkgrid(sepButs[["tab"]], sepButs[["semi"]], sepButs[["comma"]],
           sepButs[["space"]])
    sepButs[["newline"]] <- tkradiobutton(sepFrame, text = "Newline",
                              variable = sepVar, value = "\n",
                              width = 9, anchor = "nw",
                                        command = sepButFun)
    sepButs[["other"]] <- tkradiobutton(sepFrame, text = "Other:",
                              variable = sepVar, value = "other",
                              width = 9, anchor = "nw",
                                        command = sepButFun)
    otherEntry <- tkentry(sepFrame, width = 10, state = "disabled")
    tkbind(otherEntry, "<KeyRelease>", sepEntered)
    applyBut <- tkbutton(sepFrame, text = "Apply", width = 9)
    # Second row with an entry box for delimiters other those given here
    tkgrid(sepButs[["newline"]], sepButs[["other"]], otherEntry, applyBut)
    tkpack(sepFrame, side = "left", anchor = "ne", fill = "x",
           expand = TRUE)
    if(!is.null(getArgs(env)[[state]][["sep"]])){
        tkselect(sepButs[[whatDeli(getArgs(env)[[state]][["sep"]])]])
    }

    return(applyBut)
}

# Sets the list box for quotes for state2 mid frame
setQuoteList <- function(frame, env){
    quoteSelected <- function(){
        setQuote(quoteList, env)
    }

    label1 <- tklabel(frame, text = "     Quote:")
    tkpack(label1, side = "left", anchor = "ne")
    quoteFrame <- tkframe(frame)
    quoteList <- makeViewer(quoteFrame, vWidth = 8, vHeight = 1,
                            what  = "list", side = "top")
    tkconfigure(quoteList, selectmode = "extended")
    tkbind(quoteList, "<B1-ButtonRelease>", quoteSelected)
    writeList(quoteList, c("\"", "'"), clear = TRUE)
    tkpack(quoteFrame, anchor = "w", fill = "x", expand = TRUE)

}

# Sets the value for quote when user selects quote in the list for
# quotes in state2
setQuote <- function(listBox, env, state = "state2"){
    quotes <- ""
    # Quote can be multiple (" and/or ')
    selIndex <- unlist(strsplit(as.character(tkcurselection(listBox)), " "))
    for(i in selIndex){
        quotes <- paste(quotes, tkget(listBox, i), sep = "")
    }
    temp <- getArgs(env)
    temp[[state]][["quote"]] <- quotes
    assignArgs(temp, env)
}
# Sets the canvas holding the preview of data for state2
setState2BFrame <- function(frame, env){
    viewFrame <- tkframe(frame)
    dataView2 <- makeViewer(viewFrame, vScroll = TRUE, hScroll = TRUE,
                           vWidth = 30, vHeight = 20,
                            what = "text", side = "top")
    tkpack(viewFrame, anchor = "w", padx = 5, pady = 5, fill = "both",
           expand = TRUE)
    showData4State2(dataView2, env)
    return(dataView2)
}
# Populates the data preview list of state2
showData4State2 <- function(canvas, env, state = "state2"){
    writeColList <- function(i){
        tempList <- tklistbox(tempFrame, height = 0, width = 0,
                              background = "white")
        writeList(tempList, dataFile[,i])
        #tkinsert(tempList, "end", dataFile[,i])
        tkpack(tempList, side = "left", fill = "both", expand = TRUE)
    }

    # Only show the number of rows defined
    temp <- getArgs(env)[[state]]
    tempFrame <- tkframe(canvas)
    # Puts n in temporaly
    temp[["nrows"]] = getShowNum(env)
    conn <- safeFileOpen(temp[["file"]])
    if(inherits(conn, "connection")){
        temp[["file"]] <- conn
        options(show.error.messages = FALSE)
        dataFile <- try(do.call(read.table, temp))
        options(show.error.messages = TRUE)
        if(inherits(dataFile, "try-error")){
            tkmessageBox(title = "Import Error",
                         message = paste("An error message:\n\n", dataFile,
                         "\nwas generated while reading file ",
                         args[["file"]], "."), icon = "error", type = "ok")
        }else{
            # For data without a separater
            if(is.null(ncol(dataFile))){
                writeColList(1)
                env$numCols <- length(dataFile)
            }else{
                for(i in 1:ncol(dataFile)){
                    writeColList(i)
                }
                env$numCols <- ncol(dataFile)
            }
        }
    }else{
        tkmessageBox(title = "IO Error",
                     message = paste("Can't open file because", conn),
                     icon = "error", type = "ok")
    }
    tkwindow.create(canvas, "end", window = tempFrame)
    #tkcreate(canvas, "window", 0, 0, anchor = "nw", window = tempFrame)
}

# Gets the frame containing the interface for the top frame of
# importWizard for state3
getState3Frame <- function(base, env){
    # A frame containing the interface that will be returned
    frame <- tkframe(base)
    label1 <- tklabel(frame, text = paste("File:",
                             getArgs(env)[["state3"]][["file"]]),
                      font ="Helvetica 11 bold")
    tkpack(label1, pady = 5)
    topFrame <- tkframe(frame)
    if(!is.null(getArgs(env)[["state3"]][["sep"]])){
        setState3TFrame(topFrame, env)
        tkpack(topFrame, anchor = "nw", fill = "x", expand = TRUE)
    }
    bottomFrame <- tkframe(frame)
    setState3BFrame(bottomFrame, env)
    tkpack(bottomFrame, padx = 5, pady = 5, fill = "both", expand = TRUE)
    return(frame)
}
# Creates the left bottom portion of state3 frame
setState3TFrame <- function(frame, env){
    # More arguments for read.table
    moreClicked <- function(){
        moreArgs(env)
    }
    label <- tklabel(frame, text = paste("Editable column names are",
                            " shown in the first entry box on top of data",
                            " columns.\nEditable data type of columns is",
                            " shown in the entry box following column",
                            " names\nClick the check box on top of a column",
                            " to drop the column.\nClick 'More Args' button",
                            " for more arguments.", sep = ""),
                       width = 80, height = 4, justify = "left")
    tkpack(label, anchor = "w", pady = 5, side = "left", padx = 5)
    moreBut <- tkbutton(frame, text = "More Args...", width = 10,
                                                 command = moreClicked)
    tkpack(moreBut, side = "left", padx = 5, pady = 15)
}
# Read the other arguments from a widget for read.table arguments
moreArgs <- function(env){
    temp <- getArgs(env)
    moreArgs <- getMoreArgs()
    for(i in names(moreArgs)){
        temp[[getCState(env)]][[i]] <- moreArgs[[i]]
    }
    assignArgs(temp, env)
}
# Creates the right bottom portion of state3 frame
setState3BFrame <- function(frame, env){
    # Creat a canvas to hold the other widget elements
    rCanv <-  makeViewer(frame, vWidth = 700, vHeight = 280,
                       vScroll = TRUE, hScroll = TRUE,
                       what = "canvas", side = "top")
    tempFrame <- tkframe(rCanv)
    argsList <- getArgs(env)[["state3"]]
    argsList[["nrows"]] <- getShowNum(env)
    conn <- safeFileOpen(argsList[["file"]])
    if(inherits(conn, "connection")){
        argsList[["file"]] <- conn
        options(show.error.messages = FALSE)
        dataFile <- try(do.call(read.table, argsList))
        options(show.error.messages = TRUE)
        if(inherits(dataFile, "try-error")){
            tkmessageBox(title = "Import Error",
                         message = paste("An error message:\n\n", dataFile,
                         "\nwas generated while reading file ",
                         args[["file"]], "."), icon = "error", type = "ok")
        }else{
            if(env$rowNameCol != 0){
                # Take out the column that will be used for row names
                dataFile <- dataFile[, -env$rowNameCol]
            }
            setColInfos(find.type(dataFile, argsList[["state3"]][["sep"]],
                                  isFile = FALSE), env)
    # Cut to right size of file if longer than maxRow
#    if(nrow(dataFile) > getShowNum(env)){
#        dataFile <- dataFile[1:getShowNum(env),]
#    }
    # Finds the data type for columns
            colInfos <- getColInfo(env)
            writeCol4Matrix(tempFrame, dataFile, colInfos, env)
        }
    }else{
        tkmessageBox(title = "IO Error",
                     message = paste("Can't open file because", conn),
                     icon = "error", type = "ok")
    }
    tkcreate(rCanv, "window", 0, 0, anchor = "nw", window = tempFrame)
}
# Create a group of list boxes with entry boxes and a radio button on
# top to allow for user inputs.
writeCol4Matrix <- function(tempFrame, dataFile, colInfos, env){
    # For text files, row names are hard to guess. Check col value
    # here instead
    #if(ncol(dataFile) > length(colInfos)){
    #    dataFile <- dataFile[, 2:ncol(dataFile)]
    #}
    writeDataCol <- function(i, data){
        colFrame <- tkframe(tempFrame)
        dropCMD[[i]] <- function(){}
        body <- list(as.name("{"),
                     substitute(eval(dropColumn(j, env)), list(j = i)))
        body(dropCMD[[i]]) <- as.call(body)
        var <- tclVar()
        dropCheck[[i]] <- tkcheckbutton(colFrame, text = "Drop",
                                   variable = var, command = dropCMD[[i]])
        tkpack(dropCheck[[i]], side = "top", fill = "x", expand = TRUE)
        nameEntry[[i]] <- tkentry(colFrame, width = 0)
        # Also updates the value of colInfos
        temp <- colInfos[[i]]
        if(!is.null(colnames(data))){
            writeList(nameEntry[[i]], colnames(data))
            colName(temp) <- colnames(data)
        }else{
            writeList(nameEntry[[i]], paste("V", i, sep = ""))
            colName(temp) <- paste("V", i, sep = "")
        }
        colInfos[[i]] <<- temp
        nameCMD[[i]] <- function(){}
        body <- list(as.name("{"), substitute(eval(setColName(j,
                                   nameEntry[[j]], env)), list(j = i)))
        body(nameCMD[[i]]) <- as.call(body)
            tkbind(nameEntry[[i]], "<KeyRelease>", nameCMD[[i]])
        tkpack(nameEntry[[i]], side = "top", fill = "x", expand = TRUE)
        typeEntry[[i]] <- tkentry(colFrame, width = 0)
        writeList(typeEntry[[i]], colType(colInfos[[i]]))
        typeCMD[[i]] <- function(){}
        body <- list(as.name("{"), substitute(eval(setColType(j,
                                      typeEntry[[j]], env)), list(j = i)))
        body(typeCMD[[i]]) <- as.call(body)
        tkbind(typeEntry[[i]], "<KeyRelease>", typeCMD[[i]])
        tkpack(typeEntry[[i]], side = "top", fill = "x", expand = TRUE)
        colList[[i]] <- tklistbox(colFrame, width = 0,
                                  height = 0, background = "white")
        writeList(colList[[i]], data)
#        tkinsert(colList[[i]], "end", data)
        tkpack(colList[[i]], side = "top", fill = "x", expand = TRUE)
        tkpack(colFrame, side = "left", fill = "both", expand = TRUE)
    }
    typeEntry <- list()
    dropCheck <- list()
    nameEntry <- list()
    # Lists to keep the command associated with the radio buttons of
    # entry boxex
    dropCMD <- list()
    nameCMD <- list()
    typeCMD <- list()
    colList <- list()
    if(is.null(ncol(dataFile))){
        writeDataCol(1, dataFile)
    }else{
        for(i in 1:ncol(dataFile)){
            writeDataCol(i, dataFile[,i])
        }
    }
    # Sets values for colInfo object
    assignColInfo(colInfos, env)
}

# Set the value of slot 'drop' of a colInfo object
dropColumn <- function(index, env){
    colInfos <- getColInfo(env)
    temp <- colInfos[[index]]
    if(dropOrNot(colInfos[[index]])){
        temp <- colInfos[[index]]
        dropOrNot(temp) <- FALSE
    }else{
        dropOrNot(temp) <- TRUE
    }
    colInfos[[index]] <- temp
    assignColInfo(colInfos, env)
}
# Set the value of slot (column) 'name' of a colInfo object
setColName <- function(index, entryBox, env){
    colInfos <- getColInfo(env)
    entry <- as.character(tkget(entryBox))
    temp <- colInfos[[index]]
    colName(temp) <- entry
    colInfos[[index]] <- temp
    assignColInfo(colInfos, env)
}
# Set the value of slot 'type' of a colInfo object
setColType <- function(index, entryBox, env){
    colInfos <- getColInfo(env)
    entry <- as.character(tkget(entryBox))
    temp <- colInfos[[index]]
    colType(temp) <- entry
    colInfos[[index]] <- temp
    assignColInfo(colInfos, env)
}
# Gets the word representation of delimiters
whatDeli <- function(delimiter){
    switch(delimiter,
           "\t" = return("tab"),
           ";" = return("semi"),
           " " = return("space"),
           "," = return("comma"),
           "\n" = return("newline"),
           return("other"))
}
# This function generates a widget using widgetTools to collect all
# the arguments for read.table that are not yet collected by importWizard
getMoreArgs <- function(){
    args <- formals(read.table)

    args <- args[setdiff(names(args),
                         c("file", "header", "sep", "skip","quote",
                           "row.names", "col.names"))]

    # Argument fill has to be defined using the value of
    # blank.lines.skip.
    args[["fill"]] <- !args[["blank.lines.skip"]]
    return(argsWidget(args))
}
# This function provides the interface for uers to decide whether to
# save the imported data in the global environment
getName4Data <- function(filename, objType = "object"){
    # Gets ride of the separaters
    temp <- gsub(paste("^.*", .Platform$file.sep,
                                  "(.*)", sep = ""), "\\1", filename)
    # Gets ride of the extensions
    temp <- strsplit(temp, "\\.")[[1]][1]
    var <- tclVar(temp)
    # Destroy the window
    end <- function(){
        tkgrab.release(top)
        tkdestroy(top)
    }
    # Save the data
    save <- function(){
        temp <<- tclvalue(var)
        if(temp == ""){
            tkmessageBox(title = "I/O Error",
                     message = "No name entered\nPlease try again",
                     icon = "error",
                     type = "ok")
        }else{
            end()
        }
    }
    # Do not save the data
    noSave <- function(){
        temp <<- NULL
        end()
    }
    ## Set up the interface
    top <- tktoplevel()

    tktitle(top) <- "BioC Data Import Wizard"
    tkpack(tklabel(top, text = paste("Would you like to save the", objType)),
                   side = "top", pady = 5, padx = 5)

    nameFrame <- tkframe(top)
    inst <- tklabel(nameFrame, text = "Save as: ")
    tkpack(inst, side = "left")
    nameEntry <- tkentry(nameFrame, width = 10, textvariable = var)
    tkpack(nameEntry, side = "left")
    tkpack(nameFrame, side = "top", pady = 5, padx = 5)
    butFrame <- tkframe(top)
    noSaveBut <- tkbutton(butFrame, text = "Don't Save", width = 10,
                       command = noSave)
    saveBut <- tkbutton(butFrame, text = "Save", width = 10,
                        command = save)
    tkpack(noSaveBut, saveBut, side = "left")
    tkpack(butFrame, side = "top", pady = 5, padx = 5)

    tkgrab.set(top)
    tkwait.window(top)

    return(temp)
}

Try the tkWidgets package in your browser

Any scripts or data that you put into this service are public.

tkWidgets documentation built on Nov. 8, 2020, 5:17 p.m.