2011-12-09 19 views
13

Se trata de generar una imagen, o cualquier otra representación, para un conjunto de paralelos datos. No se trata de dibujar o programar la GUI, sino calcular las posiciones. Primero explicaré un poco dónde estoy parado ahora y la segunda imagen y ejemplo muestra mi problema.Generación de una representación lineal, basada en la línea de tiempo, de elementos que consumen tiempo y elementos que no, pero aún necesitan espacio para dibujarse en

Estado actual

exampleOne-Easy http://www.wargsang.de/text3935.png

tengo objetos unidimensionales, sino que están alineados los coloca en paralelo "líneas". Llamemos a estos objetos unidimensionales "eventos" que tienen una "Duración" como una unidad de tiempo. Estos eventos tienen una variante donde no ocurre nada, objetos sin datos pero con una duración; Un objeto "gap".

Así que obtenemos un calendario con objetos simulativos que consisten en eventos y espacios, que es bastante fácil de manejar como tres listas de objetos. La visualización también es simple: Pasa el cursor por encima de las listas y dibuja cada objeto según su duración.

class Event(): 
    def __init__(self, duration, displacement = 0): #displacement is explained in the second example and the core problem of this question 
     self.duration = duration 
     self.displacement = displacement 
     #additional data 

    def self.draw(self, start_coordinate): 
     """draw duration * 10 pixels in black""" 
     #drawing code using start_coordinate to place the drawn object. see example graphic 
     return duration * 10 


class Gap(): 
    def __init__(self, duration, displacement = 0): 
     self.duration = duration 
     self.displacement = displacement 
     #no data 

    def self.draw(self, start_coordinate): 
     """draw duration * 10 pixels in transparent""" 
     #drawing code using start_coordinate to place the drawn object. see example graphic 
     return duration * 10 

row_one = [Event(1), Gap(1), Event(1), Gap(1), Event(1), Gap(1), Event(2)] 
row_two = [Event(1), Gap(2), Event(1), Event(1), Gap(1), Event(1), Gap(1), ] 
row_thr = [Gap(1), Event(1), Gap(1), Event(1), Gap(1), Event(3),] 

timetable = [row_one, row_two, row_thr] 

for row in timetable: 
    pixelcounter = 0 # the current position. 
    for item in row: 
     ret = item.draw(pixelcounter) #draw on the current position. Get how width the item was 
     pixelcounter += ret #save width for the next iteration   
    #instructions to move the "drawing cursor" down a few pixels so the next row does not overlap.  

El problema

ahora al problema. Hay objetos que necesitan un espacio gráfico pero tienen una duración cero. Lo llamo "Desplazamiento".

exampleTwo-Problematic http://www.wargsang.de/text4120.png

o, necesitamos objetos que tienen una duración, pero también tienen el desplazamiento. Esto todavía no es un problema cuando solo tenemos una fila, pero la sincronización de las filas es más compleja y no tengo una solución para esto.

En la imagen de arriba, los bloques rojos tienen una duración igual a cero y están desplazados. Los bloques azules tienen duración y están desplazados, también.

Ejemplo: * Imagine un cronograma para una conferencia con diferentes ranuras de bocinas cada hora (nuestros tragamonedas de duración). Cada fila representa una sala de conferencias diferente.

  • Los bloques negros son discursos y tal vez tienen un breve tema escrito dentro de ellos (gráficamente).

  • Los bloques azules también son discursos, pero el tema era demasiado largo para escribir, por lo que necesitamos más espacio una vez.

  • Rojo son notas como un cambio de número de habitación. No se toman el tiempo de su propia sino que se refieren a todos los artículos que vienen después de ellos. *

La tarea es encontrar una manera de Calulate la pixelcounter de la función anterior para que sea correcta para cada fila solo, pero también ese desplazamiento en una fila afecta a todas las otras filas y crea espacio adicional allí. El objetivo es que las duraciones sean fijas y estén alineadas en cada fila. Cualquier evento o brecha que debería comenzar, por ejemplo, recuento de unidades 4, debe comenzar en la misma posición absoluta.

Esto significa que cualquier objeto de duración/desplazamiento cero comienza en un punto real de tiempo/duración pero no consume tiempo/duración, de modo que todos los elementos subsiguientes comienzan en la misma duración hasta que se produce el siguiente evento de duración real incluido. Desde otro punto de vista, eso también significa que los elementos de duración cero comienzan siempre antes que los eventos que tienen duración.

