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

scripting-plugin does not handle the kind of edges. #193

Open
ju1m opened this issue Jul 20, 2018 · 2 comments
Open

scripting-plugin does not handle the kind of edges. #193

ju1m opened this issue Jul 20, 2018 · 2 comments

Comments

@ju1m
Copy link

ju1m commented Jul 20, 2018

There is no .kind or .Kind on edges.
This handling appears to be missing in __findattr_ex__ in gephi-plugins/modules/ScriptingPlugin/src/main/java/org/gephi/scripting/wrappers/GyEdge.java.
Not knowing the java tools, I am unable to easily get a compiling gephi-plugins to investigate further. My understanding of the README.md instructions lead me to a failure, apparently due to missing dependencies:

% mvn clean package
Warning: JAVA_HOME environment variable is not set.
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=lcd -Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] ScriptingPlugin
[INFO] gephi-plugins
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building ScriptingPlugin 0.9.2.1
[INFO] ------------------------------------------------------------------------
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom (4 KB at 1.4 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom (13 KB at 15.6 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.jar
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.jar (25 KB at 22.8 KB/sec)
[WARNING] The POM for net.java.dev:stax-utils:jar:snapshot-20100402 is missing, no dependency information available
[WARNING] The POM for gython:gython:jar:1.0 is missing, no dependency information available
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] ScriptingPlugin .................................... FAILURE [  5.517 s]
[INFO] gephi-plugins ...................................... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.708 s
[INFO] Finished at: 2018-07-20T20:52:49+02:00
[INFO] Final Memory: 15M/195M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project scripting.plugin: Could not resolve dependencies for project org.gephi:scripting.plugin:nbm:0.9.2.1: The following artifacts could not be resolved: net.java.dev:stax-utils:jar:snapshot-20100402, gython:gython:jar:1.0: Failure to find net.java.dev:stax-utils:jar:snapshot-20100402 in https://raw.github.com/gephi/gephi/mvn-thirdparty-repo/ was cached in the local repository, resolution will not be reattempted until the update interval of gephi-thirdparty has elapsed or updates are forced

A workaround is to add a new column (say 'Category') copying the 'Kind' column. This is a bit painful because it doesn't seem to be doable within gephi, so I had to export the links to a CSV, copying the column with gnumeric, and reimporting the CSV. By the way, mass deleting links in gephi 0.9.2 does not seem to work, it only deletes one link. So, I had to start a new project to then import the links from CSV. HTH.

@eduramiba eduramiba self-assigned this Jul 20, 2018
@eduramiba eduramiba added the Bug label Jul 20, 2018
@eduramiba
Copy link
Member

Thanks for the report, I will take a look at the plugin.

@ju1m
Copy link
Author

ju1m commented Jul 22, 2018

Guessing and pondering lead me to the following Python-only workaround:

# -*- coding: utf-8 -*-
import java.awt.Color as Color
import org.openide.util.Lookup as Lookup

def getWorkSpace():
    import org.gephi.project.api.ProjectController as ProjectController
    return Lookup.getDefault().lookup(ProjectController).getCurrentWorkspace()
def getGraphModel():
    import org.gephi.graph.api.GraphModel as GraphModel
    return getWorkSpace().getLookup().lookup(GraphModel)
def getFilterModel():
    import org.gephi.filters.api.FilterModel as FilterModel
    return getWorkSpace().getLookup().lookup(FilterModel)

def filterEdgesByType(edgeType):
    import org.gephi.filters.plugin.edge.EdgeTypeBuilder as EdgeTypeBuilder
    filterEdgeType = getFilterModel().getLibrary().getLookup().lookup(EdgeTypeBuilder).getFilter(getWorkSpace())
    graphModel = getGraphModel()
    edgeTypes = graphModel.getEdgeTypes() # NOTE: tricky table mapping label index to edge type
    edgeTypeLabels = graphModel.getEdgeTypeLabels()
    edgeTypeIndex = edgeTypeLabels.index(edgeType)
    filterEdgeType.setType(edgeTypes[edgeTypeIndex])
    import org.gephi.filters.api.FilterController as FilterController
    filterController = Lookup.getDefault().lookup(FilterController)
    query = filterController.createQuery(filterEdgeType)
    newGyFilter = (type == type) # NOTE: by lack of a better way to get a new GyFilter
    newGyFilter.setUnderlyingQuery(query)
    return newGyFilter

def filterEdgesNot(filter):
    import org.gephi.filters.plugin.operator.NOTBuilderEdge as NOTBuilderEdge
    filterNOTEdge = getFilterModel().getLibrary().getLookup().lookup(NOTBuilderEdge).getFilter(getWorkSpace())
    import org.gephi.filters.api.FilterController as FilterController
    filterController = Lookup.getDefault().lookup(FilterController)
    query = filterController.createQuery(filterNOTEdge)
    filterController.setSubQuery(query, filter.getUnderlyingQuery());
    newGyFilter = (type == type)
    newGyFilter.setUnderlyingQuery(query)
    return newGyFilter

Beware that I am NOT knowledgeable about Java nor Python, and thus could be doing bad things without my knowing.
Below an example of using the above code in a filterEdgesByType.py file put in gephi launching directory:

>>> execfile("./filterEdgesByType.py")
>>> filterEdgesByType("usage")
<org.gephi.scripting.wrappers.GyFilter object at 0x2>
>>> len(g.filter(filterEdgesByType("usage")).edges)
201
>>> len(g.edges) == len(g.filter(filterEdgesByType("usage") | filterEdgesNot(filterEdgesByType("usage"))).edges)
True

So, in the end, the Java->Python binding is quite exhaustive and flexible.
I think it would be great to emphasize better that power in the wiki because the Gython code mislead me into thinking it was an ad hoc binding, not an easily extensible one. On the contrary, the Python can access most of (if not all) the Java code of Gephi! Sometimes in convoluted ways, but once we know the tricks, it's not that hard.
I first engaged on this journey by reading gephi-plugins/modules/ScriptingPlugin/src/main/resources/org/gephi/scripting/util/preload.py, which let me hope there was something that could be done without hacking the Java with all its difficulties and lack of shareability.
Introspection capabilities in Gephi's Console helped, but mostly educated greps on the Java did the heavy lifting.
This said, coming from an Haskell background, I was eager to have a more assisting type system to teach me the codebase and check my guesses. Anyway, thanks for producing Gephi as a Free Software :) Keep it up!

