simray.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. # -*- coding: utf-8 -*-
  2. #---Standard library imports
  3. import math
  4. from collections import namedtuple
  5. import time
  6. import threading
  7. import sys
  8. import os
  9. import subprocess
  10. import logging;
  11. logging.basicConfig(level=logging.DEBUG)
  12. log = logging.getLogger()
  13. #---Third-party imports
  14. import PyQt4
  15. from PyQt4 import QtGui, QtCore
  16. from scipy.misc import fromimage, imfilter, pilutil
  17. import numpy as np
  18. import scipy.ndimage.filters as image_filters
  19. from PIL import Image, ImageFilter, ImageDraw
  20. sys.path.append(os.path.abspath('../'))
  21. from utils.path import path
  22. from utils.xml import NS_MAP
  23. from utils import extract as np_extract, getPyPlot, cython_annotate
  24. from utils.qt import NumpyModel, QTLoggingHandler, MaterialModel
  25. from utils.objparser import Mesh
  26. from utils.materials import loadMaterials
  27. from intersect import Mesh2D
  28. import pyximport; pyximport.install(setup_args={'include_dirs': [np.get_include()]})
  29. from _gpu import dostuff
  30. np.set_printoptions(linewidth=200, threshold=1000)
  31. #---Framework imports
  32. cython_annotate('_simulator.pyx')
  33. from _simulator import Simulation
  34. REBUILD_UI = True
  35. if REBUILD_UI:
  36. # rebuild ui file
  37. f = path(PyQt4.__file__).parent.joinpath('uic/pyuic.py')
  38. try:
  39. subprocess.check_call([sys.executable, f, 'simray.ui', '-o', 'ui_simray.py'])
  40. except subprocess.CalledProcessError:
  41. log.exception('Error during pyqt compiling')
  42. # import from build ui file
  43. from ui_simray import Ui_MainWindow
  44. #---Globals
  45. INTENSITY = 0.0000002
  46. OFFSET_X = OFFSET_Y = 0
  47. tmpDir = path('tmp')
  48. #---Functions
  49. class Source(object):
  50. def __init__(self, x, y, power, idx):
  51. self.x = x
  52. self.y = y
  53. self.power = power
  54. self.idx = idx
  55. class Config(object):
  56. # [rays]
  57. rayangle = 38
  58. raycount = 2000
  59. max_generations = 6
  60. # [map]
  61. pixel_per_meter = 16
  62. # auto calculated during loadmesh
  63. width = None
  64. height = None
  65. alphachannel = 150
  66. bgmap = True
  67. reflections = True
  68. refractions = True
  69. gausssigma = 5
  70. # files
  71. meshfile = path('../../maps/umic/umic.obj')
  72. measurementsfile = path(r'd:\loco-dev\maps\umic\experiment\measure.txt')
  73. mesh_xlim = (-1, 58)
  74. mesh_ylim = (-1, 18)
  75. config = Config()
  76. def smoothMap(Z, maxpower):
  77. #~ Z = Z / maxpower # normalize to max=1.0
  78. Z = 10 * np.log10(Z)
  79. # filter values that are -inf'ed by np.log(0)
  80. #~ Z[np.isinf(Z)] = -200
  81. Z[np.isinf(Z)] = -130 #-np.min(np.isfinite(Z))
  82. Z = image_filters.gaussian_filter(Z, config.gausssigma)
  83. return Z
  84. class MeshLoader(object):
  85. def __init__(self):
  86. self.objfile = None
  87. self.m2d = None
  88. self.mesh = None
  89. def load(self, objfile):
  90. self.objfile = path(objfile)
  91. self.mesh = Mesh()
  92. self.mesh.parseObjFile(self.objfile)
  93. self.m2d = Mesh2D(self.mesh, config.mesh_xlim, config.mesh_ylim, config.pixel_per_meter)
  94. config.width = self.m2d.width
  95. config.height = self.m2d.height
  96. meshLoader = MeshLoader()
  97. meshLoader.load(config.meshfile)
  98. class Renderer(object):
  99. def __init__(self):
  100. source1 = Source(580, 55, INTENSITY, 0)
  101. source2 = Source(500, 80, INTENSITY, 1)
  102. source3 = Source(810, 160, INTENSITY, 2)
  103. source4 = Source(60, 200, INTENSITY, 3)
  104. #~ self.sources = [source1, source2, source3, source4]
  105. self.sources = [source1]
  106. self.source_signals = [] # key: source id, value: smothed signal map
  107. self.needReplot = False
  108. self.needSimUpdate = threading.Event()
  109. self.renderLoop = threading.Thread(target=self._render)
  110. self.renderLoop.daemon = True
  111. self.renderLoop.start()
  112. self.plotLock = threading.Lock()
  113. self.imageBufferPtr = None
  114. self.envmap = np.zeros([config.width, config.height], dtype=np.int16)
  115. self.materials = [] # holds material data for each object
  116. def _runsim(self, verbose=True):
  117. t = time.time()
  118. self.sim = Simulation(config.width, config.height, config.max_generations,
  119. config.reflections, config.refractions, self.sources,
  120. self.materials, config.pixel_per_meter)
  121. self.sim.initRays(config.raycount, config.rayangle)
  122. init_time = time.time() -t
  123. t = time.time()
  124. #~ rays = self.sim.rays[0].toNumpy()
  125. #~ maparray = dostuff(rays, self.sim.map)
  126. #~ print 'gpu: %.3f sec' % (time.time() - t)
  127. self.sim.build(self.envmap)
  128. render_time = time.time() -t
  129. t = time.time()
  130. self.sim.map.writeBGRA(self.imageBufferPtr, self.envmap_, config.alphachannel)
  131. draw_time = time.time() - t
  132. if verbose:
  133. s = '%.3f init, %.3f render, %.3f draw [%.2f frames/sec]' % (init_time, render_time, draw_time, 1 / (render_time + draw_time + 1e-9))
  134. log.info(s)
  135. #~ s = ', '.join('g%s:%sr/%sms' % (i, rays.count, int(duration*1000))
  136. #~ for i, rays, duration in self.sim.generations[:20])
  137. #~ log.debug(s)
  138. def _render(self):
  139. try:
  140. while True:
  141. self.needSimUpdate.wait()
  142. if self.imageBufferPtr is None:
  143. raise ValueError('imagebuffer not connected')
  144. self._runsim()
  145. signals.newSimulationData.emit()
  146. self.needSimUpdate.clear()
  147. if self.needReplot:
  148. self._plot()
  149. self.needReplot = False
  150. except Exception:
  151. log.exception('error during rendering')
  152. def _plot(self):
  153. plt, mlab, dates, font_manager, ticker = getPyPlot()
  154. map = self.sim.map
  155. def _work():
  156. with self.plotLock:
  157. log.debug('plotting...')
  158. self.source_signals = []
  159. t = time.time()
  160. dpi = 96
  161. fig = plt.figure(figsize=(float(config.width) / dpi, float(config.height) / dpi))
  162. try:
  163. #~ ax = plt.axes([0.10, 0.10, 0.89, 0.89])
  164. ax = plt.axes([0.00, 0.00, 1.0, 1.0], axisbg='black')
  165. x = np.arange(0, map.width, 1)
  166. y = np.arange(0, map.height, 1)
  167. X, Y = np.meshgrid(x, y)
  168. gauss_duration = 0
  169. for i in range(len(map.sources)):
  170. _t = time.time()
  171. Z = map.asArray(i, invertY=True)
  172. Z = smoothMap(Z, map.sources[i].power)
  173. gauss_duration += (time.time() - _t)
  174. #~ Z = image_filters.median_filter(Z, size=(5,5))
  175. try:
  176. levels = range(-110, -50, 5)
  177. #~ levels = range(-110, 10, 10)
  178. ax.contour(X, Y, Z, levels)
  179. except ValueError:
  180. pass
  181. self.source_signals.append(Z)
  182. fig.savefig(tmpDir.joinpath('analysis.png'), format='png', dpi=dpi)
  183. log.info('plotted map in %.3f sec [gauss: %.3f sec]' % (time.time() - t, gauss_duration))
  184. finally:
  185. plt.close(fig)
  186. signals.newPlotAvailable.emit()
  187. threading.Thread(target=_work).start()
  188. def setImage(self, image):
  189. '''the a QImage instance that should be used for drawing'''
  190. self.imageBufferPtr = image.bits()
  191. def update(self, withPlot=False):
  192. if withPlot:
  193. self.needReplot = True
  194. self.needSimUpdate.set()
  195. def saveEnvMap(self, fname):
  196. self.envmap_ = self.envmap.transpose().ravel()
  197. envmap = self.envmap.transpose()
  198. colors = [(255, 0, 0, 255), (0, 255, 0, 255),
  199. (0, 0, 255, 255), (255, 255, 255, 255),
  200. (255, 0, 255, 255), (128, 128, 128, 255)]
  201. img = Image.new('RGBA', (config.width, config.height), color=0)
  202. pixels = img.load()
  203. for x in range(config.width):
  204. for y in range(config.height):
  205. if envmap[y][x] > 0:
  206. idx = envmap[y][x] % len(colors)
  207. pixels[x, y] = colors[idx]
  208. log.debug('saving envmap to %s' % path(fname).abspath())
  209. img.save(fname)
  210. renderer = Renderer()
  211. class Signals(QtCore.QObject):
  212. ''' signals have to be classlevel attributes of a QObject
  213. so we need to define them here
  214. '''
  215. logMsg = QtCore.pyqtSignal(str)
  216. newPlotAvailable = QtCore.pyqtSignal()
  217. newAnalysisMap = QtCore.pyqtSignal(str)
  218. newSimulationData = QtCore.pyqtSignal()
  219. renderMapMouseMove = QtCore.pyqtSignal(int, int, tuple)
  220. renderMapSelectRect = QtCore.pyqtSignal(tuple)
  221. signals = Signals()
  222. class RenderedMap(QtGui.QLabel):
  223. ''' acts as drawing pane for the renderer data
  224. and as mouse controller for moving the sources
  225. '''
  226. def __init__(self, parent, mainform):
  227. QtGui.QLabel.__init__(self, parent)
  228. self.setGeometry(0, 0, config.width, config.height)
  229. self.image = QtGui.QImage(config.width, config.height, QtGui.QImage.Format_ARGB32)
  230. # tell the renderer where to store the image data
  231. renderer.setImage(self.image)
  232. self.setMouseTracking(True)
  233. self.dragged_source = None
  234. signals.newSimulationData.connect(self.onNewSimulationData)
  235. self.mainform = mainform
  236. self.mouseMode = 'dragSources'
  237. self.selected_rect = None
  238. self.inSelect = False
  239. def onNewSimulationData(self):
  240. if (self.selected_rect is not None and self.dragged_source is None):
  241. self.selectRect(self.selected_rect)
  242. else:
  243. self.reloadImageBuffer()
  244. def reloadImageBuffer(self):
  245. self.setPixmap(QtGui.QPixmap.fromImage(self.image))
  246. def mousePressEvent(self, event):
  247. x = event.pos().x()
  248. y = event.pos().y()
  249. if self.mouseMode == 'dragSources':
  250. min_dist, min_dist_source = 99999, None
  251. for source in renderer.sources:
  252. d = math.sqrt((source.x - x)**2 + (source.y - y)**2)
  253. if d < min_dist:
  254. min_dist, min_dist_source = d, source
  255. self.dragged_source = min_dist_source
  256. elif self.mouseMode == 'zoomRect':
  257. if self.inSelect:
  258. self._zoomRectEndSelect(x, y)
  259. else:
  260. self.inSelect = True
  261. self.selected_rect = [x, y, None, None]
  262. #~ elif self.mouseMode == 'hover':
  263. #~ self._inspectPosition(x, y)
  264. def _normalizeRect(self, r):
  265. # ensure x1 < x2 and y1 < y2
  266. r = list(r)
  267. if r[2] < r[0]:
  268. r[0], r[2] = r[2], r[0]
  269. if r[3] < r[1]:
  270. r[1], r[3] = r[3], r[1]
  271. return r
  272. #~ def _inspectPosition(self, x, y):
  273. #~ w = h = 100
  274. #~ r = [x-int(w/2), y-int(h/2), x+int(w/2), y+int(h/2)]
  275. #~ self.selectRect(r)
  276. def _inspectSection(self, r):
  277. map = renderer.sim.map
  278. w = r[2] - r[0]
  279. h = r[3] - r[1]
  280. x = r[0] + w / 2
  281. y = r[1] + h / 2
  282. #~ mode = 'smoothed'
  283. #~ mode = 'raw'
  284. mode = 'decibel_from_raw'
  285. if mode == 'smoothed':
  286. a = smoothMap(map.asArray(0), map.sources[0].power)
  287. elif mode in ('raw', 'decibel_from_raw'):
  288. a = map.asArray(0)
  289. a = np_extract(a, (h, w ), (y, x), fill=np.nan)
  290. if map.width / float(w) > map.height / float(h):
  291. size = (map.height, int(float(w) * map.height / h))
  292. else:
  293. size = (int(float(h) * map.width / w), map.width)
  294. offset = x - int(w / 2), y - int(h / 2)
  295. mainform.ui.tableViewArray.setModel(NumpyModel(a, offset, mode))
  296. a = pilutil.imresize(a, size, interp='nearest')
  297. pilutil.imsave('tmp/zoomrect.png', a)
  298. signals.newAnalysisMap.emit('tmp/zoomrect.png')
  299. def _zoomRectEndSelect(self, x, y):
  300. r = self.selected_rect
  301. self.selected_rect = r = self._normalizeRect([r[0], r[1], x, y])
  302. signals.renderMapSelectRect.emit(tuple(r))
  303. log.info('selected:%s,%s,%s,%s' % (r[0], r[2], r[1], r[3]))
  304. self._inspectSection(r)
  305. self.inSelect = False
  306. def mouseReleaseEvent(self, event):
  307. x = event.pos().x()
  308. y = event.pos().y()
  309. if self.mouseMode == 'dragSources':
  310. log.info('moved source to %s,%s' % (x, y))
  311. self.dragged_source = None
  312. renderer.update(withPlot=True)
  313. elif self.mouseMode == 'zoomRect':
  314. self._zoomRectEndSelect(x, y)
  315. def selectRect(self, r):
  316. self.selected_rect = r
  317. renderer.sim.map.drawRect(r[0], r[1], r[2], r[3])
  318. self.reloadImageBuffer()
  319. self._inspectSection(r)
  320. def mouseMoveEvent(self, event):
  321. x = event.pos().x()
  322. y = event.pos().y()
  323. if self.mouseMode == 'dragSources':
  324. if self.dragged_source:
  325. self.dragged_source.x = x
  326. self.dragged_source.y = y
  327. if self.dragged_source is not None:
  328. renderer.update()
  329. elif self.mouseMode == 'zoomRect':
  330. r = self.selected_rect
  331. if self.inSelect:
  332. nr = self._normalizeRect([r[0], r[1], x, y])
  333. signals.renderMapSelectRect.emit(tuple(nr))
  334. renderer.sim.map.drawRect(nr[0], nr[1], nr[2], nr[3])
  335. self.reloadImageBuffer()
  336. elif self.mouseMode == 'measure':
  337. # TODO: interface slowdown?
  338. sourceVector = tuple(sig[config.height-y][x] for sig in renderer.source_signals)
  339. signals.renderMapMouseMove.emit(x, y, sourceVector)
  340. class MainForm(QtGui.QMainWindow):
  341. def __init__(self, parent=None):
  342. QtGui.QWidget.__init__(self, parent)
  343. self.ui = Ui_MainWindow()
  344. self.ui.setupUi(self)
  345. self.setupFeedbackTree()
  346. self.loadConfig()
  347. self.ui.actionExit.triggered.connect(lambda: self.close())
  348. self.ui.actionBenchmark.triggered.connect(self.benchmark)
  349. self.ui.actionMoveSources.setChecked(True)
  350. self.ui.actionMoveSources.triggered.connect(self.toggleMouseMode('dragSources'))
  351. self.ui.actionZoomRect.triggered.connect(self.toggleMouseMode('zoomRect'))
  352. self.ui.actionHover.triggered.connect(self.toggleMouseMode('measure'))
  353. self.loadMaterials()
  354. self.loadEnvMap()
  355. self.setupLogger()
  356. if not tmpDir.exists():
  357. tmpDir.mkdir()
  358. self.renderedMap = RenderedMap(self.ui.previewImage, self)
  359. self.loadBackgroundForRenderMap()
  360. signals.newPlotAvailable.connect(self.onNewPlotAvailable)
  361. signals.newAnalysisMap.connect(self.onNewAnalysisImage)
  362. signals.renderMapMouseMove.connect(self.onRenderMapMouseMove)
  363. signals.renderMapSelectRect.connect(self.onRenderMapRectSelect)
  364. renderer.update(withPlot=True)
  365. def toggleMouseMode(self, mode):
  366. def _inner():
  367. self.renderedMap.mouseMode = mode
  368. if mode != 'dragSources':
  369. self.ui.actionMoveSources.setChecked(False)
  370. if mode != 'zoomRect':
  371. self.ui.actionZoomRect.setChecked(False)
  372. if mode != 'measure':
  373. self.ui.actionHover.setChecked(False)
  374. if mode in ('zoomRect', 'measure'):
  375. self.renderedMap.selected_rect = None
  376. return _inner
  377. def benchmark(self):
  378. log.info('starting benchmark...')
  379. total = 150
  380. t = time.time()
  381. for i in range(total):
  382. renderer._runsim(verbose=False)
  383. avgdur = (time.time() - t) / total
  384. log.info('%s runs - avg duration: %.3f/sec [%.2f frames/sec]' % (total, avgdur, avgdur**-1))
  385. def setupFeedbackTree(self):
  386. p = QtGui.QTreeWidgetItem(self.ui.treeFeedback)
  387. p.setText(0, 'Position')
  388. e = self.feedback_posx = QtGui.QTreeWidgetItem()
  389. p.addChild(e)
  390. e.setText(0, 'x')
  391. e = self.feedback_posy = QtGui.QTreeWidgetItem()
  392. p.addChild(e)
  393. e.setText(0, 'y')
  394. self.ui.treeFeedback.expandItem(p)
  395. p = QtGui.QTreeWidgetItem(self.ui.treeFeedback)
  396. p.setText(0, 'Sources')
  397. self.feedback_sources = {}
  398. for i, source in enumerate(renderer.sources):
  399. e = QtGui.QTreeWidgetItem()
  400. p.addChild(e)
  401. e.setText(0, str(i))
  402. self.feedback_sources[i] = e
  403. self.ui.treeFeedback.expandItem(p)
  404. self.feedback_rect_parent = p = QtGui.QTreeWidgetItem(self.ui.treeFeedback)
  405. p.setText(0, 'SelectedRect')
  406. self.feedback_rect = []
  407. for s in ('x1', 'y1', 'x2', 'y2'):
  408. e = QtGui.QTreeWidgetItem()
  409. p.addChild(e)
  410. e.setText(0, s)
  411. self.feedback_rect.append(e)
  412. def loadEnvMap(self):
  413. ''' load map from mesh file and sets self.envmap'''
  414. #~ zz = np.zeros([config.width, config.height], dtype=np.int16)
  415. t = time.time()
  416. CUT_AT_Z = 1.0
  417. for i, objname in enumerate(meshLoader.m2d.objnames, 1):
  418. #~ if objname != 'Light_Walls_M4':
  419. #~ continue
  420. log.debug('loading object %s: %s' % (i, objname))
  421. fn = 'cut_%s_%s_%s.png' % (objname, ','.join(str(e) for e in config.mesh_xlim + config.mesh_ylim), config.pixel_per_meter)
  422. cachefile = tmpDir.joinpath(fn)
  423. if not cachefile.exists() or cachefile.mtime < meshLoader.objfile.mtime:
  424. img = meshLoader.m2d.cut(objname, CUT_AT_Z)
  425. log.debug('cache file to %s' % cachefile.abspath())
  426. img.save(cachefile)
  427. else:
  428. log.debug('from cached %s' % cachefile.abspath())
  429. img = Image.open(cachefile)
  430. #~ if img is None:
  431. #~ log.debug('cutplane for object %s is empty' % objname)
  432. #~ continue
  433. edges = fromimage(img)
  434. e = edges.astype(np.int16).transpose()
  435. shapediff = renderer.envmap.shape[0] - e.shape[0]
  436. both = (e, np.zeros((shapediff, e.shape[1])))
  437. e = np.vstack(both)
  438. shapediff = renderer.envmap.shape[1] - e.shape[1]
  439. both = (e, np.zeros((e.shape[0], shapediff)))
  440. e = np.hstack(both)
  441. e = np.clip(e, 0, 1) * i # set every entry that is greater 0 to layerindex
  442. # user first array as condition - if entry of forst array is greater 0 use that entry - else the stored value
  443. # effect: copy layer over layer
  444. renderer.envmap = np.where(e, e, renderer.envmap).astype(np.int16)
  445. log.info('loaded layer in %.3f sec' % (time.time() -t))
  446. renderer.saveEnvMap('tmp/envmap_edges.png')
  447. def loadMaterials(self):
  448. materials = loadMaterials('../../maps/umic/materials.xml')
  449. def reloadMaterials():
  450. renderer.materials[:] = []
  451. for objname in meshLoader.m2d.objnames:
  452. matname = meshLoader.mesh.materials[objname]
  453. log.debug('using material %s' % matname)
  454. reflection, refraction = materials[matname].reflect, materials[matname].alpha
  455. renderer.materials.append((reflection, refraction))
  456. reloadMaterials()
  457. def newMaterialParams():
  458. reloadMaterials()
  459. renderer.update(withPlot=True)
  460. model = MaterialModel(materials)
  461. self.ui.tableMaterials.setModel(model)
  462. model.onChange = newMaterialParams
  463. def loadConfig(self):
  464. def onChange(cfgname, translate, check=None):
  465. def _inner(value):
  466. try:
  467. value = translate(value)
  468. except ValueError:
  469. pass
  470. else:
  471. if check is None or check(value):
  472. setattr(config, cfgname, value)
  473. renderer.update(withPlot=True)
  474. return _inner
  475. cfg = [('spinBoxRayAngle', 'rayangle'), ('spinBoxRayCount', 'raycount'),
  476. ('spinBoxGenerations', 'max_generations'), ('spinBoxAlphaChannel', 'alphachannel'),
  477. ('spinBoxGaussSigma', 'gausssigma'),
  478. ('checkBoxBGMap', 'bgmap'), ('checkBoxReflections', 'reflections'), ('checkBoxRefractions', 'refractions')]
  479. for ui_name, cfg_name in cfg:
  480. e = getattr(self.ui, ui_name)
  481. if isinstance(e, PyQt4.QtGui.QSpinBox):
  482. e.setValue(getattr(config, cfg_name))
  483. e.valueChanged.connect(onChange(cfg_name, int, lambda v: 0 < v < 10000))
  484. elif isinstance(e, PyQt4.QtGui.QCheckBox):
  485. e.setCheckState(2 if getattr(config, cfg_name) else 0)
  486. e.stateChanged.connect(onChange(cfg_name, lambda v: True if v else False))
  487. for qtimg in (self.ui.previewImage, self.ui.analysisImage):
  488. qtimg.setMaximumSize(config.width, config.height)
  489. qtimg.setMinimumSize(config.width, config.height)
  490. # load measurement file
  491. self.ui.lineEditMeasurements.setText(config.measurementsfile)
  492. if config.measurementsfile.exists():
  493. self.ui.textMeasurements.setPlainText(config.measurementsfile.text())
  494. def loadBackgroundForRenderMap(self, baseImgFile=None):
  495. if baseImgFile is None or not config.bgmap:
  496. img = Image.new('RGBA', (config.width, config.height), (0,0,0, 255))
  497. else:
  498. img = Image.open(baseImgFile)
  499. draw = ImageDraw.Draw(img)
  500. edgeimg = Image.open('tmp/envmap_edges.png')
  501. img.paste(edgeimg, (0, 0), edgeimg)
  502. # draw access points
  503. for source in renderer.sources:
  504. draw.ellipse([(source.x - 3, source.y - 3), (source.x + 2, source.y + 3)], fill='white')
  505. # draw measurements
  506. for line in config.measurementsfile.lines():
  507. x, y, z, _, sigstrength = line.split()
  508. x, y = meshLoader.m2d.meter2pixel(float(x), float(y))
  509. draw.ellipse([(x - 3, y - 3), (x + 2, y + 3)], fill='red')
  510. img.save('tmp/renderMapBackground.png')
  511. self.ui.previewImage.setStyleSheet("background-image: url(tmp/renderMapBackground.png)")
  512. log.info('loaded bgimage')
  513. def setupLogger(self):
  514. from PyQt4.Qsci import QsciScintilla
  515. _fromUtf8 = QtCore.QString.fromUtf8
  516. self.ui.textLogger = QsciScintilla(self.ui.mainWidget)
  517. self.ui.textLogger.setToolTip(_fromUtf8(""))
  518. self.ui.textLogger.setWhatsThis(_fromUtf8(""))
  519. self.ui.textLogger.setObjectName(_fromUtf8("textLogger"))
  520. #~ print dir(self.ui.tabLogger)
  521. self.ui.tabLogger.layout().addWidget(self.ui.textLogger)
  522. self.ui.textLogger.setFont(QtGui.QFont('Lucida Console', pointSize=9))
  523. self.ui.textLogger.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0)
  524. def _log(msg):
  525. self.ui.textLogger.append(msg + '\n')
  526. self.ui.textLogger.SendScintilla(QsciScintilla.SCI_GOTOPOS, len(self.ui.textLogger.text()), 0)
  527. log.handlers = []
  528. log.addHandler(QTLoggingHandler(signals.logMsg))
  529. signals.logMsg.connect(_log)
  530. self.ui.actionClearLog.triggered.connect(lambda: self.ui.textLogger.setText(''))
  531. #~ @signals.newPlotAvailable.connect
  532. def onNewPlotAvailable(self):
  533. self.loadBackgroundForRenderMap('tmp/analysis.png')
  534. #~ self.ui.analysisImage.setStyleSheet("background-image: url(tmp/analysis.png)")
  535. #~ @signals.newAnalysisMap.connect
  536. def onNewAnalysisImage(self, fname):
  537. css = "background-image: url(%s); background-repeat: no-repeat" % fname
  538. self.ui.analysisImage.setStyleSheet(css)
  539. #~ @signals.renderMapMouseMove.connect
  540. def onRenderMapMouseMove(self, x, y, sourceVector):
  541. _x, _y = meshLoader.m2d.pixel2meter(x, y)
  542. self.feedback_posx.setText(1, '%.2f [%s]' % (_x, x))
  543. self.feedback_posy.setText(1, '%.2f [%s]' % (_y, y))
  544. for i, value in enumerate(sourceVector):
  545. self.feedback_sources[i].setText(1, '%.3f' % value)
  546. #~ @signals.renderMapSelectRect.connect
  547. def onRenderMapRectSelect(self, r):
  548. for item, value in zip(self.feedback_rect, r):
  549. item.setText(1, '%s' % value)
  550. self.ui.treeFeedback.expandItem(self.feedback_rect_parent)
  551. def closeEvent(self, event):
  552. pass
  553. def eventFilter(self, object, event):
  554. print event.type()
  555. return True
  556. class App(QtGui.QApplication):
  557. pass
  558. if __name__ == '__main__':
  559. app = App(sys.argv)
  560. mainform = MainForm()
  561. mainform.show()
  562. sys.exit(app.exec_())