En la imagen podemos ver un caso bastante simple en la columna 2, lo que también significa que comienza la segunda ranura de duración. A pesar de que hay tres eventos reales en esa columna que se desplazan a la derecha porque un elemento de desplazamiento está allí. La columna 4 tiene un elemento de duración que también tiene desplazamiento. Una vez más, todos los artículos que comienzan en la ranura 5 se desplazan hacia la derecha. Colum 6 es el problema más interesante y mi verdadero problema, no puedo encontrar una solución aquí. Una vez más, todos los eventos reales en la columna 6 se desplazan hacia la derecha y aún comienzan al mismo tiempo. Pero aquí tenemos a) Desplazamiento: objetos en dos filas y dos b) dos objetos uno detrás del otro. Por lo tanto, es importante que los eventos reales conozcan el desplazamiento completo, pero también es importante para el segundo objeto en la tercera fila saber que hay un elemento de desplazamiento más antes.

Advertencia: La representación gráfica puede sugerir un enfoque basado en tablas donde cada columna tiene un ancho individual. Pero aquí es donde termina este ejemplo. La aplicación real se refiere a una duración común de 300-10,000 por evento, pero las duraciones de 1 son improbables pero técnicamente posibles. Entonces, la tabla tendría un ancho de columna de una duración. Teniendo en cuenta que entramos en los cientos de miles de duración completa (multiplicado por el número de filas), esto puede reducir el rendimiento.

Los datos de esta imagen se verían así. ¿Cómo puedo dibujar la segunda imagen con esta información? O lo que debe cambiarse, estoy abierto a todas las sugerencias.

Muchas gracias por su tiempo e interés. Si no conoce una solución, no dude en hacerme preguntas o mostrarme los defectos de mi concepto, eso me ayudará a pensar también.

row_one = [ Event(1), #1 
      Event(0,1), Event(1), #2 
      Gap(1), #3 
      Event(1), #4 
      Gap(1), #5 
      Event(0,1), Event(1), #6 
      Event(1), #7 
      ] 
row_two = [ Event(1), #1 
      Event(1), #2 
      Gap(1), #3 
      Event(1, 0.5), #4, 0,5 is not important. we can also simply to just ints. The important bit is that it has both values. 
      Event(1), #5 
      Event(1), #6 
      Event(1), #7 
      ] 
row_thr = [ Event(1), #1 
      Event(1), #2 
      Event(1), #3    
      Event(1), #4 
      Event(1), #5    
      Event(0,1), Event(0,1), Event(1), #6 #Please pay attention to this case. 
      Event(1), #7 
      ] 
+0

Bueno, antes que nada, ¿estás seguro de que tienes que tener espacio para ellos? Puede dibujarlos de manera diferente, en lugar de barras de tiempo que tienen otras formas de mostrarlos. –

+0

Tristemente, sí, los necesito a todos en una fila. – nilsge

+0

Dado todos los objetos con un número de fila, un ancho gráfico y ancho de tiempo: necesitará encontrar las posiciones gráficas para cada objeto, de modo que todas las filas se ajusten gráficamente (sin superposiciones) y todos los mismos tiempos de inicio comiencen en el mismo gráfico posición. ¿Es eso correcto? – Ishtar

Respuesta

0

Necesita una función para asignar su tiempo a la coordenada x. La parte difícil es que a veces necesitas más espacio en un momento. Esto se puede resolver con una lista de objetos, que indica cuándo y cuánto espacio adicional se necesita.

function timeToLocation(t) 
    location = t * scale 
    for (o : extraSpaceList) 
    if o.when < t 
     location = location + o.space 
    return location 

Siempre que se trate de colocar un objeto, y nota que no hay suficiente espacio (ya que los elementos se superponen), basta con insertar algo más de espacio en el momento deseado, extraSpaceList.add({when=2s,space=4pixels}). Procese todas las filas una a una, y luego todas nuevamente para la ubicación final.

Esto es más fácil si transforma sus objetos para tener una hora de inicio y un tamaño gráfico. Entonces no hay diferencia entre Gap y Event.

0

Si entiendo su pregunta ...

Para cada columna, no perder de vista la anchura máxima de las filas cajas cero duración que usted está haciendo un procesamiento inicial de cada fila. Cuando finalmente dibuja cada evento real, comienza al final del valor de ancho máximo para su columna. (Supongo que ya está calculando dónde está el inicio de la columna.)

En su colorido gráfico anterior, procesar la fila 1 le daría anchos máximos de duración cero (por ejemplo) [0, 10, 0, 0, 0, 10, 0]. La fila 2 no lo cambiaría. La fila 3 lo cambiaría a [0, 10, 0, 0, 0, 20, 0].

Cuando dibuja un evento real, comenzará en su column_start + column max-zero-duration-width.

0

Veo un par de problemas con su código de evento tal como está: 1) no hay forma de decir cuánto espacio asignar para el desplazamiento, si el evento es de duración cero o no; 2) si tienes (digamos en la fila cuatro) un evento que toma 3 unidades y comienza en 5, se dibujará completamente en las ranuras 5 y 6 sin llegar a 7.