I guess next step may be to get into the gephi-toolkit, but not sure at this point if there would be a gain for my usecase.

In case it helps someone following the same Python path, I'm dumping my current .py below because it contains other useful tricks (maybe improvable):

# -*- coding: utf-8 -*-
import sys
import time

import java.awt.Color as Color
import java.awt.Font as Font
import org.openide.util.Lookup as Lookup

import org.gephi.layout.api.LayoutController as LayoutController
layoutController = Lookup.getDefault().lookup(LayoutController)

import org.gephi.layout.plugin.labelAdjust.LabelAdjustBuilder as LabelAdjust
import org.gephi.layout.plugin.forceAtlas.ForceAtlas as ForceAtlas
#import org.gephi.layout.plugin.circularLayout.CircularLayout as CircularLayout
#import org.gephi.scripting.plugin as ScriptingPlugin
#import org.gephi.desktop.scripting.ConsoleTopComponent as ConsoleTopComponent

communs   = g.filter(type == u"Commun")
personnes = g.filter(type == u"Personne")

usage      = g.filter(cat == u"usage")
economique = g.filter(cat == u"économique")

def getWorkSpace():
    import org.gephi.project.api.ProjectController as ProjectController
    return Lookup.getDefault().lookup(ProjectController).getCurrentWorkspace()
def getGraphModel():
    import org.gephi.graph.api.GraphModel as GraphModel
    return getWorkSpace().getLookup().lookup(GraphModel)
