Визуализация графа
У меня есть простая программа, которая имитирует распределенную среду. Я хочу визуализировать поведение процессора с помощью библиотеки GraphStream. Каждый процессор является потоком, они делают некоторые вычисления, но не все время, только когда я устанавливаю switch
переменная
while(running){
if(switch)
computate();
}
Я написал класс, который принимает процессорный список и готовит визуализацию графа. Для этой задачи есть функция
public void adjustBFSGraph2(){
for(Edge e: this.g.getEdgeSet())
{
e.clearAttributes();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(Processor p : processorList)
{
p.getNode().setAttribute("ui.label", "Pid" +" " + p.getPID() +" "+"Dist: " + p.getFieldValue("bfs") + " " + "Parent" + " " + p.getFieldValue("parent"));
if(!p.getFieldValue("parent").equals("-1"))
{
Edge e = p.getNode().getEdgeBetween(p.getFieldValue("parent"));
if(e != null)
{
e.setAttribute("ui.color", p.getColor());
e.setAttribute("ui.label", "added" + p.getPID());
}
}
}
}
Моя проблема в том, что я вижу цвета по краям только на мгновение ока, а затем цвет исчезает. Похоже, это добавляет атрибут "ui.color"
и удаляет его в том же цикле, но как это возможно?
@update Я отредактировал свой код, теперь я могу видеть края для времени, указанного в thread.sleep()
после первого цикла, я не понимаю, почему после очистки всех атрибутов я могу их увидеть. вот как я называю свою функцию
while(true)
{ i++;
//if any processor is not runing
boolean aux = false;
while(!aux) {
for (Processor proc : s.processorList) {
aux = aux || proc.isEnabled();
}
aux = !aux;
}
s.gp.adjustBFSGraph();
Thread.sleep(5000);
for(Processor proc: s.processorList)
{
proc.enable();
}
}
когда Thread.sleep()
Функция внутренней настройки установлена на значение менее 100 мс, она снова начинает мигать.
Потому что может быть немного неясно, что я делаю, я создал меньший пример
Это эквивалент моего класса процессора
public class SomeObjectWithNode {
public Node n;
public Color c;
public SomeObjectWithNode(Node n)
{
Random rand = new Random();
float r = rand.nextFloat();
float g = rand.nextFloat();
float b = rand.nextFloat();
Color randomColor = new Color(r, g, b);
c = randomColor;
this.n = n;
}
}
Вот класс, изменяющий стиль графического стиля / рисующий это
public class TestDraw {
Vector<SomeObjectWithNode> n;
Graph g;
public TestDraw(Vector<SomeObjectWithNode> k, Graph g)
{
this.g= g;
this.n = k;
}
public void adjust()
{
Random rand = new Random();
for(Edge e: g.getEdgeSet())
{
e.clearAttributes();
}
for(SomeObjectWithNode k: n)
{
k.n.addAttribute("ui.color", k.c);
for(Edge e: k.n.getEdgeSet())
{
float r = rand.nextFloat();
float g = rand.nextFloat();
float b = rand.nextFloat();
Color randomColor = new Color(r, g, b);
e.addAttribute("ui.color", randomColor);
}
}
}
}
а вот и главный класс
public class TestGs {
public static void main(String[] args) {
Node lastNode;
TestDraw t;
Vector<SomeObjectWithNode> a = new Vector<SomeObjectWithNode>();
System.setProperty("gs.ui.renderer", "org.graphstream.ui.j2dviewer.J2DGraphRenderer");
Graph g = new SingleGraph("test1");
g.display();
g.addAttribute("ui.stylesheet", "node { fill-mode: dyn-plain; size: 10px;} edge { fill-mode: dyn-plain; size: 2px;}");
a.add(new SomeObjectWithNode(g.addNode(Integer.toString(0))));
lastNode = g.getNode("0");
for(int i = 1; i < 10; i++)
{
a.add(new SomeObjectWithNode(g.addNode(Integer.toString(i))));
g.addEdge(Integer.toString(i-1).concat(Integer.toString(i)), a.get(i).n, lastNode);
lastNode = a.get(i).n;
}
t = new TestDraw(a,g);
while(true)
{
t.adjust();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Когда я запускаю пример, мы видим, что график имеет цвета только на мгновение вместо того, чтобы отображать его в течение определенного времени. Thread.sleep()
1 ответ
Основная проблема e.clearAttributes();
в методе adjust()
, Вы удаляете ВСЕ атрибуты не только "ui.color". Ты можешь использовать e.setAttribute("ui.color", randomColor);
вместо e.addAttribute("ui.color", randomColor);
и удалить блок с e.clearAttributes()
на все (см. оригинальный пример № 2).
Я изменил ваш код соответственно моим комментариям и поделился им на gist.github. Теперь он меняет цвет каждые 2 секунды.
PS: теперь о причине блинов. У меня было краткое исследование исходного кода GraphStream (много java-кода, извините).
AbstractElement.java (только интересные методы):
public void clearAttributes() {
if (attributes != null) {
for (Map.Entry<String, Object> entry : attributes.entrySet())
attributeChanged(AttributeChangeEvent.REMOVE, entry.getKey(),
entry.getValue(), null);
attributes.clear();
}
}
...
public void setAttribute(String attribute, Object... values) {
addAttribute(attribute, values);
}
...
public void addAttribute(String attribute, Object... values) {
if (attributes == null)
attributes = new HashMap<String, Object>(1);
Object oldValue;
Object value;
if (values.length == 0)
value = true;
else if (values.length == 1)
value = values[0];
else
value = values;
AttributeChangeEvent event = AttributeChangeEvent.ADD;
if (attributes.containsKey(attribute)) // In case the value is null,
event = AttributeChangeEvent.CHANGE; // but the attribute exists.
oldValue = attributes.put(attribute, value);
attributeChanged(event, attribute, oldValue, value);
}
...
Как видите, setAttribute и addAttribute действительно похожи. Теперь реализация attributeChanged() в GraphicElement.java:
@Override
protected void attributeChanged(AttributeChangeEvent event,
String attribute, Object oldValue, Object newValue) {
if (event == AttributeChangeEvent.ADD
|| event == AttributeChangeEvent.CHANGE) {
if (attribute.charAt(0) == 'u' && attribute.charAt(1) == 'i') {
if (attribute.equals("ui.class")) {
mygraph.styleGroups.checkElementStyleGroup(this);
// mygraph.styleGroups.removeElement( tis );
// mygraph.styleGroups.addElement( this );
mygraph.graphChanged = true;
} else if (attribute.equals("ui.label")) {
label = StyleConstants.convertLabel(newValue);
mygraph.graphChanged = true;
} else if (attribute.equals("ui.style")) {
// Cascade the new style in the style sheet.
if (newValue instanceof String) {
try {
mygraph.styleSheet.parseStyleFromString(
new Selector(getSelectorType(), getId(),
null), (String) newValue);
} catch (Exception e) {
logger.log(Level.WARNING, String.format("Error while parsing style for %S '%s' :", getSelectorType(), getId()), e);
}
mygraph.graphChanged = true;
} else {
logger.warning("Unknown value for style [" + newValue + "].");
}
} else if (attribute.equals("ui.hide")) {
hidden = true;
mygraph.graphChanged = true;
} else if (attribute.equals("ui.clicked")) {
style.pushEventFor(this, "clicked");
mygraph.graphChanged = true;
} else if (attribute.equals("ui.selected")) {
style.pushEventFor(this, "selected");
mygraph.graphChanged = true;
} else if (attribute.equals("ui.color")) {
style.pushElementAsDynamic(this);
mygraph.graphChanged = true;
} else if (attribute.equals("ui.size")) {
style.pushElementAsDynamic(this);
mygraph.graphChanged = true;
} else if (attribute.equals("ui.icon")) {
mygraph.graphChanged = true;
}
// else if( attribute.equals( "ui.state" ) )
// {
// if( newValue == null )
// state = null;
// else if( newValue instanceof String )
// state = (String) newValue;
// }
} else if (attribute.equals("label")) {
label = StyleConstants.convertLabel(newValue);
mygraph.graphChanged = true;
}
} else // REMOVE
{
if (attribute.charAt(0) == 'u' && attribute.charAt(1) == 'i') {
if (attribute.equals("ui.class")) {
Object o = attributes.remove("ui.class"); // Not yet removed
// at
// this point !
mygraph.styleGroups.checkElementStyleGroup(this);
attributes.put("ui.class", o);
mygraph.graphChanged = true;
} else if (attribute.equals("ui.label")) {
label = "";
mygraph.graphChanged = true;
} else if (attribute.equals("ui.hide")) {
hidden = false;
mygraph.graphChanged = true;
} else if (attribute.equals("ui.clicked")) {
style.popEventFor(this, "clicked");
mygraph.graphChanged = true;
} else if (attribute.equals("ui.selected")) {
style.popEventFor(this, "selected");
mygraph.graphChanged = true;
} else if (attribute.equals("ui.color")) {
style.popElementAsDynamic(this);
mygraph.graphChanged = true;
} else if (attribute.equals("ui.size")) {
style.popElementAsDynamic(this);
mygraph.graphChanged = true;
}
} else if (attribute.equals("label")) {
label = "";
mygraph.graphChanged = true;
}
}
}
Примечание. Как вы можете видеть при очистке атрибутов, динамический стиль будет добавлен / удален методами popElementAsDynamic/pushElementAsDynamic
, И реализации этих методов в StyleGroup.java:
/**
* Indicate the element has dynamic values and thus cannot be drawn in bulk
* operations. Called by the GraphicElement.
*
* @param element
* The element.
*/
protected void pushElementAsDynamic(Element element) {
if (dynamicOnes == null)
dynamicOnes = new HashSet<Element>();
dynamicOnes.add(element);
}
/**
* Indicate the element has no more dynamic values and can be drawn in bulk
* operations. Called by the GraphicElement.
*
* @param element
* The element.
*/
protected void popElementAsDynamic(Element element) {
dynamicOnes.remove(element);
if (dynamicOnes.isEmpty())
dynamicOnes = null;
}
Хорошо, когда мы добавляем /setAttribute, мы добавляем только новые элементы в существующую хэш-карту. Когда мы используем clearAttributes, мы создаем новые экземпляры HashMap для dynamicOnes
, Вероятно, в этом случае у нас есть проблема синхронизации, и это является причиной мигания.