Sospecho que necesitarás algo como:

class Event(object): 
    def __init__(self, duration, displacement=0): 
     self.duration = duration 
     self.displacement = displacement 
     self.width = max(STANDARD_WIDTH, duration+displacement) 
     #additional data 

    def self.draw(self, start_coordinate): 
     """draw duration * 10 pixels in black""" 
     return self.width * 10 

sin el tratamiento de la gráfica como una mesa, no estoy seguro de cómo llegar en torno a la segunda cuestión que planteé anteriormente.

Además, se necesitarán dos pasos: el primer pase para descubrir los distintos anchos de columna de sus bloques de horas, el segundo para usar esa información para dibujar las filas.

1

No estoy completamente seguro, pero creo que lo que quiere decir:

  • desplazamientos en diferentes filas que tienen la misma hora de inicio están sincronizados, es decir, tienen la misma posición horizontal (el primer desplazamiento en cada fila está sincronizado con los primeros desplazamientos en otras filas)
  • eventos "reales" y vacíos que tienen el mismo hora de inicio están sincronizados, y vienen después de los desplazamientos con el mismo tiempo de partida
  • de la anchura de un evento depende de su duración (y, posiblemente, su desplazamiento), pero no en desplazamientos en otras filas, es decir, los veces que terminan no están sincronizados

Si desea veces terminando para ser sincronizado , tendrías que explicar cómo; pero no veo ninguna manera obvia.

se obtiene la solución para su problema original siguientes (capitales = eventos, letras minúsculas = desplazamientos, puntos = lagunas, Espacio = times "en espera de la sincronización", los números están empezando de eventos):

0 123 4 567 
AbC.D .e FG 
A B.CcD EF 
A BCD EfgHI 

se puede ver que los tiempos que terminan no están sincronizados en el siguiente ejemplo:

0 12 
AAa 
Aa B 
AaaB 

Y un ejemplo al azar más grande:

      11 11 1 1  1  1 1 1 22 22 2  2 22 22 33 333 3 3 3 3 3 4 44444 4 4 4 45 
    01 2 34 5678 9 01 23 4 5  6  7 8 9 01 23 4  5 67 89 01 234 5 6 7 8 9 0 12345 6 7 8 90 
    AAAA BB CCC dd.. EEe Fff..  GGGGGg   ....   ... HHH ....  IIii JJJ  ... KKK LLLLl 
abbbCCC DDDDDdd ..  EEEEE  Fff GGG   HHH IIIii  JJJjj KKKK  LLLl Mno. PPP qR SSSSSs TT uuuVV 
    ... AAAAA BBB  CC DDDD    ...  EE FFFF   GHhhIIII  JJ. K Lll.m....  NNNO .... 
    ......  AAAA  .. ....  BBB   CCCCCc  DDDDDd  Ee FFFFff G hhhIIIII   JJJ KLLLLLll  M 
    .. AAA BBBCcc DD EE .. FFF   gH IIIIIi  J  KKk LL MMMMM  NNNNNn   OOo PPQQQQ rrr... 
    AAAAa . BBBBbb CCCCC  DDDDDd   eeeFFFFF  GG  HH .....  IIIII   JJ K LM.NNNNN   . 
    AAAaaBBB CCCcc DDDDDdd  EeFF   ...  GGgHHHH   III JJJJ  KKK llMMMm nnnOOOO PPPp ...  Q 
    AAAAA  BBBBB  CCCC  .....    DDD EEEEE   FFFff .... GGGG   HHHHhh  II....  j . . 
    AAAaa.. BBBBbb CccDDDDD  ....    EEE .F GgghhhII Jj KKKK  ... ...  LLll ... MMMM  N OooP 
    .... Aa ..BCCC  .....   DDD   EEEe FFf .....   GGGG  HIIIIIii   . JJ .... KKk  LL 
    AAAAAa bbC.....  DDDDD   ....   eeFFFFff GGGGG   ... hh IIJJJ  KKK  L MMMMMmmNNNN 
    ..aBBB CCCCc .....  .....    ... D. E  FFFFFff ggHHhiiiJKKKk  LLLLL  mmmNNNOP Q RRR 
    AA BbCCCC DD Ee FFFFFff  GGGGG     HH IIIi  JjjK.. LLLll  MMMMmm ....  . NNNOOOOOoo  P 
    AB CCCCC .....  ddEEEE  fffGgg HHHHHhh  II jjKKKK   LLLL  MMMM nn.. OO PPPPPpp QQQQQqq 
    AAA BBB CCCC  DDdd EE FFF  gggHh IIIii JJJJ   K LLLLl MMm NNOOOO   . PP .QQQRRRRR 

Y ahora el código (lo siento, es tan largo, mira Timetable.__init__ para la parte interesante, el resto es bastante bonito de impresión).