def getFilterModel():
    import org.gephi.filters.api.FilterModel as FilterModel
    return getWorkSpace().getLookup().lookup(FilterModel)

def filterEdgesByType(edgeType):
    import org.gephi.filters.plugin.edge.EdgeTypeBuilder as EdgeTypeBuilder
    filterEdgeType = getFilterModel().getLibrary().getLookup().lookup(EdgeTypeBuilder).getFilter(getWorkSpace())
    graphModel = getGraphModel()
    edgeTypes = graphModel.getEdgeTypes()
    edgeTypeLabels = graphModel.getEdgeTypeLabels()
    edgeTypeIndex = edgeTypeLabels.index(edgeType)
    filterEdgeType.setType(edgeTypes[edgeTypeIndex])
    import org.gephi.filters.api.FilterController as FilterController
    filterController = Lookup.getDefault().lookup(FilterController)
    query = filterController.createQuery(filterEdgeType)
    newGyFilter = (type == type)
    newGyFilter.setUnderlyingQuery(query)
    return newGyFilter

def filterEdgesNot(filter):
    import org.gephi.filters.plugin.operator.NOTBuilderEdge as NOTBuilderEdge
    filterNOTEdge = getFilterModel().getLibrary().getLookup().lookup(NOTBuilderEdge).getFilter(getWorkSpace())
    import org.gephi.filters.api.FilterController as FilterController
    filterController = Lookup.getDefault().lookup(FilterController)
    query = filterController.createQuery(filterNOTEdge)
    filterController.setSubQuery(query, filter.getUnderlyingQuery());
    newGyFilter = (type == type)
    newGyFilter.setUnderlyingQuery(query)
    return newGyFilter

def getConsole():
    return getWorkSpace().getLookup().lookup(org.python.core.PyObject)
def getLocals():
    return getConsole().locals
def getNameSpace():
    #import org.gephi.scripting.api.ScriptingController as ScriptingController
    #scriptingController = Lookup.getDefault().lookup(ScriptingController)
    import org.gephi.scripting.api.ScriptingModel as ScriptingModel
    scriptingModel = getWorkSpace().getLookup().lookup(ScriptingModel)
    return scriptingModel.getLocalNamespace()
def getVizController():
    import org.gephi.visualization.VizController as VizController
    vizController = Lookup.getDefault().lookup(VizController)
    return vizController
    #workspace = getWorkSpace()
    #return workspace.getLookup().lookup(VizController)

def debug(msg):
    console = getConsole()
    console.stdout.write(msg)
    # XXX: useless, not better than print():
    # there does not seem to be a flush+refresh
    # of the Console while executing a layout.

def aspectEdges():
    graphModel = getGraphModel()
    edgeTypes = graphModel.getEdgeTypes()
    edgeTypeLabels = graphModel.getEdgeTypeLabels()
    for e in g.edges:
        edgeType = edgeTypeLabels[edgeTypes.index(graphModel.getGraph().getEdge(e.id).getType())]
        if edgeType == u"usage":
            e.color = Color(0xFA8072) # salmon
        if edgeType == u"économique":
            e.color = Color(0xCDAD00) # gold3
def aspectNodes(nodeSize=150):
    graphModel = getGraphModel()
    for node in personnes.nodes:
        node.color = Color(0xE5E5E5) # gray90
        node.size  = nodeSize
        textProperties = graphModel.getDirectedGraph().getNode(node.id).getTextProperties()
        textProperties.setColor(Color.BLACK)
        textProperties.setVisible(True)
    for node in communs.nodes:
        node.color = Color(0xB0E2FF) # LightSkyBlue
        node.size  = nodeSize
        textProperties = graphModel.getDirectedGraph().getNode(node.id).getTextProperties()
        textProperties.setColor(Color(0x0000CD)) # medium blue
        textProperties.setVisible(True)
