title: Hex grid map with PyQt4
body: <p>I am trying to create a map editor. I intend the map to be an hexagonal grid where each hexagon is a tile of the map. A tile will be a graphical representation of that area (sea, meadow, desert, mountain, etc). The map is intended to be of any size. Let's freeze the requirements here for now :)</p>
  
  <p>I want to use PyQt4 (take it as a design requirement). As I am just starting with Qt/PyQt, I am facing the problem of vastness: so big this Qt thing that I cannot grasp it all. And here I am, asking for your kind and most welcome experience.</p>
  
  <p>After a bit of googling, I've decided to use the QGraphicalView/Scene approach. In fact, I was thinking about creating my own hexgrid class inheriting from QGraphicalView and creating my RegularPolygon class inheriting from QGraphicalPolygonItem. </p>
  
  <p>Now they come the doubts and problems. </p>
  
  <p>My main doubt is "Is my approach a correct one?" Think about the needs I have explained at the beginning of the post: hexagonal map, where each hexagon will be a tile of a given type (sea, desert, meadows, mountains, etc). I am concerned about performance once the editor works (scrolling will feel nice? and this kind of things).</p>
  
  <p>And so far, the problem is about precision. I am drawing the hexgrid by creating and drawing all its hexagons (this even sounds bad to me... thinking about performance). I used some formulas to calculate the vertices of each hexagon and creating the polygon from there. I expect the sides of two consecutive hexagons to coincide exactly at the same location, but the rounding seems to be playing a bit with my desires, as sometimes the hexagon sides perfectly matches in the same location (good) and sometimes they do not match by what seems to be 1 pixel difference (bad). This gives a poor visual impression of the grid. Maybe I have not explained myself quite well... it's better if I give you the code and you run it by yourselves</p>
  
  <p>So summarizing:</p>
  
  <ol>
  <li>Do you think my approach will give future performance issues?</li>
  <li>Why are not the hexagons placed exactly so that they share sides? How to avoid this problem?</li>
  </ol>
  
  <p>The code:</p>
  
  <pre><code>#!/usr/bin/python
  """
  Editor of the map.
  """
  
  __meta__ =  \
  {
      (0,0,1): (
                [ "Creation" ],
                [ ("Victor Garcia","vichor@xxxxxxx.xxx") ]
               )
  } 
  
  import sys, math
  from PyQt4 import QtCore, QtGui
  
  # ==============================================================================
  class HexGrid(QtGui.QGraphicsView):
      """
      Graphics view for an hex grid.
      """
  
      # --------------------------------------------------------------------------
      def __init__(self, rect=None, parent=None):
          """
          Initializes an hex grid. This object will be a GraphicsView and it will
          also handle its corresponding GraphicsScene.
              rect -- rectangle for the graphics scene.
              parent -- parent widget
          """
          super(HexGrid,self).__init__(parent)
  
          self.scene = QtGui.QGraphicsScene(self)
          if rect != None: 
              if isinstance(rect, QtCore.QRectF): self.scene.setSceneRect(rect)
              else: raise StandardError ('Parameter rect should be QtCore.QRectF')
          self.setScene(self.scene)
  
  # ==============================================================================
  class QRegularPolygon(QtGui.QGraphicsPolygonItem):
      """
      Regular polygon of N sides
      """
  
      def __init__(self, sides, radius, center, angle = None, parent=None):
          """
          Initializes an hexagon of the given radius.
              sides -- sides of the regular polygon
              radius -- radius of the external circle
              center -- QPointF containing the center
              angle -- offset angle in radians for the vertices
          """
          super(QRegularPolygon,self).__init__(parent)
  
          if sides &lt; 3: 
              raise StandardError ('A regular polygon at least has 3 sides.')
          self._sides = sides
          self._radius = radius
          if angle != None: self._angle = angle
          else: self._angle = 0.0
          self._center = center
  
          points = list()
          for s in range(self._sides):
              angle = self._angle + (2*math.pi * s/self._sides)
              x = center.x() + (radius * math.cos(angle))
              y = center.y() + (radius * math.sin(angle))
              points.append(QtCore.QPointF(x,y))
  
          self.setPolygon( QtGui.QPolygonF(points) )
  
  
  # ==============================================================================
  def main():
      """
      That's it: the  main function
      """
      app = QtGui.QApplication(sys.argv)
  
      grid = HexGrid(QtCore.QRectF(0.0, 0.0, 500.0, 500.0))
  
      radius = 50
      sides = 6
  
      apothem = radius * math.cos(math.pi/sides)
      side = 2 * apothem * math.tan(math.pi/sides)
  
      xinit = 50
      yinit = 50
      angle = math.pi/2
      polygons = list()
  
      for x in range(xinit,xinit+20):
          timesx = x - xinit
          xcenter = x + (2*apothem)*timesx
          for y in range(yinit, yinit+20):
              timesy = y - yinit
              ycenter = y + ((2*radius)+side)*timesy
  
              center1 = QtCore.QPointF(xcenter,ycenter)
              center2 = QtCore.QPointF(xcenter+apothem,ycenter+radius+(side/2))
  
              h1 = QRegularPolygon(sides, radius, center1, angle)
              h2 = QRegularPolygon(sides, radius, center2, angle)
  
              # adding polygons to a list to avoid losing them when outside the
              # scope (loop?). Anyway, just in case
              polygons.append(h1)
              polygons.append(h2)
  
              grid.scene.addItem(h1)
              grid.scene.addItem(h2)
  
      grid.show()
      app.exec_()
  
  # ==============================================================================
  if __name__ == '__main__':
      main()
  </code></pre>
  
  <p>and last but not least, sorry for the long post :)</p>
  
  <p>Thanks
  Victor</p>