from heapq import merge 
from itertools import groupby, cycle, chain 
from collections import defaultdict 
from operator import attrgetter 
from string import ascii_uppercase 

# events are processed in this order: 
# increasing start time, displacements (duration=0) first, and grouped by row_id 
ev_sort_attrs = attrgetter("time", "duration", "row_id") 


class Event: 

    def __init__(self, duration, displacement=0, visible=True, time=None, row_id=None): 
     self.duration = duration 
     self.displacement = displacement 
     self.visible = visible 
     self.time = time 
     self.row_id = row_id 
     self.pos = None 

    def draw(self, char): 
     return char * self.duration + char.lower() * self.displacement 

    def __lt__(self, other): 
     return ev_sort_attrs(self) < ev_sort_attrs(other) 


def Gap(duration): 
    return Event(duration, visible=False) 


class Timetable(list): 
    def __init__(self, *args): 
     """ compute positions for a list of rows of events """ 
     list.__init__(self, *args) 

     # compute times for the events, and give them row_ids 
     for i, row in enumerate(self): 
      t = 0 
      for ev in row: 
       ev.time = t 
       t += ev.duration 
       ev.row_id = i 

     # map times to position for displacements and event 
     t2pos_disp = defaultdict(int) # maps times to position of synchronized start of displacements 
     t2pos_ev = defaultdict(int) # maps times to position of synchronized start of events and gaps 

     # the real work is done in the following loop 
     t_prev = 0 
     for t, g in groupby(merge(*self), key=attrgetter("time")): 

      # different times should have a minimum distance corresponding to their difference 
      t2pos_ev[t] = t2pos_disp[t] = max(t2pos_ev[t], t2pos_ev[t_prev] + t - t_prev) 
      t_prev = t 

      for (duration, row_id), g_row in groupby(g, key=attrgetter("duration", "row_id")): # process all displacements first, then the events 
       pos_ev = t2pos_ev[t] if duration > 0 else t2pos_disp[t] # events and displacements start at different 
       for ev in g_row: 
        ev.pos = pos_ev 
        pos_ev += ev.duration + ev.displacement 
       t2pos_ev[t + ev.duration] = max(t2pos_ev[t + ev.duration], pos_ev) 

     # keep our results... 
     self.t2pos_ev = t2pos_ev 
     self.t2pos_disp = t2pos_disp 


    @staticmethod 
    def str_row(row): 
     """ draw row, uppercase letters for real events, lower case letters for 
     displacements, dots for gaps""" 

     ev_chars = cycle(ascii_uppercase) 
     out = [] 
     l = 0 
     for ev in row: 
      if ev.pos > l: 
       out.append(" " * (ev.pos - l)) 
      out.append(ev.draw(next(ev_chars) if ev.visible else ".")) 
      l = ev.pos + len(out[-1]) 
     return "".join(out) 

    def __str__(self): 
     max_t, max_p = max(self.t2pos_ev.items()) 
     w = len(str(max_t)) 
     header_temp = [" " * w] * (max_p + 1) 
     for t, p in self.t2pos_ev.items(): 
      header_temp[p] = "%*d" % (w, t) 
     headers = ("".join(header) for header in zip(*header_temp)) 

     rows = (self.str_row(row) for row in self) 

     return "\n".join(chain(headers, rows)) 


if __name__ == "__main__": 
    # original example 
    row_one = [Event(1), Event(0,1), Event(1), Gap(1), Event(1), Gap(1), Event(0,1), Event(1), Event(1)] 
    row_two = [Event(1), Event(1), Gap(1), Event(1, 1), Event(1), Event(1), Event(1)] 
    row_thr = [Event(1), Event(1), Event(1), Event(1), Event(1), Event(0,1), Event(0,1), Event(1), Event(1)] 

    timetable = Timetable([row_one, row_two, row_thr]) 
    print(timetable) 

    print("-" * 80) 

    # short example, shows ending times are not synchronized 
    print(Timetable([[Event(2, 1)], [Event(1, 1), Event(1)], [Event(1, 2), Event(1)]])) 

    print("-" * 80) 

    # larger random example 
    def random_row(l): 
     import random 
     res = [] 
     t = 0 
     while t < l: 
      x = random.random() 
      if x < 0.1: res.append(Event(0, random.randint(1, 3))) 
      elif x < 0.8: res.append(Event(min(random.randint(1, 5), l - t), random.randint(0, 1) * random.randint(0, 2))) 
      else: res.append(Gap(min(random.randint(1, 5), l - t))) 
      t += res[-1].duration 
     return res 

    print(Timetable([random_row(50) for _ in range(15)])) 
-1

Recomiendo usar un icono para el evento en sí y una barra para la duración.

Cuestiones relacionadas