def aspect():
    aspectNodes()
    aspectEdges()
    import org.gephi.project.api.ProjectController as ProjectController
    import org.gephi.preview.api.PreviewController as PreviewController
    import org.gephi.preview.api.PreviewProperty as PreviewProperty
    import org.gephi.preview.api.PreviewProperties as PreviewProperties
    import org.gephi.io.exporter.api.ExportController as ExportController
    import org.gephi.preview.api.PreviewModel as PreviewModel
    projectController = Lookup.getDefault().lookup(ProjectController)
    workspace         = projectController.getCurrentWorkspace()
    exportController  = Lookup.getDefault().lookup(ExportController)
    previewController = Lookup.getDefault().lookup(PreviewController)
    previewModel      = previewController.getModel(workspace)
    previewProperties = previewModel.getProperties()
    
    import org.gephi.preview.types.DependantOriginalColor as DependantOriginalColor
    import org.gephi.preview.types.DependantColor as DependantColor
    import org.gephi.preview.types.EdgeColor as EdgeColor
    # See: modules/PreviewAPI/src/main/java/org/gephi/preview/api/PreviewProperty.java
    previewProperties.putValue(PreviewProperty.BACKGROUND_COLOR, Color.WHITE)
    # NODE
    previewProperties.putValue(PreviewProperty.SHOW_NODE_LABELS, True)
    previewProperties.putValue(PreviewProperty.NODE_BORDER_COLOR, DependantColor(Color.BLACK))
    previewProperties.putValue(PreviewProperty.NODE_BORDER_WIDTH, 1.0)
    previewProperties.putValue(PreviewProperty.NODE_OPACITY, 0)
    previewProperties.putValue(PreviewProperty.NODE_LABEL_BOX_COLOR, DependantColor(DependantColor.Mode.PARENT))
    previewProperties.putValue(PreviewProperty.NODE_LABEL_BOX_OPACITY, 100)
    previewProperties.putValue(PreviewProperty.NODE_LABEL_COLOR, DependantOriginalColor(DependantOriginalColor.Mode.ORIGINAL))
    previewProperties.putValue(PreviewProperty.NODE_LABEL_FONT, Font("DejaVu Sans Condensed", Font.BOLD, 3))
    previewProperties.putValue(PreviewProperty.NODE_LABEL_MAX_CHAR, 100)
    previewProperties.putValue(PreviewProperty.NODE_LABEL_OUTLINE_COLOR, DependantColor(Color.WHITE))
    previewProperties.putValue(PreviewProperty.NODE_LABEL_OUTLINE_OPACITY, 80)
    previewProperties.putValue(PreviewProperty.NODE_LABEL_OUTLINE_SIZE, 0)
    previewProperties.putValue(PreviewProperty.NODE_LABEL_PROPORTIONAL_SIZE, True)
    previewProperties.putValue(PreviewProperty.NODE_LABEL_SHORTEN, False)
    previewProperties.putValue(PreviewProperty.NODE_LABEL_SHOW_BOX, False)
    
    # EDGE
    previewProperties.putValue(PreviewProperty.SHOW_EDGES, True)
    previewProperties.putValue(PreviewProperty.SHOW_EDGE_LABELS, False)
    previewProperties.putValue(PreviewProperty.ARROW_SIZE, 3)
    previewProperties.putValue(PreviewProperty.EDGE_COLOR, EdgeColor(EdgeColor.Mode.ORIGINAL))
    previewProperties.putValue(PreviewProperty.EDGE_CURVED, True)
    previewProperties.putValue(PreviewProperty.EDGE_RESCALE_WEIGHT, False)
    previewProperties.putValue(PreviewProperty.EDGE_OPACITY, 100)
    previewProperties.putValue(PreviewProperty.EDGE_RADIUS, 0)
    previewProperties.putValue(PreviewProperty.EDGE_THICKNESS, 4)
    previewProperties.putValue(PreviewProperty.EDGE_LABEL_COLOR, DependantOriginalColor(DependantOriginalColor.Mode.ORIGINAL))
    previewProperties.putValue(PreviewProperty.EDGE_LABEL_FONT, Font("DejaVu Sans Condensed", Font.BOLD, 1))
    previewProperties.putValue(PreviewProperty.EDGE_LABEL_MAX_CHAR, 30)
    previewProperties.putValue(PreviewProperty.EDGE_LABEL_OUTLINE_COLOR, DependantColor(Color.WHITE))
    previewProperties.putValue(PreviewProperty.EDGE_LABEL_OUTLINE_OPACITY, 80)
    previewProperties.putValue(PreviewProperty.EDGE_LABEL_OUTLINE_SIZE, 0)
    previewProperties.putValue(PreviewProperty.EDGE_LABEL_SHORTEN, False)
    
    #previewController.refreshPreview(workspace)
    #return previewProperties

