Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding custom layer to existing cranvasplot object #191

Open
mariev opened this issue Feb 6, 2013 · 3 comments
Open

adding custom layer to existing cranvasplot object #191

mariev opened this issue Feb 6, 2013 · 3 comments

Comments

@mariev
Copy link
Member

mariev commented Feb 6, 2013

Is there a way to add custom interaction to a CranvasPlot object without destroying existing interactions?

In the example provided the added layer seems to destroy the brushing interaction...

qiris <- qdata(iris)

mymousepress <- function(layer, event){
  print('do something I want!')
  print(event$pos()$y())
}
p <- qscatter(data = qiris, x = Sepal.Length, y = Sepal.Width)
new <- qlayer(mousePressFun = mymousepress, limits = qrect(p$meta$limits))
p$layerList[[1]][1,2] <- new
sync_limits(p$meta, new)
print(p)
@mariev
Copy link
Member Author

mariev commented Feb 7, 2013

some ideas:

  • make default parts of functions for mouseXXX and keyXXX event handlers in qscatter available externally. New interaction layer may incorporate those elements into the new handlers. (similar to common_key_press)
    • will new layer have to define all mouse handlers even if only an update to mousePressFun (for example) is desired?
    • will new layer have access to data in the same scope when calling defaults?
  • Pass all layers to p$layerList in CranvasPlot, instead of just root . modify the event handler layer's event functions
    • can we modify a single event handler? what will happen?
    • what about scoping?
    • how to access event handlers of a single layer?
  • Navigate to child layers from root ( $layerList[[1]]) and modify the event handler layer
  • how? there are 295 items (properties and functions) in a Rlayer instance. Which one?
  • should we have separate layers containing the event handlers and the main drawing function?

@mariev mariev closed this as completed Feb 7, 2013
@mariev mariev reopened this Feb 7, 2013
@mariev
Copy link
Member Author

mariev commented Feb 7, 2013

This lightweight example illustrates how qscatter could be reorganized to allow default event handlers to be extended by a new custom interaction layer.

  • my.meta is analogous to Scat.meta
  • mykeyevent is the key handler accessible outside plotting function
  • qNew_draw is the proposed reorganized structure modeled on qscatter

Example 1: using existing qscatter method

  • the addition of an interaction layer breaks default interaction
  • press t to invoke the new interaction (horizontal red line moves)
  • arrow up or down fail to change the point size

Example 2: using proposed plot structure

  • this example only implements point size change as the basic interaction
  • press t to invoke the new interaction (horizontal red line moves)
  • arrow up or down works!
library(objectProperties)
library(plumbr)
library(cranvas)
library(qtpaint)


### proposed generalization of qscatter, placing key event handler outside 
### draw function


my.meta <- setRefClass("My_meta", contains = "CommonMeta",
              fields = objectProperties::properties(c(Common.meta, 
              list(horizontal = "logical", xleft = "numeric"))))

mykeyevent <- function(data, meta, layer, event){
  common_key_press(layer = layer, event = event, data = data, meta = meta)
  print("key press!")
  shift = event$modifiers() == Qt$Qt$ShiftModifier
  if (shift && length(i <- which(match_key(c('Left', 'Right', 'Up', 'Down'))))) {
    j = c(1, 1, 2, 2)[i]; k = c(1, -1, -1, 1)[i]
    meta$limits[, j] = extend_ranges(meta$limits[, j], k * c(1, -1) * 0.02)
    update_limits(meta$limits)
  } else if (length(i <- which(match_key(c('Up', 'Down'))))) {
    ## change size
    data$.size = pmax(0.1, c(1.1, 0.9)[i] * data$.size)
  }
}