def layoutForceAtlas(iters):
    layout = ForceAtlas().buildLayout()
    layout.resetPropertiesValues()
    layoutController.setLayout(layout)
    
    # See: gephi/modules/LayoutPlugin/src/main/java/org/gephi/layout/plugin/forceAtlas/ForceAtlasLayout.java
    layout.setRepulsionStrength(10000)
    layout.setAttractionStrength(20)
    layout.setMaxDisplacement(10)
    layout.setFreezeBalance(True)
    layout.setFreezeStrength(80)
    layout.setFreezeInertia(0.2)
    layout.setGravity(50)
    layout.setOutboundAttractionDistribution(True)
    layout.setAdjustSizes(True)
    layout.setSpeed(1)
    layout.setCooling(1)
    
    layoutController.executeLayout(iters)
    while layoutController.canStop():
        time.sleep(0.5)
def getCircularLayout():
    # NOTE: I don't know why I cannot get it like the other layouts,
    # maybe because it's a plugin from an extension.
    for l in getLayoutBuilders():
        if l.name == "Circular Layout":
            return l
def layoutCircularLayout():
    CircularLayout = getCircularLayout()
    layout = CircularLayout.buildLayout()
    layout.resetPropertiesValues()
    layoutController.setLayout(layout)
    
    # See: gephi/modules/LayoutPlugin/src/main/java/org/gephi/layout/plugin/circularLayout/CircularLayout.java
    layout.setNodePlacement(u"Random")
    #layout.setNodePlacement(u"NodeID")
    layout.setNodePlacementNoOverlap(True)
    layout.setNodePlacementTransition(False)
    #layout.setAdjustBySize(True)
    
    layoutController.executeLayout()
    while layoutController.canStop():
        time.sleep(0.5)
def layoutLabelAdjust():
    layout = LabelAdjust().buildLayout()
    layout.resetPropertiesValues()
    layoutController.setLayout(layout)
    
    # See: gephi/modules/LayoutPlugin/src/main/java/org/gephi/layout/plugin/labelAdjust/LabelAdjust.java
    layout.setSpeed(1)
    layout.setAdjustBySize(True)
    
    layoutController.executeLayout(100)
    while layoutController.canStop():
        time.sleep(0.5)
def layout():
    setVisible(g.filter(filterEdgesByType(u"usage")).filter(degree >= 1))
    layoutCircularLayout()
    layoutForceAtlas(4000)
    layoutLabelAdjust()
    #setVisible(g)

def export():
    import java.io.File as File
    import org.gephi.io.exporter.api.ExportController as ExportController
    
    exportController  = Lookup.getDefault().lookup(ExportController)
    fileName = File("out_"+time.strftime('%Y-%m-%d_%H-%M-%S')+".png")
    fileExporter = exportController.getFileExporter(fileName)
    
    # See: gephi/modules/PreviewExport/src/main/java/org/gephi/io/exporter/preview/PNGExporter.java
    fileExporter.setWidth(4000)
    fileExporter.setHeight(4000)
    fileExporter.setMargin(4)
    fileExporter.setTransparentBackground(False)
    
    exportController.exportFile(fileName, fileExporter)

def run():
    aspect()
    layout()
    export()

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

No branches or pull requests

3 participants