qNew_draw <- function(qd, x, y){
  identify_mode <- FALSE
  meta <- my.meta$new(alpha = 1,  
                      limits = matrix(c(range(qd[,x]), range(qd[,y])), nrow = 2))
  main_draw <- function(layer, painter){
    qdrawCircle(painter, x = qd[,x], y = qd[,y], r = qd[,'.size'],
                stroke = 'black', fill = 'black')
  }

  brush_draw <- function(layer, painter){
    idx <- selected(qd)

    qdrawCircle(painter, x = qd[idx, x], y = qd[idx, y], 
                  r = 2 * qd$.size[idx], 
                stroke = 'yellow', fill = 'yellow')


  }

  identify_draw <- function(layer, painter){

  }

  mouse_press <- function(layer, event){
    print("mouse press!")

  }

  key_press <- function(layer, event){
    mykeyevent(data = qd, meta = meta, layer = layer, event = event)
  }

  scene = qscene()
  layer.root <- qlayer(scene)
  layer.main <- qlayer(paintFun = main_draw, limits = qrect(meta$limits))
  layer.int <- qlayer(mousePressFun = mouse_press, keyPressFun = key_press, 
                      limits = qrect(meta$limits))
  layer.brush <- qlayer(paintFun = brush_draw, limits = qrect(meta$limits))
  layer.identify <- qlayer(paintFun = identify_draw, limits = qrect(meta$limits))
  layer.keys <- cranvas:::key_layer(meta)
  layer.root[1, 2] <- layer.main
  layer.root[1, 2] <- layer.int
  layer.root[1,2] <- layer.brush
  layer.root[1,2] <- layer.identify
  layer.root[1,2] <- layer.keys
  view <- qplotView(scene)

  add_listener(qd, function(i, j){
    if(j == '.brushed'){
      qupdate(layer.brush)
      print('brush changed')
    }
    if(j == '.size'){
      qupdate(layer.main)
      qupdate(layer.brush)
      print('size change')
    }
  })
  layerList <- cranvas:::LayerList(layer.root = layer.root)
  res <- cranvas:::CranvasPlot(layerList, scene = scene, 
                     view = view, meta = meta, data = qd)
  print(class(res))
  res
}


###### example 1:  breaking interaction using qscatter in current state

qiris <- qdata(iris)
hline <- 2.0

mykeypress <- function(layer, event){
  print('do something I want!')
  if(match_key('T')){
    hline <<- hline + .2
    qupdate(new)
  }
}

p <- qscatter(data = qiris, x = Sepal.Length, y = Sepal.Width)


pmydrawingFun <- function(layer, painter){
  qdrawLine(painter, x = p$meta$limits[,1], y = hline, stroke = 'red')
}
new <- qlayer(paintFun = mydrawingFun, keyPressFun = mykeypress, 
              limits = qrect(p$meta$limits))
p$layerList[[1]][1,2] <- new
sync_limits(p$meta, new)


print(p)

##### example 2: using the proposed structure

qiris <- qdata(iris)
hline <- 2.0


g <- qNew_draw(qiris, 'Sepal.Length', 'Sepal.Width')


anotherkeypress <- function(layer, event){
  print('do something else I want!')
  if(match_key('T')){
    hline <<- hline + .2
    qupdate(another_layer)
  }
  mykeyevent(data = qiris, meta = g$meta, layer = layer, event = event)

}

mydrawingFun <- function(layer, painter){
  qdrawLine(painter, x = p$meta$limits[,1], y = hline, stroke = 'red')
}

another_layer <- qlayer(paintFun = mydrawingFun, keyPressFun = anotherkeypress, 
              limits = qrect(g$meta$limits))
g$layerList[[1]][1,2] <- another_layer
sync_limits(p$meta, another_layer)


print(g)

@mariev
Copy link
Member Author

mariev commented Apr 8, 2013

see #202

this works:

qiris <- qdata(iris)
test <- qscatter(data = qiris, x= Sepal.Length, y = Sepal.Width )
myPress <- function(layer, event){
  print(event)
  print('press')
}

myRel <- function(layer, event){
  print(event)
  print('release')
}
register_handlers(test, keypress = list(myPress), 
                  keyrelease = list(myRel), add = TRUE)
test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant